我們通過(guò)現(xiàn)成API實(shí)現(xiàn)一個(gè)甜甜圈的圖案和悦,并且使用默認(rèn)光源著色器進(jìn)行存儲(chǔ)初橘,通過(guò)轉(zhuǎn)動(dòng)觀察者視角奥额,但物體本身不動(dòng)的方式來(lái)看一下繪制的圖案所出現(xiàn)的bug
慣例愉昆,放一下流程圖(代碼實(shí)現(xiàn)直接放在文章末尾):
效果圖:
黑色部分是什么薄疚?如何解決碧信?
黑色部分其實(shí)是隱藏面
1、油畫算法
油畫算法街夭,我們之前的文章介紹過(guò):先繪制場(chǎng)景中的離觀察者較遠(yuǎn)的物體,再繪制較近的物體砰碴。
那么,這種?法在計(jì)算機(jī)圖形處理中是?常低效的板丽。
1呈枉、我們必須對(duì)任何發(fā)??何圖形重疊的地?每個(gè)像素進(jìn)?2 次寫操作,?在存儲(chǔ)其中進(jìn)?寫操作會(huì)使速度變慢
2埃碱、對(duì)獨(dú)?三?形進(jìn)?排序的開(kāi)銷過(guò)?
3猖辫、油畫算法 有瓶頸期. ?如繪制圖像交疊時(shí), 沒(méi)有明確的先后順序就?從下?繪制了
如果有多個(gè)三?形疊加在一起的情況,油畫算法將?法處理.
2、正背面剔除法(Face Culling)
背景:
首先砚殿,我們觀察一個(gè)3D物體啃憎,不管從任何?個(gè)?向去觀察,最多可以看到?個(gè)??
答案是:最多3?似炎。從?個(gè)??體的任意位置和?向上看辛萍,你不可能看到多于3個(gè)?.。
那么羡藐, 我們?yōu)楹我嘤嗟娜ダL制那根本看不到的3個(gè)??
如果我們能以某種?式去丟棄這部分?jǐn)?shù)據(jù),OpenGL 在渲染的性能即可提?超過(guò)50%.
如何知道某個(gè)?在觀察者的視野中不會(huì)出現(xiàn)?
任何平?都有2個(gè)?,正?/背?.意味著你?個(gè)時(shí)刻只能看到??贩毕。OpenGL 可以做到檢查所有正?朝向觀察者的?,并渲染它們.從?丟棄背?朝向的?. 這樣可以節(jié)約?元著?器的性能。
那如何告訴OpenGL仆嗦,我們繪制的圖形 哪個(gè)是正面哪個(gè)是反面辉阶?
通過(guò)分析頂點(diǎn)數(shù)據(jù)的順序就能知道(正面:逆時(shí)針 反面:順時(shí)針)對(duì)于正?、背?三?形進(jìn)?區(qū)分的原因之?欧啤,就是為了進(jìn)?剔除睛藻。
因此,我們可以給平?定義正?和背?,OpenGL可以做到檢查所有正?朝向觀察者的?邢隧,并渲染它們店印,從?丟棄 背?朝向的?。OpenGL渲染的性能即可提?超過(guò)50%
原理:
我們不去繪制那些根本看不到的?倒慧,以某種?式去丟棄這部分?jǐn)?shù)據(jù)
API:
//開(kāi)啟
glEnable(GL_CULL_FACE);
//關(guān)閉
glDisable(GL_CULL_FACE);
//設(shè)置哪一面為正面, GL_CW 按摘、GL_CCW ,默認(rèn)GL_CCW
glFrontFace(GL_CCW);
//設(shè)置剔除哪一面, GL_FRONT 包券、 GL_BACK 、GL_FRONT_AND_BACK ,默認(rèn)GL_BACK
glCullFace(GL_BACK);
/*
思考:默認(rèn)剔除背面炫贤,那么我們想剔除正面該怎么做呢溅固?
第一種:
glCullFace(GL_BACK);
glFrontFace(GL_CW);
第二種:
glCullFace(GL_FRONT);
*/
那么通過(guò)正背面剔除的技巧,把不顯示的背面剔除之后兰珍,我們來(lái)看一下效果圖:
新的問(wèn)題又來(lái)了:再轉(zhuǎn)動(dòng)過(guò)程中侍郭,會(huì)發(fā)現(xiàn)有的地方感覺(jué)像是被吃了一口似的。是什么原因呢掠河?又如何解決呢亮元?
原因:其實(shí)是兩個(gè)正面疊加在一起,出現(xiàn)了混合唠摹, 此時(shí)OpenGL不能清楚分辨 哪個(gè)圖層在前 哪個(gè)圖層在后爆捞,于是就會(huì)出現(xiàn)甜甜圈像被啃??的現(xiàn)象
深度測(cè)試
深度緩沖區(qū)(DepthBuffer)和顏?緩存區(qū)(ColorBuffer)是對(duì)應(yīng)的.顏?緩存區(qū)存儲(chǔ)像素的顏?信
息,?深度緩沖區(qū)存儲(chǔ)像素的深度信息. 在決定是否繪制?個(gè)物體表?時(shí), ?先要將表?對(duì)應(yīng)的像
素的深度值與當(dāng)前深度緩沖區(qū)中的值進(jìn)??較. 如果?于深度緩沖區(qū)中的值,則丟棄這部分.否則
利?這個(gè)像素對(duì)應(yīng)的深度值和顏?值.分別更新深度緩沖區(qū)和顏?緩存區(qū). 這個(gè)過(guò)程稱為”深度測(cè)
試”
API:
//開(kāi)啟
glEnable(GL_DEPTH_TEST);
//關(guān)閉
glDisable(GL_DEPTH_TEST);
最后我們?cè)倏聪麓蜷_(kāi)了深度測(cè)試的效果:
哇,完美解決了勾拉!
思考與拓展
我們使用了默認(rèn)光源著色器煮甥,出現(xiàn)了隱藏面。那么如果使用平面著色器藕赞,還會(huì)出現(xiàn)隱藏面嗎成肘?
- 答案是:會(huì)出現(xiàn),由于都是紅色的找默,沒(méi)有辦法區(qū)別誰(shuí)是正面艇劫,誰(shuí)是背面,所以導(dǎo)致肉眼無(wú)法察覺(jué)惩激。而在光源下店煞,被照射的部分呈現(xiàn)紅色,無(wú)法照射的部分會(huì)呈現(xiàn)黑色风钻,可以非常直觀的通過(guò)肉眼看出誰(shuí)是正面顷蟀,誰(shuí)是反面。
上面的動(dòng)圖可以看出骡技,右鍵菜單欄有改變狀態(tài)的選項(xiàng)鸣个,我們先看下一效果圖:
//修改填充方式
//充滿
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
//線
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
//點(diǎn)
glPolygonMode(GL_FRONT_AND_BACK, GL_POINT);
修改填充方式之后,也會(huì)出現(xiàn)隱藏面消除的問(wèn)題布朦,只是肉眼觀察不到囤萤。
源碼來(lái)啦!
#include "GLTools.h"
//矩陣
#include "GLMatrixStack.h"
//位置
#include "GLFrame.h"
//投影方式
#include "GLFrustum.h"
//變換管道是趴,快速傳輸矩陣
#include "GLGeometryTransform.h"
#include <math.h>
#ifdef __APPLE__
#include <glut/glut.h>
#else
#define FREEGLUT_STATIC
#include <GL/glut.h>
#endif
//觀察者視角
GLFrame viewFrame;
//投影方式
GLFrustum viewFrustum;
//容器類
GLTriangleBatch torusBatch;
//模型視圖矩陣
GLMatrixStack modelViewMatrix;
//投影矩陣
GLMatrixStack projectionMatrix;
GLGeometryTransform transformPipeline;
GLShaderManager shaderManager;
//記錄正背面剔除
int iCull = 0;
//記錄深度測(cè)試
int iDepth = 0;
//窗口變化時(shí)候的回調(diào)函數(shù)
void changeSize(int w,int h)
{
//防止寬高比中除數(shù)為0的bug
if(h==0) h=1;
//1涛舍、創(chuàng)建窗口
glViewport(0, 0, w, h);
//2、創(chuàng)建投影矩陣->透視投影(角度唆途、寬高比富雅、最小距離掸驱、最大距離)
viewFrustum.SetPerspective(35.0f, float(w)/float(h), 1.0f, 500.0f);
//3、拿到這個(gè)投影矩陣没佑,把它加載到我們聲明的透視矩陣
projectionMatrix.LoadMatrix(viewFrustum.GetProjectionMatrix());
//4毕贼、在我們的視圖模型矩陣 頂部載入單元矩陣,相當(dāng)于初始化蛤奢。(**注意** 這個(gè)默認(rèn)是有的鬼癣。可以不寫 )
modelViewMatrix.LoadIdentity();
//5啤贩、設(shè)置變換管道
transformPipeline.SetMatrixStacks(modelViewMatrix, projectionMatrix);
}
//渲染場(chǎng)景
void RenderScene()
{
//1扣溺、清除緩沖區(qū)
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
//2、判斷是否開(kāi)啟正背面剔除
if (iCull) {
glEnable(GL_CULL_FACE);
//擴(kuò)展
//設(shè)置哪一面為正面, GL_CW 瓜晤、GL_CCW ,默認(rèn)GL_CCW
glFrontFace(GL_CCW);
//設(shè)置剔除哪一面, GL_FRONT 、 GL_BACK 腹纳、GL_FRONT_AND_BACK ,默認(rèn)GL_BACK
glCullFace(GL_BACK);
/*
默認(rèn)剔除背面痢掠,那么我們想剔除正面該怎么做呢?
第一種:
glCullFace(GL_BACK);
glFrontFace(GL_CW);
第二種:
glCullFace(GL_FRONT);
*/
}else{
glDisable(GL_CULL_FACE);
}
//3嘲恍、是否開(kāi)啟深度測(cè)試
if(iDepth){
glEnable(GL_DEPTH_TEST);
}else{
glDisable(GL_DEPTH_TEST);
}
//4足画、壓棧
modelViewMatrix.PushMatrix(viewFrame);
//5、使用默認(rèn)光源著色器
GLfloat vColor[] = { 1.0f, 0.0f, 0.0f, 1.0f };
shaderManager.UseStockShader(GLT_SHADER_DEFAULT_LIGHT,transformPipeline.GetModelViewMatrix(),transformPipeline.GetProjectionMatrix(),vColor);
//6佃牛、繪制
torusBatch.Draw();
//7淹辞、出棧
modelViewMatrix.PopMatrix();
//8、提交緩沖區(qū)
glutSwapBuffers();
}
void SetupRC(){
//1俘侠、背景色
glClearColor(0.1, 0.1, 0.1, 1.0);
//2象缀、初始化著色器
shaderManager.InitializeStockShaders();
//3、設(shè)置觀察者位置
viewFrame.MoveForward(10.0f);
//4爷速、繪制甜甜圈
/*
gltMakeTorus(GLTriangleBatch& torusBatch, GLfloat majorRadius, GLfloat minorRadius, GLint numMajor, GLint numMinor)
1央星、GLTriangleBatch容器類
2、外邊緣半徑
3惫东、內(nèi)邊緣半徑
4莉给、主半徑細(xì)分單元數(shù)量
5、從半徑細(xì)分單元數(shù)量
*/
gltMakeTorus(torusBatch, 1.0f, 0.3f, 60, 30);
//切換點(diǎn)狀時(shí)點(diǎn)太小廉沮,這里提前設(shè)置
glPointSize(5.0f);
}
//控制Camera的移動(dòng)颓遏,從而改變視口
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();
}
void RightButtonMenu(int value){
switch(value)
{
case 1:
iDepth = !iDepth;
break;
case 2:
iCull = !iCull;
break;
case 3:
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
break;
case 4:
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
break;
case 5:
glPolygonMode(GL_FRONT_AND_BACK, GL_POINT);
break;
}
glutPostRedisplay();
}
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("Laps");
//函數(shù)回調(diào)
glutReshapeFunc(changeSize);
glutDisplayFunc(RenderScene);
glutSpecialFunc(SpecialKeys);
//**新方法:右鍵打開(kāi)列表函數(shù)
glutCreateMenu(RightButtonMenu);
glutAddMenuEntry("深度測(cè)試",1);
glutAddMenuEntry("正背面剔除",2);
glutAddMenuEntry("充滿狀", 3);
glutAddMenuEntry("線狀", 4);
glutAddMenuEntry("點(diǎn)狀", 5);
glutAttachMenu(GLUT_RIGHT_BUTTON);
GLenum err = glewInit();
if (GLEW_OK != err) {
fprintf(stderr, "GLEW Error: %s\n", glewGetErrorString(err));
return 1;
}
//初始化圖案信息
SetupRC();
glutMainLoop();
return 0;
}