一、繪制甜甜圈
整體繪制流程在之前的案例已經(jīng)描述
#include "GLTools.h"
#include "GLMatrixStack.h"
#include "GLFrame.h"
#include "GLFrustum.h"
#include "GLGeometryTransform.h"
#include <glut/glut.h>
//著色管理器
GLShaderManager shaderManager;
//設(shè)置角色幀, 作為相機(jī)木张, 也就是觀察者
GLFrame viewFrame;
GLFrustum viewFrustum;
GLTriangleBatch torusBatch;
GLMatrixStack modelViewMatix;
GLMatrixStack projectionMatrix;
GLGeometryTransform transformPipeline;
//在窗口大小改變時(shí), 接收新的寬度 & 高度
void changeSize(int w, int h)
{
glViewport(0, 0, w, h);
//設(shè)置透視模式众辨,初始化其透視矩陣 參數(shù):表示一個(gè)從頂點(diǎn)方向看去的視場(chǎng)角度
viewFrustum.SetPerspective(35.0f, float(w)/float(h), 1.0, 100.0);
//把透視矩陣加載到透視矩陣對(duì)陣中
projectionMatrix.LoadMatrix(viewFrustum.GetProjectionMatrix());
//初始化渲染管線
transformPipeline.SetMatrixStacks(modelViewMatix, projectionMatrix);
}
//為程序做一次性設(shè)置
void setupRC()
{
glClearColor(0.7, 0.7, 0.7, 1.0);
shaderManager.InitializeStockShaders();
viewFrame.MoveForward(10.0);
//創(chuàng)建一個(gè)甜甜圈
gltMakeTorus(torusBatch, 1.0, 0.3, 50, 50);
//點(diǎn)的大小 方便點(diǎn)填充時(shí),肉眼觀察舷礼。
glPointSize(4.0);
}
//鍵位控制
void SpecialKeys(int key, int x, int y)
{ //1.判斷方向
if(key == GLUT_KEY_UP)
//2.根據(jù)方向調(diào)整觀察者位置
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();
}
//開(kāi)始渲染
void RenderScene(void)
{
//清楚緩存
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
//將攝像機(jī)矩陣壓入模型矩陣中
modelViewMatix.PushMatrix(viewFrame);
//設(shè)置繪圖顏色
GLfloat vYellow[] = {0.0, 1.0, 0.0, 1.0};
//默認(rèn)光源著色器
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;
}
效果圖:
二鹃彻、出現(xiàn)問(wèn)題
我們發(fā)現(xiàn) 當(dāng)轉(zhuǎn)動(dòng)甜甜圈的時(shí)候 出現(xiàn)了 黑色,出現(xiàn)這個(gè)的原因是因?yàn)樵谶@個(gè)OpenGL中也存在“陽(yáng)面”妻献,“陰面”蛛株,陽(yáng)面就是我們能夠看到的面,陰面是看不到的育拨,在我們轉(zhuǎn)動(dòng)甜甜圈的時(shí)候谨履,OpenGL此時(shí)是不知道哪個(gè)是陽(yáng)面 哪個(gè)是陰面,所以導(dǎo)致一些陰面的部分展示了出現(xiàn)熬丧,所以出現(xiàn)了這樣不該被觀察者看到的黑色部分笋粟。
三、解決方法
1. 油畫法 : 由遠(yuǎn)到近繪圖析蝴,先繪制遠(yuǎn)的圖層害捕,再繪制近的,一層一層疊加闷畸,觀察者看不到的地方也繪制上去了尝盼,所以這個(gè)方法是效率比較低的。
2. 正背面剔除(Face Culling)
此方法在圖元裝配的過(guò)程中就拋棄了一些觀察者看不到的部分佑菩,只繪制觀察者能看到的盾沫,因此這個(gè)方法時(shí)很高效的。
- 開(kāi)啟背面剔除
glEnable(GL_CULL_FACE);
- 關(guān)閉背面剔除
glDisable(GL_CULL_FACE);
- 上面的方法時(shí)沒(méi)有聲明需要剔除的面倘待,設(shè)置要剔除的面
void glCullFace(GLenum mode);
- 代碼
//開(kāi)始渲染
void RenderScene(void)
{
//清楚緩存
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glEnable(GL_CULL_FACE);
glFrontFace(GL_CCW);
glCullFace(GL_BACK);
//將攝像機(jī)矩陣壓入模型矩陣中
modelViewMatix.PushMatrix(viewFrame);
//設(shè)置繪圖顏色
GLfloat vYellow[] = {0.0, 1.0, 0.0, 1.0};
//默認(rèn)光源著色器
shaderManager.UseStockShader(GLT_SHADER_DEFAULT_LIGHT, transformPipeline.GetModelViewMatrix(), transformPipeline.GetProjectionMatrix(), vYellow);
//繪制
torusBatch.Draw();
//出棧
modelViewMatix.PopMatrix();
glutSwapBuffers();
}
這是運(yùn)行后 轉(zhuǎn)動(dòng)后的效果
會(huì)發(fā)現(xiàn) 黑色的部分已經(jīng)沒(méi)有了疮跑,但是卻出現(xiàn)了一個(gè)新的問(wèn)題,就是當(dāng)這個(gè)甜甜圈旋轉(zhuǎn)到有重疊的部門時(shí)凸舵,出現(xiàn)了一個(gè)缺口祖娘,因?yàn)镺penGL不能區(qū)別哪個(gè)圖層是在前面,哪個(gè)圖層在后面啊奄,導(dǎo)致的問(wèn)題渐苏。
3.深度測(cè)試(可解決深度問(wèn)題,也可解決正背面剔除問(wèn)題)
深度
其實(shí)深度就是OpenGL中坐標(biāo)的Z值菇夸,距離觀察者的距離琼富。
而這個(gè)距離的大小是和觀察者所在位置有關(guān)
- 如果觀察者在Z軸的正方向,Z值越大則靠近觀察者
- 如果觀察者在Z軸的負(fù)方向庄新,Z值越小則越靠近觀察者
深度緩存區(qū)(DepthBuffer):
- 存儲(chǔ)在顯存中鞠眉,就是把距離觀察者平面的深度值與窗口中每個(gè)像素點(diǎn)1對(duì)1進(jìn)行關(guān)聯(lián)以及存儲(chǔ)(即像素為 100x100薯鼠,那么也要存儲(chǔ)120x120個(gè)深度值)。
- 深度緩存區(qū)和顏色緩存區(qū)是對(duì)應(yīng)的械蹋,顏色緩存區(qū)存儲(chǔ)像素的顏色信息出皇,深度緩存區(qū)則存儲(chǔ)像素的深度信息,在決定是否繪制一個(gè)物體表面時(shí)哗戈,首先要將表面對(duì)應(yīng)的像素的深度值與當(dāng)前深度緩存區(qū)中的值進(jìn)行比較郊艘,如果大于深度緩存區(qū)中的值(就是比較當(dāng)前兩個(gè)重疊的圖層哪個(gè)離觀察者更遠(yuǎn)),則丟棄這部分唯咬,不進(jìn)行繪制纱注。
- 利用這個(gè)像素對(duì)應(yīng)的深度值和顏色值分別更新深度緩存區(qū)和顏色緩存區(qū),這個(gè)過(guò)程稱為 深度測(cè)試
開(kāi)啟深度測(cè)試
glEnable(GL_DEPTH_TEST);
關(guān)閉深度測(cè)試
glDisable(GL_DEPTH_TEST);
深度測(cè)試規(guī)則的枚舉值胆胰,可通過(guò)函數(shù)glDepthFunc(GLenum func)
修改狞贱。