先说一下我们想要做什么,以及这个小的系列会讲些什么。
首先这个系列很小,我觉得大概也就几节罢了,不可能给大家把三维Opengl讲透,所以读者应该是有点基础的。
在做体渲染的时候,我们有时候只需要渲染里面的一部分内容,也就是说我们不需要把整个体空间都渲染出来,这个时候,我们就应该建立一个窗口,里面有一个可以移动的矩形体来调节我们想要渲染的内容,注意要是矩形的。
在我们已有的工程上,建立一个DockWidget。然后再定义一个具体的Opengl类:
class My3DDisplayWidget : public QOpenGLWidget,protected QOpenGLFunctions
同时继承自两个Opengl类,一个是控件,一个是功能。在里面声明这些函数:
void initializeGL();
void paintGL();
void resizeGL(int width, int height);
void keyPressEvent(QKeyEvent *event);
void mousePressEvent(QMouseEvent *event);
void mouseMoveEvent(QMouseEvent *event);
void mouseReleaseEvent(QMouseEvent *event);
其中前三个是系统自动调用的,用来进行初始化,绘制以及大小改变的时候进行一些修改。
按键事件暂时用不到。我们主要使用鼠标事件。
然后定义私有变量:
private:
QOpenGLShaderProgram *program;
QOpenGLBuffer vbo;
GLfloat xRot, yRot, zRot;
GLfloat translateX, translateY, translateZ;
这些私有变量第一个是用来设置着色器程序的,第二个是缓冲区,然后是x,y,z轴的旋转角度,以及针对x,y,z方向的平移量。
在构造函数中,我们定义:
My3DDisplayWidget::My3DDisplayWidget(QWidget *parent)
: QOpenGLWidget(parent)
{
xRot = 0.0;
yRot = 0.0;
zRot = 0.0;
translateX = 0;
translateY = 0;
translateZ = -3.0;
setMinimumSize(240,240);
}
进行了一些数据的初始化工作。
然后开始做Opengl初始化:
void My3DDisplayWidget::initializeGL()
{
// 为当前环境初始化OpenGL函数
initializeOpenGLFunctions();
QSurfaceFormat surfaceFormat;
surfaceFormat.setSamples(4);//多重采样
setFormat(surfaceFormat); //setFormat是QOpenGLWidget的函数
//glEnable( GL_BLEND );
glEnable(GL_DEPTH_TEST);
//glClearColor(255, 25, 15, 255);
glClearColor(0, 0, 0, 255);
// 创建顶点着色器
QOpenGLShader *vshader = new QOpenGLShader(QOpenGLShader::Vertex, this);
const char *vsrc =
"#version 330 \n"
"in vec4 vPosition; \n"
"in vec4 vColor; \n"
"out vec4 color; \n"
"uniform mat4 matrix; \n"
"void main() { \n"
" color = vColor; \n"
" gl_Position = matrix * vPosition; \n"
"} \n";
vshader->compileSourceCode(vsrc);
// 创建片段着色器
QOpenGLShader *fshader = new QOpenGLShader(QOpenGLShader::Fragment, this);
const char *fsrc =
"#version 330 \n"
"in vec4 color; \n"
"out vec4 fColor; \n"
"void main() { \n"
" fColor = color; \n"
"} \n";
fshader->compileSourceCode(fsrc);
// 创建着色器程序
program = new QOpenGLShaderProgram;
program->addShader(vshader);
program->addShader(fshader);
program->link();
program->bind();
}
注意如果你的程序调用一直失败,就一定要在着色器的程序里加上:
"#version 330 \n"
来强调使用的着色器OpenGL版本。
resizeGL以及按键响应都不添加了,在这里没什么用。
因为鼠标事件和旋转有关,我们待会再讲。先看一下我们定义的立方体:
// 顶点位置
GLfloat vertices[6][4][3] = {
//{ {-0.8f, 0.8f, 0.8f}, {-0.8f, -0.8f, 0.8f}, {0.8f, -0.8f, 0.8f}, {0.8f, 0.8f, 0.8f} },
//{ {0.8f, 0.8f, 0.8f}, {0.8f, -0.8f, 0.8f}, {0.8f, -0.8f, -0.8f}, {0.8f, 0.8f, -0.8f} },
{ { 0.5f, -0.5f, -0.5f },{ 0.5f, 0.5f, -0.5f },{ -0.5f, 0.5f, -0.5f },{ -0.5f, -0.5f, -0.5f } },
{ { -0.5f, 0.5f, 0.5f },{ -0.5f, -0.5f, 0.5f },{ -0.5f, -0.5f, -0.5f },{ -0.5f, 0.5f, -0.5f } },
//{ {0.8f, 0.8f, 0.8f}, {0.8f, 0.8f, -0.8f}, {-0.8f, 0.5f, -0.8f}, {-0.8f, 0.8f, 0.8f} },
{ { 0.5f, -0.5f, 0.5f },{ 0.5f, -0.5f, -0.5f },{ -0.5f, -0.5f, -0.5f },{ -0.5f, -0.5f, 0.5f } }
};
// 顶点颜色
GLfloat colors[6][4][3] = {
{ { 1.0f, 0.0f, 0.0f },{ 0.0f, 1.0f, 0.0f },{ 0.0f, 0.0f, 1.0f },{ 1.0f, 1.0f, 1.0f } },
{ { 1.0f, 0.0f, 0.0f },{ 0.0f, 1.0f, 0.0f },{ 1.0f, 1.0f, 1.0f },{ 0.0f, 0.0f, 1.0f } },
{ { 1.0f, 0.0f, 0.0f },{ 0.0f, 1.0f, 0.0f },{ 1.0f, 1.0f, 1.0f },{ 0.0f, 0.0f, 1.0f } },
};
GLfloat rvs[6][4][3] = {
{ { -0.4f, 0.4f, 0.4f },{ -0.4f, -0.4f, 0.4f },{ 0.4f, -0.4f, 0.4f },{ 0.4f, 0.4f, 0.4f } },
{ { 0.4f, 0.4f, 0.4f },{ 0.4f, -0.4f, 0.4f },{ 0.4f, -0.4f, -0.4f },{ 0.4f, 0.4f, -0.4f } },
{ { -0.4f, 0.4f, -0.4f },{ -0.4f, -0.4f, -0.4f },{ 0.4f, -0.4f, -0.4f },{ 0.4f, 0.4f, -0.4f } },
{ { -0.4f, 0.4f, 0.4f },{ -0.4f, -0.4f, 0.4f },{ -0.4f, -0.4f, -0.4f },{ -0.4f, 0.4f, -0.4f } },
{ { 0.4f, 0.4f, 0.4f },{ 0.4f, 0.4f, -0.4f },{ -0.4f, 0.4f, -0.4f },{ -0.4f, 0.4f, 0.4f } },
{ { 0.4f, -0.4f, 0.4f },{ 0.4f, -0.4f, -0.4f },{ -0.4f, -0.4f, -0.4f },{ -0.4f, -0.4f, 0.4f } }
};
GLfloat vertices2[6][4][3] = {};
GLfloat colors2[6][4][3] = {
{ { 1.0f, 0.0f, 0.0f },{ 0.0f, 1.0f, 0.0f },{ 0.0f, 0.0f, 1.0f },{ 1.0f, 1.0f, 1.0f } },
{ { 1.0f, 0.0f, 0.0f },{ 0.0f, 1.0f, 0.0f },{ 0.0f, 0.0f, 1.0f },{ 1.0f, 1.0f, 1.0f } },
{ { 1.0f, 0.0f, 0.0f },{ 0.0f, 0.0f, 1.0f },{ 0.0f, 1.0f, 0.0f },{ 1.0f, 1.0f, 1.0f } },
{ { 1.0f, 0.0f, 0.0f },{ 0.0f, 1.0f, 0.0f },{ 0.0f, 0.0f, 1.0f },{ 1.0f, 1.0f, 1.0f } },
{ { 1.0f, 0.0f, 0.0f },{ 0.0f, 1.0f, 0.0f },{ 0.0f, 0.0f, 1.0f },{ 1.0f, 1.0f, 1.0f } },
{ { 1.0f, 0.0f, 0.0f },{ 0.0f, 1.0f, 0.0f },{ 0.0f, 0.0f, 1.0f },{ 1.0f, 1.0f, 1.0f } }
};
这里大立方体和内部小立方体的顶点和颜色坐标。注意我们的内部小立方体是可以进行大小的调节的,但是为什么这里还要定义成常量的赋值呢?这样做有一个好处:为了方便再赋值:即我们根据rvs这里面初始值的正负来判断里面的值是vertices的左边界还是右边界。
我们在DockWidget里面定义一个
GLfloat centralRectPos[3] = { 0,0,0 };
GLfloat halfBoundaries[3] = { 0.5,0.5,0.5 };
这个变量的值先赋一个初始值,这两个数组代表了大立方体内部的小立方体的位置和大小。其中上面的代表中心点的位置,我们设置为0,0,0,即中心点,然后下面的代表长度的一半。即总长度是1.0,这样,初始值正好让小立方体和大立方体重合了。
之后我们在OpenGL类里面写一个更新边界的函数:
void updateVetices() {
for (int i = 0;i<6;i++) {
for (int j = 0;j<4;j++) {
if (rvs[i][j][0] < 0) {
vertices2[i][j][0] = centralRectPos[0] - halfBoundaries[0];
}
else {
vertices2[i][j][0] = centralRectPos[0] + halfBoundaries[0];
}
if (rvs[i][j][1] < 0) {
vertices2[i][j][1] = centralRectPos[1] - halfBoundaries[1];
}
else {
vertices2[i][j][1] = centralRectPos[1] + halfBoundaries[1];
}
if (rvs[i][j][2] < 0) {
vertices2[i][j][2] = centralRectPos[2] - halfBoundaries[2];
}
else {
vertices2[i][j][2] = centralRectPos[2] + halfBoundaries[2];
}
}
}
}
它可以根据变化的中心点的量生成新的小立方体各个顶点的数组。