该案例主要是对常见的图元连接方式的运用,常见的基本图元连接方式见此链接
最终实现的效果如图所示:
如图所示,整体的绘制流程如下
-
main函数:程序入口
-
ChangeSize函数:主要是设置视口及投影方式
-
SetupRC函数:图形数据配置,主要是顶点数据及图元连接方式
-
RenderScene函数:主要用于图形的绘制,可以系统触发,也可以开发者手动触发
-
SpecialKeys函数:对特殊键位的回调处理
-
KeyPressFunc:针对空格键的回调处理
-
DrawWireFrameBatch:用于立体图形的填充及边框绘制
主要对其中三个函数进行一些说明
ChangeSize函数
在之前的demo中,changeSize主要是用来设置视口大小以及当视口发生变化时调用的,而本案例中立体图形的绘制需要使用投影矩阵,因此需要在该函数中设置投影矩阵
主要涉及以下几个步骤
- 设置图形投影的方式:因为是立体图形,所以选择透视投影
//参数1:垂直方向上的视场角度
//参数2:视口纵横比 = w/h
//参数3:近裁剪面距离
//参数4:远裁剪面距离
viewFrustum.SetPerspective(35.0f, float(w)/float(h), 1.0f, 500.0f);
- 通过设置的投影方式获得投影矩阵,并将其存入投影矩阵中
projectionMatrix.LoadMatrix(viewFrustum.GetProjectionMatrix());
- 初始化模型视图矩阵堆栈,压入一个单元矩阵
modelViewMatrix.LoadIdentity();
SetupRC函数
从流程图上可以看出,除了基本的背景色设置,存储着色器初始化以及顶点数据的创建及传输外,还需要对阵矩阵及观察者做一下设置
- 将模型视图矩阵和投影矩阵放到变换管道中,变换管道的作用是能帮助快速进行矩阵相乘,在RenderScene函数中可以直接通过变换管道的Get方法得到相应的矩阵
transformPipeline.SetMatrixStacks(modelViewMatrix, projectionMatrix);
其中,变换管道通过get可获得的矩阵有4种
方法 | 说明 |
---|---|
GetProjectionMatrix() | 投影矩阵 |
GetNormalMatrix() | 法线矩阵 |
GetModelViewMatrix() | 模型视图矩阵,简称mv |
GetModelViewProjectionMatrix() | 模型视图投影矩阵,简称mvp |
- 设置观察者位置
cameraFrame.MoveForward(-15.0f);
观察者位置的设置有三种
方法 | 说明 |
---|---|
void MoveForward(float fDelta) | 向外移动的像素点,修改z |
void MoveUp(float fDelta) | 向上移动的像素点,修改y |
void MoveRight(float fDelta) | 向右移动的像素点,修改x |
RenderScene函数
其流程如下所示,主要是立体图形的渲染过程
从流程图中可以看出,这个过程是将物体坐标转换为裁剪坐标,然后经过OpenGL的处理,转换为NDC,并显示到屏幕上的一个过程。在此过程中栈的变化如下所示
-
ChangeSize函数中向栈中初始化了一个单元矩阵
-
RenderScene函数中再次向栈中压入一个单元矩阵:主要是为了图形绘制完成后,矩阵的复原,所以此时栈中有两个单元矩阵
modelViewMatrix.PushMatrix();
- 将cameraFrame构建为 观察者矩阵,将栈顶单元矩阵取出,与观察者矩阵相乘,得到新的观察者矩阵,再将其入栈
M3DMatrix44f mCamera;
cameraFrame.GetCameraMatrix(mCamera);
modelViewMatrix.MultMatrix(mCamera);
- 将objectFrame构建为 物体矩阵,取出栈顶的观察者矩阵,与物体矩阵相乘,得到模型视图矩阵,并将其入栈
- 然后利用固定管线渲染图形,在图像渲染完成后,将栈中模型视图矩阵pop,恢复其初始状态。
具体代码见Github 04_OpenGL点/线...
首先,我们先思考,如果在绘制金字塔时,在没有绘制黑色边框问题的情况下,是什么样的。效果如图所示:
从图上我们可以看出,金字塔绘制的过程中出现类似ZFighting的问题,主要是OpenGL中没有办法通过相同颜色的图层,来区分图层关系,因此需要给图形绘制黑色边框,以便于区分不同图层。
黑色图层的绘制流程如图所示
函数中主要包含两部分
- 图形的绘制 即 金字塔
- 边框的绘制 即 黑色边框
图形的绘制这里就不过多说明了,主要说说边框绘制的过程
边框绘制
- 开启多边形偏移、设置偏移量
在同一个位置同时绘制填充和边线,会产生ZFighting问题,而ZFighting问题的解决可以通过多边形偏移
,所以需要设置一个偏移量,通常默认是 -1 和 -1
glPolygonOffset(-1.0f, -1.0f);
//启用线的深度偏移
glEnable(GL_POLYGON_OFFSET_LINE);
- 开启抗锯齿功能:主要是为了让线条更光滑
glEnable(GL_LINE_SMOOTH);
- 开启颜色混合功能
颜色混合主要适用于边框颜色与填充颜色的混合,但是个人感觉其实这里是没有必要开启颜色混合的,图形上层并没有半透明颜色需要与其他颜色进行混合(在代码中已验证,开启与不开启并没有差别)
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
- 绘制边框、设置线条宽度
其实,在整个绘制过程中,图形绘制了两边,第一遍是以颜色和面的方式填充金字塔,第二遍是以颜色和线的方式填充线框
//绘制线框几何黑色版 三种模式,实心,边框,点,可以作用在正面,背面,或者两面
//通过调用glPolygonMode将多边形正面或者背面设为线框模式,实现线框渲染
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
//设置线条宽度
glLineWidth(2.5f);
shaderManager.UseStockShader(GLT_SHADER_FLAT, transformPipeline.GetModelViewProjectionMatrix(), vBlack);
pBatch->Draw();
- 还原设置属性
//通过调用glPolygonMode将多边形正面或者背面设为全部填充模式
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
glDisable(GL_POLYGON_OFFSET_LINE);
glLineWidth(1.0f);
glDisable(GL_BLEND);
glDisable(GL_LINE_SMOOTH);
最终实现的效果如图所示
- 金字塔有边框
网友评论