本篇將會(huì)介紹一個(gè)大球的自轉(zhuǎn)以及一個(gè)小球圍繞大球公轉(zhuǎn)的demo骆姐,效果如下圖:
實(shí)現(xiàn)過程
如上圖所示,整個(gè)項(xiàng)目的基本流程較之前幾個(gè)例子沒有太多的變化捏题。都是:
- 初始化窗口诲锹;
- 注冊(cè)各函數(shù)的監(jiān)聽,如 重塑函數(shù)涉馅、重繪函數(shù)等归园;
- 調(diào)用setupRC,初始化窗口背景稚矿、著色器管理器庸诱、頂點(diǎn)數(shù)據(jù)等;
- 開啟glut的mainloop晤揣,類似iOS的runloop桥爽。
1、SetupRC方法
#pragma mark - 頂點(diǎn)數(shù)據(jù)私有方法
void vertextDataFloor() {
floorBatch.Begin(GL_LINES, 324);
/*
GL_LINES 每兩個(gè)點(diǎn)畫一條線
畫法是
第一次循環(huán)
(-20,-0.5,20) 到(-20昧识,-0.5钠四,-20) 畫一條直線
(20,-0.5跪楞,-20)到(-20缀去,-0.5,-20)畫一條直線
第二次循環(huán)
(-19.5,-0.5,20) 到(-19.5甸祭,-0.5缕碎,-20) 畫一條直線
(20,-0.5池户,-19.5)到(-20咏雌,-0.5凡怎,-19.5)畫一條直線
直到最后一次循環(huán),閉合整個(gè)網(wǎng)格
*/
for (GLfloat x = -20.f; x <= 20.f; x+=0.5f) {
floorBatch.Vertex3f(-x, commonY, 20.f);
floorBatch.Vertex3f(-x, commonY, -20.f);
floorBatch.Vertex3f(20.f, commonY, x);
floorBatch.Vertex3f(-20.f, commonY, x);
}
floorBatch.End();
}
void vertextDataBigBall() {
gltMakeSphere(torusBatch, 0.5f, 40, 80);
}
void vertextDataSmallBallRotate() {
gltMakeSphere(sphereBatch, 0.2, 20, 40);
}
void vertextDataRandomSmallBall(){
for (int i = 0; i < SMALL_BALL_NUMBER; i ++) {
GLfloat randowX = ((random()%400) - 200) * 0.1;
GLfloat randowZ = ((random()%400) - 200) * 0.1;
sphereFrames[i].SetOrigin(randowX,0,randowZ);
}
}
void SetupRC() {
glClearColor(1, 1, 1, 1);
glEnable(GL_LINE_SMOOTH);
shaderManager.InitializeStockShaders();
vertextDataFloor();
vertextDataBigBall();
vertextDataSmallBallRotate();
vertextDataRandomSmallBall();
}
在這個(gè)方法中赊抖,主要做一些初始化的工作统倒,如初始化窗口背景色、著色器管理器氛雪、頂點(diǎn)數(shù)據(jù)等檐薯。
2、ChangeSize方法
void ChangeSize(int nWidth, int nHeight) {
glViewport(0, 0, nWidth, nHeight);
viewFrustum.SetPerspective(35.f, float(nWidth)/float(nHeight), 1.f, 500.f);
//將投影矩陣載入投影矩陣堆棧
projectionMatrixStack.LoadMatrix(viewFrustum.GetProjectionMatrix());
//可以不調(diào)用注暗,默認(rèn)會(huì)有一個(gè)單元矩陣
modelViewMatrixStack.LoadIdentity();
transformPipeline.SetMatrixStacks(modelViewMatrixStack, projectionMatrixStack);
}
如代碼所示,在這個(gè)方法中依然是常規(guī)的設(shè)置視口的位置和大小墓猎、設(shè)置投影方式捆昏、載入投影矩陣到投影矩陣堆棧、載入單元矩陣進(jìn)模型視圖矩陣堆棧毙沾,然后將這兩個(gè)堆棧設(shè)置到變化管道對(duì)象里骗卜,方便使用和管理。
3左胞、RenderScence方法
void RenderScence() {
//GL_STENCIL_BUFFER_BIT 在這個(gè)demo中寇仓,可以不清
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
//開啟深度測(cè)試
glEnable(GL_DEPTH_TEST);
modelViewMatrixStack.PushMatrix();
//畫地面
static GLfloat vBlue[] = {0.f,0.5f,1.f,1.f};
static GLfloat vBigBall[] = {0,0,1,1};
static GLfloat vSmallBall[] = {1,0.5,0,1};
static GLfloat vSmallBallRandom[] = {0,0.5,1,1};
static CStopWatch watchObj;
GLfloat angle = watchObj.GetElapsedSeconds() * 60.f;
M3DMatrix44f viewMatrix;
viewFrame.GetCameraMatrix(viewMatrix);
modelViewMatrixStack.MultMatrix(viewMatrix);
shaderManager.UseStockShader(GLT_SHADER_FLAT,transformPipeline.GetModelViewProjectionMatrix(),vBlue);
floorBatch.Draw();
//棧頂矩陣z軸負(fù)方向方向平移
modelViewMatrixStack.Translate(0, 0, -3);
//復(fù)制一份,畫完大球之后烤宙,pops棧頂數(shù)據(jù)遍烦,然后棧頂數(shù)據(jù)是設(shè)置過平移的矩陣
modelViewMatrixStack.PushMatrix();
modelViewMatrixStack.Rotate(angle, 0, 1, 0);
M3DVector4f pointPosition = {0,10,5,1};
shaderManager.UseStockShader(GLT_SHADER_POINT_LIGHT_DIFF,transformPipeline.GetModelViewMatrix(),
transformPipeline.GetProjectionMatrix(),pointPosition,vBigBall);
torusBatch.Draw();
modelViewMatrixStack.PopMatrix();
for (int i = 0; i < SMALL_BALL_NUMBER; i ++) {
GLFrame frame = sphereFrames[i];
modelViewMatrixStack.PushMatrix();
modelViewMatrixStack.MultMatrix(frame);
shaderManager.UseStockShader(GLT_SHADER_POINT_LIGHT_DIFF,transformPipeline.GetModelViewMatrix(),
transformPipeline.GetProjectionMatrix(),pointPosition,vSmallBallRandom);
sphereBatch.Draw();
modelViewMatrixStack.PopMatrix();
}
//無需壓棧,因?yàn)檫@是當(dāng)前繪制的最后一個(gè)圖形
modelViewMatrixStack.Rotate(angle * -2.f, 0, 1, 0);
//z值的絕對(duì)值越大躺枕,離大球越遠(yuǎn)服猪。
modelViewMatrixStack.Translate(0, 0, 1.1);
shaderManager.UseStockShader(GLT_SHADER_POINT_LIGHT_DIFF,transformPipeline.GetModelViewMatrix(),
transformPipeline.GetProjectionMatrix(),pointPosition,vSmallBall);
sphereBatch.Draw();
modelViewMatrixStack.PopMatrix();
glutSwapBuffers();
glutPostRedisplay();
//關(guān)閉深度測(cè)試
glDisable(GL_DEPTH_TEST);
}
這個(gè)方法是本次案例中,與之前的案例相比最大的方法拐云。
3.1 畫地板
- 清理顏色緩存區(qū)和深度緩存區(qū)罢猪,這里的模板緩存區(qū)在本demo中可以不清。
- 開啟深度測(cè)試
- 棧頂矩陣copy一份壓棧
- 初始化各顏色值以及一個(gè)定時(shí)器叉瘩,定時(shí)器用于計(jì)算當(dāng)前旋轉(zhuǎn)角度
- 從觀察者角色幀獲取觀察者矩陣膳帕,并用當(dāng)前模型視圖矩陣堆棧棧頂矩陣乘以觀察者矩陣,得到結(jié)果覆蓋棧頂矩陣
- 使用平面著色器處理數(shù)據(jù)
- 通過批次類畫出地板
- 在全部圖形繪制完之后薇缅,會(huì)交換緩存區(qū)
3.2 畫大球
- 棧頂矩陣沿z軸負(fù)方向移動(dòng)3危彩;
- 棧頂矩陣copy一份壓棧(后續(xù)計(jì)算完成出棧之后,棧頂矩陣依然是之前沿著z軸負(fù)方向移動(dòng)3之后的矩陣)
- 沿著y軸旋轉(zhuǎn)
- 設(shè)置光源位置
- 使用點(diǎn)光源著色器處理數(shù)據(jù)
- 通過三角形批次類畫出大球
- 針對(duì)最近一次的入棧操作進(jìn)行出棧泳桦,保證棧頂矩陣為之前沿著z軸負(fù)方向移動(dòng)3之后的矩陣恬砂。
- 在全部圖形繪制完之后,會(huì)交換緩存區(qū)
3.3 畫多個(gè)分散的小球
在上述的SetupRC方法中調(diào)用的vertextDataRandomSmallBall方法里蓬痒,做了很多頂點(diǎn)數(shù)據(jù)的初始化泻骤,這些就是隨機(jī)小球的位置數(shù)據(jù)漆羔。
在RenderScence方法中也需要將他們繪制出來。
- 開啟for循環(huán)
- 每次循環(huán)都復(fù)制一份棧頂矩陣壓棧
- 將棧頂矩陣乘以當(dāng)前小球的角色幀數(shù)據(jù)狱掂,賦值給棧頂數(shù)據(jù)
- 使用點(diǎn)光源著色器處理數(shù)據(jù)
- 使用三角形批次類進(jìn)行繪制
- 將每次循環(huán)的棧頂矩陣出棧
- 在全部圖形繪制完之后演痒,會(huì)交換緩存區(qū)
3.4 畫公轉(zhuǎn)的小球
- 棧頂矩陣?yán)@y軸旋轉(zhuǎn)一定的角度,角度根據(jù)當(dāng)前的計(jì)時(shí)器的時(shí)間來計(jì)算
- 棧頂矩陣沿z軸平移操作趋惨,無論正負(fù)鸟顺,絕對(duì)值越大,離大球越遠(yuǎn)
- 使用點(diǎn)光源著色器處理數(shù)據(jù)
- 使用三角形批次類進(jìn)行繪制
- 將當(dāng)前棧頂矩陣出
3.5 收尾
- 交換緩存區(qū)
- 提交重新渲染器虾,保證重復(fù)調(diào)用RenderScence方法盾计,形成旋轉(zhuǎn)動(dòng)畫
- 關(guān)閉深度測(cè)試
總結(jié):
RenderScence方法中变勇,會(huì)有各種矩陣變換的計(jì)算操作,將原始的頂點(diǎn)數(shù)據(jù),經(jīng)過各種變換逝淹,全部到達(dá)一個(gè)新的位置谚赎,最終實(shí)現(xiàn)我們要的效果诬烹。在計(jì)算的過程中此虑,需要注意的幾個(gè)點(diǎn):
- 整體的入棧和出棧要成對(duì)出現(xiàn),有入棧就必須有出棧库正,否則下次繪制的時(shí)候數(shù)據(jù)會(huì)錯(cuò)亂
- 對(duì)于整個(gè)變換過程曲楚,拿公轉(zhuǎn)小球舉例,雖然它是經(jīng)過了
modelViewMatrixStack.Translate(0, 0, -3);
modelViewMatrixStack.Rotate(angle * -2.f, 0, 1, 0);
modelViewMatrixStack.Translate(0, 0, 1.1);
這三部褥符,但是在OpenGL實(shí)現(xiàn)的時(shí)候龙誊,由于OpenGL采用的是右乘的方式,所以喷楣,其實(shí)小球是經(jīng)過了先z軸平移载迄,再旋轉(zhuǎn),再z軸平移的操作抡蛙。
而對(duì)于大球來說护昧,它經(jīng)過了
modelViewMatrixStack.Translate(0, 0, -3);
modelViewMatrixStack.PushMatrix();
modelViewMatrixStack.Rotate(angle, 0, 1, 0);
這三部操作,而OpenGL實(shí)現(xiàn)的時(shí)候粗截,其實(shí)大球是先旋轉(zhuǎn)惋耙,再z軸平移。
因此熊昌,對(duì)于大球來說绽榛,它是自轉(zhuǎn),而對(duì)于小球來說婿屹,它是公轉(zhuǎn)灭美。
最終總結(jié)
上述為個(gè)人實(shí)現(xiàn)與總結(jié),如有錯(cuò)漏之處昂利,歡迎并感謝批評(píng)指正届腐。