一、绘制甜甜圈
整体绘制流程在之前的案例已经描述
#include "GLTools.h"
#include "GLMatrixStack.h"
#include "GLFrame.h"
#include "GLFrustum.h"
#include "GLGeometryTransform.h"
#include <glut/glut.h>
//着色管理器
GLShaderManager shaderManager;
//设置角色帧, 作为相机, 也就是观察者
GLFrame viewFrame;
GLFrustum viewFrustum;
GLTriangleBatch torusBatch;
GLMatrixStack modelViewMatix;
GLMatrixStack projectionMatrix;
GLGeometryTransform transformPipeline;
//在窗口大小改变时, 接收新的宽度 & 高度
void changeSize(int w, int h)
{
glViewport(0, 0, w, h);
//设置透视模式,初始化其透视矩阵 参数:表示一个从顶点方向看去的视场角度
viewFrustum.SetPerspective(35.0f, float(w)/float(h), 1.0, 100.0);
//把透视矩阵加载到透视矩阵对阵中
projectionMatrix.LoadMatrix(viewFrustum.GetProjectionMatrix());
//初始化渲染管线
transformPipeline.SetMatrixStacks(modelViewMatix, projectionMatrix);
}
//为程序做一次性设置
void setupRC()
{
glClearColor(0.7, 0.7, 0.7, 1.0);
shaderManager.InitializeStockShaders();
viewFrame.MoveForward(10.0);
//创建一个甜甜圈
gltMakeTorus(torusBatch, 1.0, 0.3, 50, 50);
//点的大小 方便点填充时,肉眼观察。
glPointSize(4.0);
}
//键位控制
void SpecialKeys(int key, int x, int y)
{ //1.判断方向
if(key == GLUT_KEY_UP)
//2.根据方向调整观察者位置
viewFrame.RotateWorld(m3dDegToRad(-5.0), 1.0f, 0.0f, 0.0f);
if(key == GLUT_KEY_DOWN)
viewFrame.RotateWorld(m3dDegToRad(5.0), 1.0f, 0.0f, 0.0f);
if(key == GLUT_KEY_LEFT)
viewFrame.RotateWorld(m3dDegToRad(-5.0), 0.0f, 1.0f, 0.0f);
if(key == GLUT_KEY_RIGHT)
viewFrame.RotateWorld(m3dDegToRad(5.0), 0.0f, 1.0f, 0.0f);
//3.重新刷新
glutPostRedisplay();
}
//开始渲染
void RenderScene(void)
{
//清楚缓存
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
//将摄像机矩阵压入模型矩阵中
modelViewMatix.PushMatrix(viewFrame);
//设置绘图颜色
GLfloat vYellow[] = {0.0, 1.0, 0.0, 1.0};
//默认光源着色器
shaderManager.UseStockShader(GLT_SHADER_DEFAULT_LIGHT, transformPipeline.GetModelViewMatrix(), transformPipeline.GetProjectionMatrix(), vYellow);
//绘制
torusBatch.Draw();
//出栈
modelViewMatix.PopMatrix();
glutSwapBuffers();
}
int main(int argc,char* argv[])
{
gltSetWorkingDirectory(argv[0]);
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH | GLUT_STENCIL);
glutInitWindowSize(600, 600);
glutCreateWindow("Geometry");
glutReshapeFunc(changeSize);
glutSpecialFunc(SpecialKeys);
glutDisplayFunc(RenderScene);
GLenum err = glewInit();
if (GLEW_OK != err) {
fprintf(stderr, "GLEW Error: %s\n", glewGetErrorString(err));
return 1;
}
setupRC();
glutMainLoop();
return 0;
}
效果图:
甜甜圈.png 甜甜圈.gif二、出现问题
我们发现 当转动甜甜圈的时候 出现了 黑色,出现这个的原因是因为在这个OpenGL中也存在“阳面”,“阴面”,阳面就是我们能够看到的面,阴面是看不到的,在我们转动甜甜圈的时候,OpenGL此时是不知道哪个是阳面 哪个是阴面,所以导致一些阴面的部分展示了出现,所以出现了这样不该被观察者看到的黑色部分。
三、解决方法
油画法.png1. 油画法 : 由远到近绘图,先绘制远的图层,再绘制近的,一层一层叠加,观察者看不到的地方也绘制上去了,所以这个方法是效率比较低的。
2. 正背面剔除(Face Culling)
此方法在图元装配的过程中就抛弃了一些观察者看不到的部分,只绘制观察者能看到的,因此这个方法时很高效的。
- 开启背面剔除
glEnable(GL_CULL_FACE);
- 关闭背面剔除
glDisable(GL_CULL_FACE);
- 上面的方法时没有声明需要剔除的面,设置要剔除的面
void glCullFace(GLenum mode);
- 代码
//开始渲染
void RenderScene(void)
{
//清楚缓存
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glEnable(GL_CULL_FACE);
glFrontFace(GL_CCW);
glCullFace(GL_BACK);
//将摄像机矩阵压入模型矩阵中
modelViewMatix.PushMatrix(viewFrame);
//设置绘图颜色
GLfloat vYellow[] = {0.0, 1.0, 0.0, 1.0};
//默认光源着色器
shaderManager.UseStockShader(GLT_SHADER_DEFAULT_LIGHT, transformPipeline.GetModelViewMatrix(), transformPipeline.GetProjectionMatrix(), vYellow);
//绘制
torusBatch.Draw();
//出栈
modelViewMatix.PopMatrix();
glutSwapBuffers();
}
这是运行后 转动后的效果
会发现 黑色的部分已经没有了,但是却出现了一个新的问题,就是当这个甜甜圈旋转到有重叠的部门时,出现了一个缺口,因为OpenGL不能区别哪个图层是在前面,哪个图层在后面,导致的问题。
3.深度测试(可解决深度问题,也可解决正背面剔除问题)
深度
其实深度就是OpenGL中坐标的Z值,距离观察者的距离。
而这个距离的大小是和观察者所在位置有关
- 如果观察者在Z轴的正方向,Z值越大则靠近观察者
- 如果观察者在Z轴的负方向,Z值越小则越靠近观察者
深度缓存区(DepthBuffer):
- 存储在显存中,就是把距离观察者平面的深度值与窗口中每个像素点1对1进行关联以及存储(即像素为 100x100,那么也要存储120x120个深度值)。
- 深度缓存区和颜色缓存区是对应的,颜色缓存区存储像素的颜色信息,深度缓存区则存储像素的深度信息,在决定是否绘制一个物体表面时,首先要将表面对应的像素的深度值与当前深度缓存区中的值进行比较,如果大于深度缓存区中的值(就是比较当前两个重叠的图层哪个离观察者更远),则丢弃这部分,不进行绘制。
- 利用这个像素对应的深度值和颜色值分别更新深度缓存区和颜色缓存区,这个过程称为 深度测试
开启深度测试
glEnable(GL_DEPTH_TEST);
关闭深度测试
glDisable(GL_DEPTH_TEST);
深度测试规则的枚举值,可通过函数glDepthFunc(GLenum func)
修改。
网友评论