一. 最终效果
照葫芦画瓢,简单实现了下甜甜圈的效果,最终效果如下图所示:
二. 代码实现
- 入口main函数如下所示:
int main(int argc, char* argv[])
{
gltSetWorkingDirectory(argv[0]);
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH | GLUT_STENCIL);
glutInitWindowSize(800, 600); // 设置展示的窗口的大小
glutCreateWindow("Geometry Test Program"); // 设窗口名称
glutReshapeFunc(ChangeSize); // ChanegSize为自定义函数,当窗口大小发生变化的时候,就会调用
glutSpecialFunc(SpecialKeys); // Specialkeys为自定义函数,键盘点击时候调用
glutDisplayFunc(RenderScene); // RenderScene为自定义函数,当触发渲染的时候,就会调用这个函数。
GLenum err = glewInit();
if (GLEW_OK != err) {
fprintf(stderr, "GLEW Error: %s\n", glewGetErrorString(err));
return 1;
}
SetupRC(); // 设置你需要渲染的图形相关顶点数据/颜色数据等数据的准备工作
glutMainLoop();
return 0;
}
- changeSize函数:
//窗口改变
void ChangeSize(int w, int h)
{
glViewport(0, 0, w, h);
viewFrustum.SetPerspective(20.0f, float(w)/float(h), 1.0f, 100.0f);
projectionMatrix.LoadMatrix(viewFrustum.GetProjectionMatrix()); // 加载投影矩阵
modelViewMatix.LoadIdentity(); // 加载模型视图矩阵
transformPipeline.SetMatrixStacks(modelViewMatix, projectionMatrix); // 把两个矩阵放入传送管道中
}
viewFrustum是GLFrustum类型,用于定义投影类型,因为我们需要显示的为3D图像,所以这里需要用到透视投影(区别于正投影,正投影主要用在显示2D图像上)。
GLFrustum类通过setPerspective 方法为我们构建⼀一个平截头体, 如下图所示:
函数说明如下:
CLFrustum::SetPerspective(float fFov , float fAspect ,float fNear ,float fFar);
参数:
fFov:垂直⽅方向上的视场⻆角度
fAspect:窗⼝口的宽度与⾼高度的纵横⽐比
fNear:近裁剪⾯面距离
fFar:远裁剪⾯面距离
纵横⽐比 = 宽(w)/⾼高(h)
3. SetupRC函数
void SetupRC() {
glClearColor(0.3f, 0.3f, 0.3f, 1.0f); // 设置背景颜色
glEnable(GL_CULL_FACE); // 开启正背面剔除,下一篇文章会讲到正背面剔除的原理
shaderManager.InitializeStockShaders(); // 初始化固定着色器管理类
cameraFrame.MoveForward(-10.0f); // 可以理解为视角离开屏幕的距离加10,这里也可以设置viewFrame达到相同的效果。
gltMakeTorus(torusBatch, 1.0f, 0.3f, 52, 26); // 生成甜甜圈顶点数据放入到torusBatch中
glPointSize(4.0f); // 设置点的大小
}
- RenderScene函数
这个函数是最为关键的部分,当屏幕发生变化,或者开发者主动渲染的时候,比如手动调用glutPostRedisplay(),会调用此函数进行渲染操作,把数据渲染生成图像。实现如下: ```C++ void RenderScene(){ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // 清除颜色和深度缓存
modelViewMatix.PushMatrix(); // 压入一个单元矩阵
M3DMatrix44f mCamera;
cameraFrame.GetCameraMatrix(mCamera); // 获取视角矩阵,表示视角的旋转移动等变化
modelViewMatix.MultMatrix(mCamera); // 和栈顶的单元矩阵相乘
M3DMatrix44f mObjectFrame;
viewFrame.GetMatrix(mObjectFrame); // 获取物体矩阵,表示物体的旋转移动等变化
modelViewMatix.MultMatrix(mObjectFrame); // 和栈顶的矩阵相乘
GLfloat vRed[] = { 0.0f, 1.0f, 0.0f, 1.0f };
// 使用默认光源着色器
shaderManager.UseStockShader(GLT_SHADER_DEFAULT_LIGHT, transformPipeline.GetModelViewMatrix(), transformPipeline.GetProjectionMatrix(), vRed);
// 开始绘画
torusBatch.Draw();
modelViewMatix.PopMatrix();
glutSwapBuffers();
}
```
默认光源着色器函数说明:
GLShaderManager::UserStockShader(GLT_SHADER_DEFAULT_LIGHT,GLfloat mvMatrix[16],GLfloat pMatrix[16],GLfloat vColor[4]);
参数1: 存储着⾊色器器种类-默认光源着⾊色器器 参数2: 模型4*4矩阵
参数3: 投影4*4矩阵
参数4: 颜⾊色值
使⽤用场景: 在绘制图形时, 可以应⽤用变换(模型/投影变化) 这种着⾊色器器会使绘制的图形产⽣生 阴影和光照的效果.
三. 总结
实现甜甜圈的效果其实是非常简单的,主要是要了解渲染的整个流程,以及顶点数据的获取。
上文中出现了3种矩阵:视角矩阵、模型矩阵、投影矩阵。用一个简单的图就可以说明三者之间的关系: 我们就是上图中的小人,在我们的眼中呈现这个物体时,我们从左看,或者从右看,物体展现的样子是不一样的,所以需要一个矩阵去描述我们自己的位置,即视角矩阵。物体如果旋转了,在我们眼中展现出来的图像也是不一样的,所以也需要一个矩阵去描述物体的位置,即模型矩阵。投影矩阵则决定了,图像展现在我们眼中是2D还是3D,大小等。