看过之前一系列的文章之后,我们对ES有了一个比较基础的理解了,下面我们通过绘制一个随时间旋转的立方体,来汇总前几篇文章的内容,以加深对OpenGL ES渲染过程的理解。
示例代码在git仓库
正如读者所知,一个正方体是由六个面组成的,那这里我们拆分需求,逐次绘制六个面,然后用时间作为输入量,改变MVP矩阵,就能最终实现我们的效果。在后续的文章中,我都是在VSCode里编写着色器,安装GLSL扩展后,可以高亮显示,比较方便。
1.首先我们先来展示一下立方体的空间示意图
绘制与X轴垂直的平面的代码:
- (void)drawPositiveXWithColorLocation:(GLuint)colorLocation andPositionLocation:(GLuint)positionLocation {
//每一行代表(x,y,z,r,g,b)
static GLfloat positiveX[24] = {
0.5f, 0.5f, 0.5f, 1.0f, 0.6f, 0.3f,
0.5f, -0.5f, 0.5f, 1.0f, 0.6f, 0.3f,
0.5f, -0.5f, -0.5f, 1.0f, 0.6f, 0.3f,
0.5f, 0.5f, -0.5f, 1.0f, 0.6f, 0.3f,
};
if (self.vertexBufferId == 0) {
glGenBuffers(1, &_vertexBufferId);
glBindBuffer(GL_ARRAY_BUFFER, _vertexBufferId);
}
glBufferData(GL_ARRAY_BUFFER, sizeof(positiveX), positiveX, GL_STATIC_DRAW);
GLuint offset = 3 * sizeof(float);
glVertexAttribPointer(positionLocation, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), NULL);
glVertexAttribPointer(colorLocation, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (const void *) offset);
_esContext.drawFunc(&_esContext);
}
这里我们先找出与X轴垂直相交的这个面的四个顶点坐标,因为我们绘图采用的是GL_TRIANGLE_FAN模式,所以只需要四个顶点就可以了。
其它5个面也是这样绘制的
加载MVP矩阵的代码如下:
[super glkView:view drawInRect:rect];
_elapsedTime += 0.02;
//时间系数
float varyFactor = (sin(self.elapsedTime) + 1.0) / 2.0; //0 ~ 1
_esContext.width = view.drawableWidth;
_esContext.height = view.drawableHeight;
//开启深度测试,为了确定绘制的时候哪一个面绘制在上面
glEnable(GL_DEPTH_TEST);
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
//获取属性color和position的index
GLint colorIndex = glGetAttribLocation(_esContext.program, "vColor");
GLint positionIndex = glGetAttribLocation(_esContext.program, "vPosition");
//创建MVP矩阵
GLint modelIndex = glGetUniformLocation(_esContext.program, "modelTransform");
GLint viewIndex = glGetUniformLocation(_esContext.program, "viewTransform");
GLint projectIndex = glGetUniformLocation(_esContext.program, "projectTransform");
GLKMatrix4 rotate = GLKMatrix4MakeRotation(varyFactor * M_PI * 2, 1, 1, 1);
GLKMatrix4 translate = GLKMatrix4MakeTranslation(-0.1, -0.1, -0.1);
GLKMatrix4 modelMatrix = GLKMatrix4Multiply(translate, rotate);
GLKMatrix4 projectionMatrix = GLKMatrix4MakePerspective(GLKMathDegreesToRadians(90), view.frame.size.width / view.frame.size.height, 0.2, 10.0);
GLKMatrix4 cameraMatrix = GLKMatrix4MakeLookAt(0, 0, 2 * (varyFactor + 1), 0, 0, 0, 0, 1, 0);
//加载MVP矩阵
glUniformMatrix4fv(modelIndex, 1, GL_FALSE, modelMatrix.m);
glUniformMatrix4fv(viewIndex , 1, GL_FALSE, cameraMatrix.m);
glUniformMatrix4fv(projectIndex, 1, GL_FALSE, projectionMatrix.m);
//开启顶点属性
glEnableVertexAttribArray(colorIndex);
glEnableVertexAttribArray(positionIndex);
//加载顶点数据,并且依次绘制立方体的六个面
[self drawPositiveXWithColorLocation:colorIndex andPositionLocation:positionIndex];
[self drawNegativeXWithColorLocation:colorIndex andPositionLocation:positionIndex];
[self drawPositiveYWithColorLocation:colorIndex andPositionLocation:positionIndex];
[self drawNegativeYWithColorLocation:colorIndex andPositionLocation:positionIndex];
[self drawPositiveZWithColorLocation:colorIndex andPositionLocation:positionIndex];
[self drawNegativeZWithColorLocation:colorIndex andPositionLocation:positionIndex];
//关闭顶点属性
glDisableVertexAttribArray(colorIndex);
glDisableVertexAttribArray(positionIndex);
这里需要注意两点
1.需要开启深度测试,如果没有开启深度测试的话,ES是不知道你绘制面的层次,会出现如下效果:
2.开启深度测试后,除了需要清除颜色缓冲区外,还需要清除深度缓冲区
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
最终正确的效果如下图: