首先我们渲染一个甜甜圈, 来看一下效果. 部分代码如下:
直接调用框架内写好的方法, 不需要自己处理顶点坐标.
// 设置背景颜色
glClearColor(0.3f, 0.3f, 0.3f, 1.0f );
//初始化着色器管理器
shaderManager.InitializeStockShaders();
//将相机向后移动7个单元:肉眼到物体之间的距离
viewFrame.MoveForward(7.0);
//创建一个甜甜圈
//void gltMakeTorus(GLTriangleBatch& torusBatch, GLfloat majorRadius, GLfloat minorRadius, GLint numMajor, GLint numMinor);
//参数1:GLTriangleBatch 容器帮助类
//参数2:外边缘半径
//参数3:内边缘半径
//参数4、5:主半径和从半径的细分单元数量
gltMakeTorus(torusBatch, 1.0f, 0.3f, 52, 26);
//点的大小
glPointSize(4.0f);
进行渲染
//把摄像机矩阵压入模型矩阵中
modelViewMatix.PushMatrix(viewFrame);
GLfloat vRed[] = { 1.0f, 0.0f, 0.0f, 1.0f };
//使用平面着色器
//参数1:平面着色器
//参数2:模型视图投影矩阵
//参数3:颜色
//shaderManager.UseStockShader(GLT_SHADER_FLAT, transformPipeline.GetModelViewProjectionMatrix(), vRed);
//使用默认光源着色器
//通过光源、阴影效果跟提现立体效果
//参数1:GLT_SHADER_DEFAULT_LIGHT 默认光源着色器
//参数2:模型视图矩阵
//参数3:投影矩阵
//参数4:基本颜色值
// shaderManager.UseStockShader(GLT_SHADER_DEFAULT_LIGHT, transformPipeline.GetModelViewMatrix(), transformPipeline.GetProjectionMatrix(), vRed);
GLfloat vLight[] = { -1.0f, 0.5f, 10.0f };
shaderManager.UseStockShader(GLT_SHADER_POINT_LIGHT_DIFF, transformPipeline.GetModelViewMatrix(), transformPipeline.GetProjectionMatrix(), vLight, vRed);
//绘制
torusBatch.Draw();
//出栈
modelViewMatix.PopMatrix();
运行之后效果如下
因为案例中采用固定管线进行绘制, 使用的默认光源着色器, 片元着色器在处理看不见的面时, 会计算为黑色.
所以在渲染3D图形的时候, 需要决定哪些面是不可见的, 哪些面是可见的, 看不见的面要丢弃, 这种情况叫做"隐藏面消除"
这种渲染结果是由于GPU无法判断是渲染正面(红色)还是渲染背面(黑色), 会发生颜色混乱. 那么如何解决这种情况呢?
一、油画法(一般不被采用, 了解即可)
-先绘制场景中距离观察者较远的物体, 后绘制近的物体.
如图, 先绘制红色面, 最后绘制灰色面, 即可以解决隐藏面消除的问题. 但是是否会有弊端呢?
- 如图所示, 由于涉及深度问题(Z值), 从而无法判断面的前后, 所以无法进行处理.
- 效率问题, 面与面重叠的部分会进行多次绘制, 从而导致片元着色器效率低.
二、正背面剔除
任何物体都有两个面, 正面和背面, OpenGL可以做到区分正反面, 所有可以通过绘制正面或反面解决隐藏面消除的问题.
那么OpenGL是如何做到区分正反面的呢? 答案是通过顶点计算.
正背面区分:
1.正面:按逆时针顶点顺序连接的三角形面.
2.背面:按顺时针顶点顺序连接的三角形面.
如图所示, 在同一个角度观察, 正面的三角形顶点连接顺序为逆时针, 而背面顶点为顺时针连接, OpenGL也提供api可以修改规则, 可以把顺时针修改为正面, 反之为背面, 但是一般我们使用默认即可.
正背面剔除代码如下:
开启正背面剔除
glEnable(GL_CULL_FACE);
glFrontFace(GL_CCW);//默认为GL_CCW, 表示逆时针为正面 GL_CW表示顺时针为正面
glCullFace(GL_BACK);//剔除背面
关闭正背面剔除
glDisable(GL_CULL_FACE);
例如,剔除正⾯面实现(1)
glCullFace(GL_BACK);
glFrontFace(GL_CW);
例如,剔除正⾯面实现(2)
glCullFace(GL_FRONT);
接下来我们开启正背面剔除之后, 运行代码
之前的问题解决了, 没有发生颜色混乱的问题, 但是又出现了新的问题.