教程
OpenGLES入門教程1-Tutorial01-GLKit
OpenGLES入門教程2-Tutorial02-shader入門
OpenGLES入門教程3-Tutorial03-三維變換
OpenGLES入門教程4-Tutorial04-GLKit進(jìn)階
OpenGLES進(jìn)階教程1-Tutorial05-地球月亮
OpenGLES進(jìn)階教程2-Tutorial06-光線
OpenGLES進(jìn)階教程3-Tutorial07-粒子效果
OpenGLES進(jìn)階教程4-Tutorial08-幀緩存
這一次是碰碰車颁湖。非常重要的一節(jié)岔留,是前面教程的一個(gè)應(yīng)用止吐。
這一次的內(nèi)容較多,包括復(fù)雜頂點(diǎn)模型袖牙、第一和第三人稱視角變化、萬向節(jié)鎖、物理碰撞模擬悯恍、平滑動(dòng)畫與高低通濾波器、模型封裝伙狐、材質(zhì)繪制涮毫。
這一次的教程會(huì)盡可能多詳細(xì)介紹。
效果展示
核心思路
通過加載頭文件的頂點(diǎn)數(shù)據(jù)贷屎,得到復(fù)雜的頂點(diǎn)模型--車和場(chǎng)景罢防。
先繪制場(chǎng)景,再單獨(dú)繪制每一輛車唉侄。
第三人稱視角固定為俯視角咒吐,第一人稱視角選取一輛車作為視點(diǎn)所在,視線為車的前進(jìn)方向。
當(dāng)?shù)谝惶裉尽⒌谌朔Q視角切換的時(shí)候候生,通過濾波器來達(dá)到視線平滑過渡。
物理碰撞通過向量運(yùn)算來模擬绽昼。
細(xì)節(jié)解析
1唯鸭、模型加載
模型放在頭文件,car的數(shù)據(jù)放在bumperCar.h
包括bumperCarVerts
頂點(diǎn)位置硅确,bumperCarNormals
頂點(diǎn)法線目溉,總共有bumperCarNumVerts = 1164
個(gè)頂點(diǎn)。
場(chǎng)景的數(shù)據(jù)放在bumperRink.h
菱农,格式和car一致缭付,頂點(diǎn)較少,只有bumperRinkNumVerts = 180
個(gè)頂點(diǎn)循未。
2蛉腌、模型封裝
-
SceneMesh
類是網(wǎng)格類,通過AGLKVertexAttribArrayBuffer
管理頂點(diǎn)數(shù)據(jù)只厘,發(fā)送頂點(diǎn)數(shù)據(jù)到GPU烙丛,分配頂點(diǎn)數(shù)據(jù)內(nèi)存,繪制頂點(diǎn)數(shù)據(jù)羔味。 -
SceneModel
類是模型類河咽,屬性axisAlignedBoundingBox
放置了模型的最大最小邊界,屬性mesh
是模型的網(wǎng)格類赋元,管理頂點(diǎn)數(shù)據(jù)忘蟹。 -
SceneCarModel
類是car的模型類,包括car的頂點(diǎn)數(shù)據(jù)和模型的基本屬性搁凸,可以繪制car模型媚值。 -
SceneRinkModel
類是場(chǎng)景的模型類,包括場(chǎng)景的頂點(diǎn)數(shù)據(jù)和邊界等基本屬性护糖,可以繪制場(chǎng)景褥芒。 -
SceneCar
類是car的邏輯類,包括car的速度嫡良、位置锰扶、偏航角、半徑寝受,還有濾波器函數(shù)坷牛、cars的碰撞處理、car與場(chǎng)景的碰撞處理很澄、繪制car模型京闰。
觀察SceneCar
類的初始化函數(shù)
- (id)initWithModel:(SceneModel *)aModel
position:(GLKVector3)aPosition
velocity:(GLKVector3)aVelocity
color:(GLKVector4)aColor;
SceneCar需要SceneModel(模型類)颜及、position(位置)、velocity(速度)蹂楣、color(顏色)來初始化俏站。
注意,這里的SceneCar并沒有依賴SceneCarModel捐迫,而是依賴抽象(基類)SceneModel乾翔,實(shí)現(xiàn)了解耦爱葵∈┐鳎可以新建一個(gè)SceneOtherCar繼承SceneModel,傳遞給SceneCar萌丈,不需要修改SceneCar的代碼就可以創(chuàng)建出一個(gè)新的car赞哗。
3、視角變化
視角通過函數(shù)GLKMatrix4MakeLookAt
確定辆雾,參數(shù)如下:
GLKMatrix4 GLKMatrix4MakeLookAt(
float eyeX, float eyeY, float eyeZ,
float centerX, float centerY, float centerZ,
float upX, float upY, float upZ)
GLKMatrix4MakeLookAt() 函數(shù)會(huì)計(jì)算并返回一個(gè)model-view矩陣肪笋,這個(gè)矩陣會(huì)對(duì)齊從眼睛的位置到看向的位置之間的矢量與當(dāng)前視域的中心線。
GLKMatrix4MakeLookAt()的詳細(xì)解析會(huì)在接下來的教程--基于視錐體(平截體)的OpenGL ES性能優(yōu)化篇詳細(xì)介紹度迂,目前可以按照參數(shù)來理解:假設(shè)有一個(gè)人藤乙,他的眼睛在前三個(gè)參數(shù)指定的位置,眼睛望向中間三個(gè)參數(shù)的位置惭墓,頭的朝向?yàn)樽詈笕齻€(gè)參數(shù)的方向坛梁,up(0,1腊凶,0)為標(biāo)準(zhǔn)方向划咐。
- 第一人稱視角如下踢关。
self.eyePosition = GLKVector3Make(10.5, 5.0, 0.0);
self.lookAtPosition = GLKVector3Make(0.0, 0.5, 0.0);
- 第三人稱視角如下央串。
self.targetEyePosition = GLKVector3Make(viewerCar.position.x,
viewerCar.position.y + 0.45f,
viewerCar.position.z);
self.targetLookAtPosition = GLKVector3Add(_eyePosition, viewerCar.velocity);
細(xì)心的同學(xué)已經(jīng)發(fā)現(xiàn)有eyePosition
和targetEyePosition
兩個(gè)position。
eyePosition表示的是當(dāng)前eye所在的位置岛都,targetEyePosition表示的eye最終的目的风瘦。
之所以需要設(shè)置一個(gè)目標(biāo)的位置队魏,是為了做視角的切換,通過高通濾波器函數(shù)SceneVector3FastLowPassFilter
和低通濾波器函數(shù)SceneVector3SlowLowPassFilter
万搔,實(shí)現(xiàn)視角平滑過渡器躏。
4、濾波器函數(shù)
- 高通濾波器函數(shù)蟹略,50.0是一個(gè)可替換的較大的常數(shù)登失。可以模擬撞墻后震動(dòng)的效果挖炬,因?yàn)?0.0比較大揽浙,current值再增加后可能超過target。
GLfloat SceneScalarFastLowPassFilter(NSTimeInterval elapsed,
GLfloat target,
GLfloat current)
{
return current + (50.0 * elapsed * (target - current));
}
- 低通濾波器函數(shù),4.0是一個(gè)可替換的較小的常數(shù)馅巷√懦妫可以模擬視角切換過程的效果,因?yàn)?.0比較小钓猬,current會(huì)逐漸接近target稍刀。
GLfloat SceneScalarSlowLowPassFilter(NSTimeInterval elapsed,
GLfloat target,
GLfloat current)
{
return current + (4.0 * elapsed * (target - current));
}
5、萬向節(jié)鎖
| y
|<==-----------<==飛機(jī)
| -
| -
|
|______________________x
如上敞曹,假設(shè)地面的正北方向?yàn)閤軸账月,正朝上為y軸。現(xiàn)在左下角的原點(diǎn)處有一臺(tái)望遠(yuǎn)鏡澳迫,它通過(a, b)來望向天空中的飛機(jī)局齿,a為與望遠(yuǎn)鏡與x軸正方向的夾角,我們用偏航角來解釋橄登;b為望遠(yuǎn)鏡向上抬起后與地面的夾角抓歼,我們用高度角來解釋。
對(duì)于任意一臺(tái)坐標(biāo)為(a, b)的飛機(jī)拢锹,我們都可以通過先偏移a偏航角谣妻,再抬起b高度角,從而觀察到飛機(jī)卒稳。
現(xiàn)在假設(shè)有一臺(tái)坐標(biāo)為(0, 45)的飛機(jī)蹋半,朝望遠(yuǎn)鏡飛來。飛機(jī)在望遠(yuǎn)鏡的正北方向展哭,偏航角為0湃窍,高度角為45。隨著飛機(jī)不斷接近望遠(yuǎn)鏡匪傍,偏航角不變您市,高度角不斷變大。
當(dāng)飛機(jī)飛到望遠(yuǎn)鏡的正上空的時(shí)候役衡,坐標(biāo)為(0, 90)茵休。
此時(shí),如果飛機(jī)轉(zhuǎn)向朝東方向飛手蝎,偏航角由0直接變?yōu)?0榕莺,但是此時(shí)由于望遠(yuǎn)鏡正朝上(之前的坐標(biāo)為0,90)棵介,偏航角的改變沒有意義(丟失一個(gè)自由度)钉鸯。
本教程還不需要用到四元數(shù)來解決這個(gè)問題,只是作為背景了解一下邮辽。Google上面有很多解釋唠雕,比我講的好很多贸营。
6、物理碰撞模擬
- car與墻壁的碰撞
通過(SceneAxisAllignedBoundingBox)rinkBoundingBox
可以得到墻壁的最大小邊界岩睁。根據(jù)car的radius屬性得到半徑钞脂,通過半徑radius+nextPosition與rinkBoundingBox判斷是否到達(dá)邊界。
如果到達(dá)邊界則把對(duì)應(yīng)軸的速度向量反向捕儒。 -
car之間的碰撞
假設(shè)兩輛車分別為self和other冰啃。
selfCar的速度為velocity、位置為position刘莹,otherCar的速度為otherVelocity阎毅、位置為otherPosition。
通過position和otherPosition栋猖,可以得到一條直線净薛,car的碰撞就發(fā)生在這一條線上的方向上汪榔。
velocity在直線上的分量為tanOwnVelocity蒲拉,otherVelocity在直線上的分量為tanOtherVelocity。
如上痴腌,碰撞完成后selfCar的速度為velocity - tanOwnVelocity雌团,otherCar的速度為otherVelocity - tanOtherVelocity。
self.velocity = GLKVector3Subtract(ownVelocity, tanOwnVelocity);
currentCar.velocity = GLKVector3Subtract(otherVelocity, tanOtherVelocity);
7士聪、材質(zhì)繪制
GLKBaseEffect
有一個(gè)material
屬性锦援,用于材質(zhì)顏色的設(shè)置。
注意的是需要調(diào)用anEffect的prepareToDraw之后剥悟,再調(diào)用model的draw灵寺。
//設(shè)置材質(zhì)
anEffect.material.diffuseColor = self.color;
anEffect.material.ambientColor = self.color;
[anEffect prepareToDraw];
[model draw];
思考
場(chǎng)地、車都是分別繪制的区岗,他們的位置是如何控制的略板?當(dāng)?shù)谝蝗朔Q視角切換后,為什么車的顯示會(huì)跟著變大慈缔?
這一部分作為思考題叮称,就不給出答案了,在代碼里面有注釋藐鹤。
總結(jié)
這一次的實(shí)現(xiàn)還是比較簡(jiǎn)單瓤檐,模型是放在頭文件,并沒有用modellist文件來管理娱节。
我在源代碼的基礎(chǔ)上挠蛉,簡(jiǎn)化了不需要的代碼和增加了很多注釋∫蘼看完教程和代碼的后谴古,相信都能理解绍移。
附上源碼