OpenGL(Open Graphics Library)は、2Dおよび3Dベクターグラフィックスをレンダリングするためのクロス言語およびクロスプラットフォームのアプリケーションプログラムインターフェイスです。OpenGLの実装は、グラフィックアクセラレーションハードウェアを利用しており、これらの実装は通常、ディスプレイデバイスメーカーによって提供されます。ただし、実際の使用では、glに基づくサードパーティのライブラリを使用して、プログラムのクラッシュやハードウェアの損傷を防ぐために、プログラムの実行時に現在のハードウェアが関連する拡張機能をサポートしているかどうかを判断します。現在、私が知っているサードパーティライブラリには、glad、glew、glfw、freeglutなどがあります。次の図は、それらの関係を反映しています。
さらに、Qtをインストールした後、Qt自体もある程度glライブラリ、つまりqopengl.hとQOpenGLFunctionsをカプセル化します。Qtは現在、UIに非常に優れたツールです。キューブ、hellogl2などの例と組み合わせると、より優れたヒューマンコンピューターインタラクションインターフェイスを模倣できます。
3次元モデルの場合、一般的に使用されるソフトウェアには、stlモデルファイルの描画とエクスポートに使用できるSolid Work、AutoCADなどが含まれます。STLファイルには、テキストファイル(ASCII形式)とバイナリファイル(BINARY)の2種類があります。その中で、テキスト形式は、複数の三角形の定義を含むnotepad ++で開くことができます。各三角形の定義には、三角形の各固定点の3次元座標と三角形の法線ベクトルが含まれます。三角形の頂点は右側の規則に従います。したがって、openglでstlファイルをレンダリングするには、最初にそれをロードする必要があり、https: //free3d.comは多くのモデルファイルをダウンロードすることもできます。
1.Qt独自のライブラリを使用する
Qt独自のライブラリを使用すると、ウィンドウはパブリックQOpenGLWidget、保護されたQOpenGLFunctionsを継承できます(OpenGL ES2.0 APIのセットが提供されるため、開発者はこれらの関数シンボルを手動で解析する必要がありません)。ただし、OpenGL ESの記述についてはまだ少し混乱しており、これ以上体系的な紹介は見ていません。一般的に、与えられた例では既製のテンプレートを使用しています。ここではQtの例の方法を使用しています。インターネット上でstlテキストファイルをロードする方法は多すぎます。
bool QObjLoad::load(QString fileName, QVector<float>& vPoints)
{
if (fileName.mid(fileName.lastIndexOf('.')) != ".obj" && fileName.mid(fileName.lastIndexOf('.')) != ".OBJ")
{
qDebug() << "file is not a obj file.";
return false;
}
QFile objFile(fileName);
if (!objFile.open(QIODevice::ReadOnly))
{
qDebug() << "open" << fileName << "failed";
return false;
}
else
{
qDebug() << "open" << fileName << "success!";
}
QVector<float> vertextPoints, texturePoints, normalPoints;
QVector<int> facesIndexs;
while (!objFile.atEnd())
{
QByteArray lineData = objFile.readLine();
QList<QByteArray> strValues = lineData.trimmed().split(' ');
QString dataType = strValues.takeFirst();
if (dataType == "v")
{
std::transform(strValues.begin(), strValues.end(), std::back_inserter(vertextPoints), [](QByteArray& str) {
return str.toFloat();
});
}
else if (dataType == "vt")
{
std::transform(strValues.begin(), strValues.end(), std::back_inserter(texturePoints), [](QByteArray& str) {
return str.toFloat();
});
}
else if (dataType == "vn")
{
std::transform(strValues.begin(), strValues.end(), std::back_inserter(normalPoints), [](QByteArray& str) {
return str.toFloat();
});
}
else if (dataType == "f")
{
facesIndexs << strValues.at(0).toInt() << strValues.at(1).toInt() << strValues.at(2).toInt();
}
}
objFile.close();
for (auto& verFaceInfo : facesIndexs)
{
int vIndex = verFaceInfo - 1;
vPoints << vertextPoints.at(vIndex * 3);
vPoints << vertextPoints.at(vIndex * 3 + 1);
vPoints << vertextPoints.at(vIndex * 3 + 2);
}
vertextPoints.clear();
texturePoints.clear();
normalPoints.clear();
facesIndexs.clear();
return true;
}
ここでの主な目的は、頂点座標を取得し、それをQtウィンドウに表示することです。3つの関数を再実装する必要があります:void initializeGL()オーバーライド、void paintGL()オーバーライド、およびvoid resizeGL(int width、int height )オーバーライド。
void GLWindow::initializeGL()
{
initializeOpenGLFunctions();
glClearColor(0, 0, 0, 0);
m_program = new QOpenGLShaderProgram;
m_program->addShaderFromSourceCode(QOpenGLShader::Vertex, vertexShaderSourceCore);
m_program->addShaderFromSourceCode(QOpenGLShader::Fragment, fragmentShaderSourceCore);
if (m_program->link())
{
qDebug() << "link success!";
}
else
{
qDebug() << "link failed";
}
m_program->bindAttributeLocation("vertex", 0);
m_program->bindAttributeLocation("normal", 1);
m_program->link();
m_program->bind();
m_projMatrixLoc = m_program->uniformLocation("projMatrix");
m_mvMatrixLoc = m_program->uniformLocation("mvMatrix");
m_normalMatrixLoc = m_program->uniformLocation("normalMatrix");
m_lightPosLoc = m_program->uniformLocation("lightPos");
// Setup our vertex buffer object.
m_Vbo.create();
m_Vbo.bind();
m_Vbo.allocate(m_vPoints.data(), m_vPoints.size() * sizeof(float));
// Store the vertex attribute bindings for the program.
setupVertexAttribs();
// Light position is fixed.
m_program->setUniformValue(m_lightPosLoc, QVector3D(10, 10, 10));
m_program->release();
}
void GLWindow::paintGL()
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glEnable(GL_DEPTH_TEST);
glEnable(GL_CULL_FACE);
m_world.setToIdentity();
m_world.rotate(m_xRot / 16.0f, 1, 0, 0);
m_world.rotate(m_yRot / 16.0f, 0, 1, 0);
m_world.rotate(m_zRot / 16.0f, 0, 0, 1);
m_camera.setToIdentity();
m_camera.lookAt(m_camera_pos, QVector3D(0, 0, 0), QVector3D(1, 0, 0));
m_program->bind();
m_program->setUniformValue(m_projMatrixLoc, m_proj);
m_program->setUniformValue(m_mvMatrixLoc, m_camera * m_world);
QMatrix3x3 normalMatrix = m_world.normalMatrix();
m_program->setUniformValue(m_normalMatrixLoc, normalMatrix);
glDrawArrays(GL_TRIANGLES, 0, m_vPoints.size()/3);
m_program->release();
}
void GLWindow::resizeGL(int w, int h)
{
m_proj.setToIdentity();
m_proj.perspective(45.0f, GLfloat(w) / h, 0.01f, 100.0f);
}
最後に、lookAtを使用して表示角度を設定して表示します。異なる視点から見えるように視野角を調整しました。効果は以下のとおりです。
2.freeglutとQtの組み合わせによる
FreeglutはGLUTのオープンソースの代替手段であり、多くの機能があり、ほとんどの人がそれに精通しているため、これはより人気があるかもしれません。Freeglutはhttp://freeglut.sourceforge.netからダウンロードできます。ダウンロード後、vsプロジェクトはcmakeによって生成され、freeglut.libおよびfreeglut.dllライブラリを使用できます。同時に、 GLディレクトリを対応するUnderコンテンツにコピーする必要があります。
それでも同じですが、stlファイルをロードして表示します。ただし、この方法は、マウスやキーボードの操作の処理には非常に面倒です。理由はわかりませんが、表示した色や角度が少し変わっています。ライトの位置、画角、レンダリング、およびさまざまなマトリックス変換については、まだ明確にする必要があります。
typedef struct Vertex
{
//定义三维图形的
//用于face结构体中
float x, y, z;
} Vertex;
typedef struct Face
{
//多边形(三角形)面的结构体
Face(void) : vert_number(0), verts(0) {};
int vert_number; //记录顶点的个数
Vertex** verts; //这是一个面的所有 顶点数组(含有坐标)
float normal[3]; //记录点的法向量,分别是x,y,z三个方向
//注意点的法向量通过顶点的信息计算得到!
//对于obj模型如果我们已经得到了法线的信息
//那么就直接拿来用就好!
} Face;
typedef struct myMesh
{
//自定义mesh的结构体
myMesh(void) : vert_number(0), verts(0), face_number(0), faces(0) {};
//自定义构造器
int vert_number; //总的顶点个数
Vertex* verts; //定点数组
int face_number; //面的数目
Face* faces;
vector<Vertex>point;
} myMesh;
void GLWin::drawObj()
{
if (m_mesh->face_number == 0)
return;
qDebug() << m_mesh->face_number;
float bbox[2][3] = { { 1.0E30F, 1.0E30F, 1.0E30F }, { -1.0E30F, -1.0E30F, -1.0E30F } };
for (int i = 0; i < m_mesh->vert_number; i++)
{
Vertex& vert = m_mesh->verts[i];
if (vert.x < bbox[0][0])
bbox[0][0] = vert.x;
else if (vert.x > bbox[1][0])
bbox[1][0] = vert.x;
if (vert.y < bbox[0][1])
bbox[0][1] = vert.y;
else if (vert.y > bbox[1][1])
bbox[1][1] = vert.y;
if (vert.z < bbox[0][2])
bbox[0][2] = vert.z;
else if (vert.z > bbox[1][2])
bbox[1][2] = vert.z;
}
// Setup initial viewing scale
float dx = bbox[1][0] - bbox[0][0];
float dy = bbox[1][1] - bbox[0][1];
float dz = bbox[1][2] - bbox[0][2];
scale = 2.0 / sqrt(dx * dx + dy * dy + dz * dz);
glPushMatrix();
glColor3f(0.5, 0.5, 0.5);
glScalef(scale, scale, scale);
glRotatef(rotation[0], 1.0, 0.0, 0.0);
glRotatef(rotation[1], 0.0, 1.0, 0.0);
glRotatef(rotation[2], 0.0, 0.0, 1.0);
for (int i = 0; i < m_mesh->face_number; i++)
{
Face& face = m_mesh->faces[i];
glBegin(GL_TRIANGLES);
glNormal3fv(face.normal);
for (int j = 0; j < face.vert_number; j++)
{
Vertex* vert = face.verts[j];
glVertex3f(vert->x, vert->y, vert->z);
}
glEnd();
}
glPopMatrix();
}
void GLWin::initializeGL()
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glClearColor(0.0, 0.0, 0.0, 0.0);
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
glEnable(GL_NORMALIZE);
glEnable(GL_DEPTH_TEST);
glutInitWindowSize(600, 600);
}
void GLWin::paintGL()
{
glLoadIdentity();
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glLightfv(GL_LIGHT0, GL_POSITION, light0_position);
gluLookAt(1, 1, 1, 0, 0, 0, 0, 1, 0);
//draw axis
glPushMatrix();
glLineWidth(5);
glBegin(GL_LINES);
glColor3f(1, 0, 0);
glVertex3f(0.0f, 0, 0);
glVertex3f(10, 0, 0);
glEnd();
glBegin(GL_LINES);
glColor3f(0, 1, 0);
glVertex3f(0.0f, 0, 0);
glVertex3f(0, 10, 0);
glEnd();
glBegin(GL_LINES);
glColor3f(0, 0, 1);
glVertex3f(0, 0, 0);
glVertex3f(0, 0, 10);
glEnd();
glPopMatrix();
CLoadStlObj stlobj;
m_mesh = stlobj.ReaderOBj("head.obj");
drawObj();
// 设置光照信息
static GLfloat lmodel_ambient[] = { 0.2, 0.2, 0.2, 1.0 };
glLightModelfv(GL_LIGHT_MODEL_AMBIENT, lmodel_ambient);
glLightModeli(GL_LIGHT_MODEL_LOCAL_VIEWER, GL_TRUE);
static GLfloat light0_diffuse[] = { 1.0, 1.0, 1.0, 1.0 };
设置满散射
glLightfv(GL_LIGHT0, GL_DIFFUSE, light0_diffuse);
glEnable(GL_LIGHT0);
static GLfloat light1_diffuse[] = { 0.5, 0.5, 0.5, 1.0 };
glLightfv(GL_LIGHT1, GL_DIFFUSE, light1_diffuse);
glEnable(GL_LIGHT1);
glEnable(GL_LIGHTING);
}
void GLWin::resizeGL(int width, int height)
{
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(45.0, (GLfloat)width / (GLfloat)height, 0.1, 100.0);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}
方法1のエンジニアリングコードはhttps://github.com/WelinLee/QtOpenGLDemoにあります。お楽しみください!