OpenGL ES不容錯(cuò)過的實(shí)戰(zhàn)-碰碰車

教程

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)有eyePositiontargetEyePosition兩個(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)化了不需要的代碼和增加了很多注釋∫蘼看完教程和代碼的后谴古,相信都能理解绍移。

附上源碼

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市讥电,隨后出現(xiàn)的幾起案子蹂窖,更是在濱河造成了極大的恐慌,老刑警劉巖恩敌,帶你破解...
    沈念sama閱讀 216,744評(píng)論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件瞬测,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡纠炮,警方通過查閱死者的電腦和手機(jī)月趟,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,505評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來恢口,“玉大人孝宗,你說我怎么就攤上這事「纾” “怎么了因妇?”我有些...
    開封第一講書人閱讀 163,105評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)猿诸。 經(jīng)常有香客問我婚被,道長(zhǎng),這世上最難降的妖魔是什么梳虽? 我笑而不...
    開封第一講書人閱讀 58,242評(píng)論 1 292
  • 正文 為了忘掉前任址芯,我火速辦了婚禮,結(jié)果婚禮上窜觉,老公的妹妹穿的比我還像新娘谷炸。我一直安慰自己,他們只是感情好禀挫,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,269評(píng)論 6 389
  • 文/花漫 我一把揭開白布旬陡。 她就那樣靜靜地躺著,像睡著了一般特咆。 火紅的嫁衣襯著肌膚如雪季惩。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,215評(píng)論 1 299
  • 那天腻格,我揣著相機(jī)與錄音画拾,去河邊找鬼。 笑死菜职,一個(gè)胖子當(dāng)著我的面吹牛青抛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播酬核,決...
    沈念sama閱讀 40,096評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼蜜另,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼适室!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起举瑰,我...
    開封第一講書人閱讀 38,939評(píng)論 0 274
  • 序言:老撾萬榮一對(duì)情侶失蹤捣辆,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后此迅,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體汽畴,經(jīng)...
    沈念sama閱讀 45,354評(píng)論 1 311
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,573評(píng)論 2 333
  • 正文 我和宋清朗相戀三年耸序,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了忍些。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,745評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡坎怪,死狀恐怖罢坝,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情搅窿,我是刑警寧澤嘁酿,帶...
    沈念sama閱讀 35,448評(píng)論 5 344
  • 正文 年R本政府宣布,位于F島的核電站戈钢,受9級(jí)特大地震影響痹仙,放射性物質(zhì)發(fā)生泄漏是尔。R本人自食惡果不足惜殉了,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,048評(píng)論 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望拟枚。 院中可真熱鬧薪铜,春花似錦、人聲如沸恩溅。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,683評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽脚乡。三九已至蜒滩,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間奶稠,已是汗流浹背俯艰。 一陣腳步聲響...
    開封第一講書人閱讀 32,838評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留锌订,地道東北人竹握。 一個(gè)月前我還...
    沈念sama閱讀 47,776評(píng)論 2 369
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像辆飘,于是被迫代替她去往敵國(guó)和親啦辐。 傳聞我的和親對(duì)象是個(gè)殘疾皇子谓传,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,652評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容