學(xué)習(xí)書(shū)籍: OpenGL 超級(jí)寶典(中文第五版) 密碼:fu4w
書(shū)籍源碼:OpenGL 超級(jí)寶典第五版源代碼 密碼:oyb4
環(huán)境搭建:OpenGL 學(xué)習(xí) 01 - Mac 搭建 OpenGL 環(huán)境
之前我們都只是畫(huà)平面圖形曲掰,這節(jié)我們來(lái)畫(huà)一個(gè) 3D 圖形链沼,對(duì)比下平面圖形和 3D 圖形繪制的一些區(qū)別和注意點(diǎn)。
基本概念
花托
一種環(huán)狀的像面包圈一樣的物體牵辣,如果要我們自己去一個(gè)個(gè)去計(jì)算花托的每個(gè)點(diǎn)烟瞧,是要花很多時(shí)間,GLTools 里面已經(jīng)為我們集成好了得到花托頂點(diǎn)數(shù)據(jù)的函數(shù)
//得到花托的頂點(diǎn)數(shù)據(jù)
void gltMakeTorus(
GLTriangleBatch& torusBatch, // 返回的三角形批次,和普通批次功能類(lèi)似,是專(zhuān)門(mén)畫(huà)三角形的批次
GLfloat majorRadius, // 中心到外邊緣的半徑
GLfloat minorRadius, // 外邊緣到內(nèi)邊緣的距離
GLint numMajor, // 片段數(shù)氮墨,即花托由多少個(gè)片段面組成
GLint numMinor // 堆疊數(shù),即每個(gè)片段面由多少個(gè)頂點(diǎn)組成
);
油畫(huà)法渲染
在默認(rèn)情況下吐葵,我們所渲染的點(diǎn)勇边、線或三角形都會(huì)在屏幕上進(jìn)行光柵化,并且會(huì)按照在組合批次時(shí)的指定順序進(jìn)行排列折联,這樣就會(huì)出現(xiàn)一些問(wèn)題,比如一些三角形在物體背面识颊,我們應(yīng)該是看不到的诚镰,或者前面繪制的三角形可能被后面繪制的三角形所覆蓋。
對(duì)于這些問(wèn)題祥款,可能的解決方法之一是清笨,對(duì)這些三角形進(jìn)行排序,先渲染較遠(yuǎn)的三角形刃跛,再渲染較近的三角形抠艾,這種方式稱(chēng)為“油畫(huà)法”〗瓣迹【油畫(huà)法渲染是非常低效的检号,重疊部分進(jìn)行了多次寫(xiě)操作,三角形排序開(kāi)銷(xiāo)大】
正面和背面剔除
對(duì)正面和背面三角形進(jìn)行區(qū)分的原因之一就是為了進(jìn)行剔除蛙酪。背面剔除能極大提高性能齐苛,且能修正一些上面提出的問(wèn)題。
剔除是在渲染的圖元裝配階段就整體拋棄掉一些三角形桂塞,并且沒(méi)有執(zhí)行任何不恰當(dāng)?shù)墓鈻呕僮鳌?/strong>
深度測(cè)試
深度測(cè)試是另外一種高效消除隱藏表面的技術(shù)凹蜂。深度測(cè)試是在繪制每個(gè)像素時(shí),分配一個(gè)深度值z
阁危,表示該點(diǎn)與觀察者的距離【注意玛痊,這里是深度值越大,表示離觀察者越近】狂打,當(dāng)另外一個(gè)像素需要繪制在同一個(gè)位置擂煞,會(huì)先比較深度值,深度值大的說(shuō)明在前面菱父,深度值小的說(shuō)明在后面颈娜。
深度測(cè)試是通過(guò)深度緩沖區(qū)實(shí)現(xiàn)的剑逃,深度緩存區(qū)存儲(chǔ)了屏幕上每個(gè)像素的深度值。要使用深度測(cè)試官辽,一般需要配置渲染模式:
/*初始化渲染模式*/
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH);
深度測(cè)試還有一個(gè)很強(qiáng)大的作用蛹磺,就是處理重疊的獨(dú)立對(duì)象,這是剔除技術(shù)無(wú)法處理的一種情況同仆。深度測(cè)試是先渲染離觀察者較近的對(duì)象萤捆,再渲染較遠(yuǎn)的對(duì)象,和“油畫(huà)法”剛好相反俗批。
多邊形模式
默認(rèn)情況下,多邊形是作為實(shí)心圖形進(jìn)行繪制岁忘,我們可以通過(guò)調(diào)整多邊形模式辛慰,讓多邊形繪制為只有點(diǎn)或輪廓。此外干像,我們也可以在多邊形的兩面分別應(yīng)用這個(gè)多邊形模式
多邊形模式:
- 實(shí)體(GL_FULL)
- 輪廓(GL_LINE)
- 點(diǎn)(GL_POINT)
多邊形模式應(yīng)用面:
- 正面(GL_FRONT)
- 背面(GL_BACK)
- 雙面(GL_FRONT_AND_BACK)
源碼解析
創(chuàng)建右鍵菜單帅腌,便于切換各種模式
//創(chuàng)建菜單,綁定一個(gè)響應(yīng)方法
glutCreateMenu(ProcessMenu);
//為菜單添加選項(xiàng)
glutAddMenuEntry("Toggle depth test", 1);
glutAddMenuEntry("Toggle cull backface", 2);
glutAddMenuEntry("Set Fill Mode", 3);
glutAddMenuEntry("Set Line Mode", 4);
glutAddMenuEntry("Set Point Mode", 5);
//右鍵彈出菜單
glutAttachMenu(GLUT_RIGHT_BUTTON);
物體 3D 旋轉(zhuǎn)麻汰,便于查看各種模式優(yōu)劣
//上速客、下、左五鲫、右按鍵溺职,3D 旋轉(zhuǎn)
/*
* RotateWorld(float fAngle, float x, float y, float z)
* fAngle: 旋轉(zhuǎn)弧度, x/y/z:以哪個(gè)坐標(biāo)軸旋轉(zhuǎn)
* m3dDegToRad:角度 -> 弧度
* viewFrame:物體世界坐標(biāo)
*/
switch (key) {
case GLUT_KEY_UP:
viewFrame.RotateWorld(m3dDegToRad(-5.0f), 1.0f, 0.0f, 0.0f);
break;
case GLUT_KEY_DOWN:
viewFrame.RotateWorld(m3dDegToRad(5.0f), 1.0f, 0.0f, 0.0f);
break;
case GLUT_KEY_LEFT:
viewFrame.RotateWorld(m3dDegToRad(-5.0f), 0.0f, 1.0f, 0.0f);
break;
case GLUT_KEY_RIGHT:
viewFrame.RotateWorld(m3dDegToRad(5.0f), 0.0f, 1.0f, 0.0f);
break;
default:
break;
}
開(kāi)啟/關(guān)閉剔除
// 開(kāi)啟剔除
glEnable(GL_CULL_FACE);
// 關(guān)閉剔除
glDisable(GL_CULL_FACE);
// 默認(rèn)我們是剔除背面,也可以剔除正面位喂,調(diào)以下函數(shù)進(jìn)行控制
glCullFace(GL_FRONT); // 控制剔除的面為正面
glCullFace(GL_BACK); // 控制剔除的面為背面
glCullFace(GL_FRONT_AND_BACK); // 控制剔除的面為雙面
開(kāi)啟/關(guān)閉深度測(cè)試
// 開(kāi)啟深度測(cè)試
glEnable(GL_DEPTH_TEST);
// 關(guān)閉深度測(cè)試
glDisable(GL_DEPTH_TEST);
配置多邊形模式
//多邊形面模式為雙面實(shí)體
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
//多邊形面模式為雙面輪廓
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
//多邊形面模式為雙面點(diǎn)
glPolygonMode(GL_FRONT_AND_BACK, GL_POINT);
全部源碼
#include <GLTools.h> // OpenGL toolkit
#include <GLMatrixStack.h>
#include <GLFrame.h>
#include <GLFrustum.h>
#include <GLGeometryTransform.h>
#include <math.h>
#include <glut/glut.h>
GLFrame viewFrame;
GLFrustum viewFrustum;
GLTriangleBatch torusBatch;
GLMatrixStack modelViewMatrix;
GLMatrixStack projectionMatrix;
GLGeometryTransform transformPipeline;
GLShaderManager shaderManager;
//是否開(kāi)啟正反面剔除
int iCull = 0;
//是否開(kāi)始深度測(cè)試
int iDepth = 0;
//點(diǎn)擊菜單選項(xiàng)
void ProcessMenu(int value) {
switch(value) {
case 1:
//開(kāi)關(guān)深度測(cè)試
iDepth = !iDepth;
break;
case 2:
//開(kāi)關(guān)正反面剔除
iCull = !iCull;
break;
case 3:
//開(kāi)關(guān)多邊形面模式
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
break;
case 4:
//開(kāi)關(guān)多邊形線模式
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
break;
case 5:
//開(kāi)關(guān)多邊形點(diǎn)模式
glPolygonMode(GL_FRONT_AND_BACK, GL_POINT);
break;
}
//觸發(fā)渲染
glutPostRedisplay();
}
//渲染畫(huà)面
void RenderScene(void) {
//清除一個(gè)或一組特定的緩沖區(qū)
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
//判斷是否開(kāi)啟正反面剔除
if(iCull)
glEnable(GL_CULL_FACE);
else
glDisable(GL_CULL_FACE);
//判斷是否開(kāi)啟深度測(cè)試
if(iDepth)
glEnable(GL_DEPTH_TEST);
else
glDisable(GL_DEPTH_TEST);
//保存當(dāng)前的模型視圖矩陣
modelViewMatrix.PushMatrix(viewFrame);
//這里使用默認(rèn)光源進(jìn)行著色,可以看到陰影
GLfloat vRed[] = { 1.0f, 0.0f, 0.0f, 1.0f };
shaderManager.UseStockShader(GLT_SHADER_DEFAULT_LIGHT, transformPipeline.GetModelViewMatrix(), transformPipeline.GetProjectionMatrix(), vRed);
//畫(huà)花托
torusBatch.Draw();
// 還原以前的模型視圖矩陣
modelViewMatrix.PopMatrix();
//將在后臺(tái)緩沖區(qū)進(jìn)行渲染塑崖,然后在結(jié)束時(shí)交換到前臺(tái)
glutSwapBuffers();
}
//為程序作一次性的設(shè)置
void SetupRC() {
//設(shè)置窗口背景顏色
glClearColor(0.3f, 0.3f, 0.3f, 1.0f );
//初始化著色器管理器
shaderManager.InitializeStockShaders();
//設(shè)置變換管線以使用兩個(gè)矩陣堆棧
transformPipeline.SetMatrixStacks(modelViewMatrix, projectionMatrix);
//移動(dòng)物體的位置点待,值越大物體越遠(yuǎn),值越小物體越近
viewFrame.MoveForward(6.0f);
//創(chuàng)建一個(gè)花托批次弃舒,參數(shù)依次是:批次癞埠,外半徑,內(nèi)半徑聋呢,片段數(shù)苗踪,堆疊數(shù)(在計(jì)算機(jī)中圓是正多邊形,頂點(diǎn)數(shù)越多越像真正的圓)
gltMakeTorus(torusBatch, 1.0f, 0.3f, 52, 26);
//設(shè)置默認(rèn)點(diǎn)大小
glPointSize(4.0f);
}
//特殊按鍵(功能鍵或者方向鍵)監(jiān)聽(tīng)
void SpecialKeys(int key, int x, int y) {
//上削锰、下通铲、左、右按鍵器贩,3D 旋轉(zhuǎn)
/*
* RotateWorld(float fAngle, float x, float y, float z)
* fAngle: 旋轉(zhuǎn)弧度, x/y/z:以哪個(gè)坐標(biāo)軸旋轉(zhuǎn)
* m3dDegToRad:角度 -> 弧度
*/
switch (key) {
case GLUT_KEY_UP:
viewFrame.RotateWorld(m3dDegToRad(-5.0f), 1.0f, 0.0f, 0.0f);
break;
case GLUT_KEY_DOWN:
viewFrame.RotateWorld(m3dDegToRad(5.0f), 1.0f, 0.0f, 0.0f);
break;
case GLUT_KEY_LEFT:
viewFrame.RotateWorld(m3dDegToRad(-5.0f), 0.0f, 1.0f, 0.0f);
break;
case GLUT_KEY_RIGHT:
viewFrame.RotateWorld(m3dDegToRad(5.0f), 0.0f, 1.0f, 0.0f);
break;
default:
break;
}
//觸發(fā)渲染
glutPostRedisplay();
}
//窗口大小改變時(shí)接受新的寬度和高度颅夺,其中0,0代表窗口中視口的左下角坐標(biāo)朋截,w,h代表像素
void ChangeSize(int width, int height) {
// 防止下面除法的除數(shù)為0導(dǎo)致的閃退
if(height == 0) height = 1;
//設(shè)置視圖窗口位置
glViewport(0, 0, width, height);
// 創(chuàng)建投影矩陣吧黄,并將它載入到投影矩陣堆棧中
viewFrustum.SetPerspective(35.0f, float(width) / float(height), 1.0f, 500.0f);
projectionMatrix.LoadMatrix(viewFrustum.GetProjectionMatrix());
}
//程序入口
int main(int argc, char* argv[]) {
//設(shè)置當(dāng)前工作目錄部服,針對(duì)MAC OS X
gltSetWorkingDirectory(argv[0]);
//初始化GLUT庫(kù)
glutInit(&argc, argv);
/*初始化渲染模式,其中標(biāo)志GLUT_DOUBLE拗慨、GLUT_RGBA廓八、GLUT_DEPTH、GLUT_STENCIL分別指
雙緩沖窗口赵抢、RGBA顏色模式剧蹂、深度測(cè)試、模板緩沖區(qū)*/
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH | GLUT_STENCIL);
//初始化窗口大小
glutInitWindowSize(800, 600);
//創(chuàng)建窗口
glutCreateWindow("Geometry Test Program");
//注冊(cè)回調(diào)函數(shù)
glutReshapeFunc(ChangeSize);
glutDisplayFunc(RenderScene);
glutSpecialFunc(SpecialKeys);
//創(chuàng)建菜單烦却,綁定一個(gè)響應(yīng)方法
glutCreateMenu(ProcessMenu);
//為菜單添加選項(xiàng)
glutAddMenuEntry("Toggle depth test",1);
glutAddMenuEntry("Toggle cull backface",2);
glutAddMenuEntry("Set Fill Mode", 3);
glutAddMenuEntry("Set Line Mode", 4);
glutAddMenuEntry("Set Point Mode", 5);
//右鍵彈出菜單
glutAttachMenu(GLUT_RIGHT_BUTTON);
//確保驅(qū)動(dòng)程序的初始化中沒(méi)有出現(xiàn)任何問(wèn)題宠叼。
GLenum err = glewInit();
if(GLEW_OK != err) {
fprintf(stderr, "glew error:%s\n", glewGetErrorString(err));
return 1;
}
//初始化設(shè)置
SetupRC();
//進(jìn)入調(diào)用循環(huán)
glutMainLoop();
return 0;
}
Demo 源代碼在這里:github->openGLDemo->04-GeoTest
有什么問(wèn)題可以在下方評(píng)論區(qū)提出,寫(xiě)得不好可以提出你的意見(jiàn)其爵,我會(huì)合理采納的车吹,O(∩_∩)O哈哈~,求關(guān)注求贊