教程
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-幀緩存
OpenGLES進(jìn)階教程5-Tutorial09-碰碰車
這一次的是性能優(yōu)化。
概要
渲染的優(yōu)化不是僅僅提高渲染的速度,超過(guò)60Hz的渲染速度沒(méi)有任何意義,用戶永遠(yuǎn)看不到這些信息。同時(shí)在考慮用電消耗的情況下,30Hz的刷新率能延長(zhǎng)電池的使用時(shí)間。
以下的渲染優(yōu)化策略總是管用的:
- 減少I/O
- 渲染更少的幾何對(duì)象
- 減少內(nèi)存訪問(wèn)
效果展示

核心思路
通過(guò)減少渲染的幾何對(duì)象,在不影響顯示效果的前提下,盡可能減少需要繪制的圖元。
在一個(gè)場(chǎng)景中,很多物體是處于平截體外部,這些物體是用戶永遠(yuǎn)看不到的對(duì)象。

具體細(xì)節(jié)
a.測(cè)試點(diǎn)是否在平截體內(nèi)
計(jì)算眼睛到當(dāng)前測(cè)試點(diǎn)的向量,提取這個(gè)向量關(guān)于平截體X、Y、Z軸的分量,分別進(jìn)行判斷。

- 1、計(jì)算眼睛到當(dāng)前測(cè)試點(diǎn)的向量。
// eye到point的向量
const GLKVector3 eyeToPoint = GLKVector3Subtract(frustumPtr->eyePosition, point);
- 2、測(cè)試Z軸分量,這個(gè)分量要在區(qū)間[nearDistance, farDistance]。如果是,繼續(xù)步驟3。
// z軸分量
const GLfloat pointZComponent = GLKVector3DotProduct(eyeToPoint, frustumPtr->zUnitVector);
if(pointZComponent > frustumPtr->farDistance || pointZComponent < frustumPtr->nearDistance)
{
result = AGLKFrustumOut;
}
- 3、Y軸分量要小于被測(cè)點(diǎn)所在的平截體高度,高度的可以通過(guò)Z軸分量*視角/2的正切值計(jì)算。如果在區(qū)間內(nèi),繼續(xù)步驟4。
// y軸分量
const GLfloat pointYComponent =
GLKVector3DotProduct(eyeToPoint,
frustumPtr->yUnitVector);
const GLfloat frustumHeightAtZ = pointZComponent * frustumPtr->tangentOfHalfFieldOfView;
if(pointYComponent > frustumHeightAtZ || pointYComponent < -frustumHeightAtZ)
{
result = AGLKFrustumOut;
}
- 4、X軸分量要小于被測(cè)點(diǎn)鎖在的平截體的寬度,寬度可以通過(guò)平截體高度值 * 寬高比。
//X軸分量
const GLfloat pointXComponent =
GLKVector3DotProduct(eyeToPoint,
frustumPtr->xUnitVector);
const GLfloat frustumWidthAtZ = frustumHeightAtZ *
frustumPtr->aspectRatio;
if(pointXComponent > frustumWidthAtZ ||
pointXComponent < -frustumWidthAtZ)
{
result = AGLKFrustumOut;
}
b.判斷球體是否在平截體內(nèi)
測(cè)試球體會(huì)測(cè)試點(diǎn)更復(fù)雜,同樣是對(duì)比X/Y/Z軸分量,在判斷的范圍加上半徑的距離。
但是,考慮下面的情況

按照上面的判斷,球體是在平截體之外,但是實(shí)際上是相交的。
解決方案
把半徑乘以特定的因子。
如下圖,考慮球體被外切情況,得出相應(yīng)的放大因子。

- 1、Y軸因子
sphereFactorY = 1.0f/cosf(halfFieldOfViewRad);
- 2、X軸因子
const GLfloat angleX = atanf(frustumPtr->tangentOfHalfFieldOfView * aspectRatio);
frustumPtr->sphereFactorX = 1.0f/cosf(angleX);
擴(kuò)展
場(chǎng)景圖(scene graph)輔助剔除
用樹形數(shù)據(jù)結(jié)的幾何對(duì)象層次組織。樹形結(jié)構(gòu)有一個(gè)根元素。根元素是子元素的父,子元素可能是其他元素的父。參考Cocoa的視圖層次結(jié)構(gòu),2DUIView實(shí)例的場(chǎng)景圖。同樣的概念也使用與3D對(duì)象的層次結(jié)構(gòu)。如果父元素在平截體外部,根據(jù)定義所有它的子元素也在平截體外部,沒(méi)有必要再單獨(dú)測(cè)試每個(gè)子元素。
關(guān)鍵詞:Ochre 八叉樹。減少緩存復(fù)制
為GPU提供一個(gè)頂點(diǎn)屬性緩存后,用CPU處理另一個(gè)。在所有渲染指令發(fā)送完后,通過(guò)glBindBuffer()函數(shù)來(lái)切換緩存。(蘋果公司官網(wǎng)有例子,OpenGLESApplicationDesign.html)減少狀態(tài)變換
OpenGL ES上下文存儲(chǔ)了大量的用于控制渲染運(yùn)算的信息。信息緩存可能在CPU控制的內(nèi)存,也可能在GPU的寄存器。
調(diào)用glEnable(GL_DEPTH_TEST)多次會(huì)浪費(fèi)時(shí)間更新上下文的狀態(tài),即使值是相同的。OES
OES擴(kuò)展是OpenGL ES標(biāo)準(zhǔn)的維護(hù)者,提出的一個(gè)非標(biāo)準(zhǔn)的擴(kuò)展。
思考
為什么FPS會(huì)在20FPS和30FPS之間擺動(dòng)?
繪制 和 顯示 并不一樣。
通過(guò)CADisplayLink(hardware generated),繪制的速率可能是60FPS。
如果繪制的時(shí)間超過(guò)1/60s,理論上幀率最多為30FPS。
想象一條1s的線段,分隔成60小段,每個(gè)小段的起點(diǎn)都可以作為繪制的起點(diǎn)。
如果繪制的時(shí)間超過(guò)1/60s,那么繪制的終點(diǎn)會(huì)延伸到第二個(gè)小段。
這樣,一條1s的線段,最多有30個(gè)繪制的時(shí)間段。
Since CADisplayLink is hardware generated the only thing you can do with it is divide the time, that's what the frameInterval is there to do.
frameInterval = 1 gets you 60 fps
frameInterval = 2 gets you 30 fps
frameInterval = 3 gets you 20 fps
I use a lot frameInterval = 5 for menus for example, it still gives me 12fps (about the minimum for reasonable simple animation) and the battery consumption reduces drastically.
你能得到FPS,但是它不代表真正的性能,每幀持續(xù)時(shí)間是一個(gè)更佳選擇。FPS不能線性評(píng)判性能表現(xiàn)。
For example, if your FPS goes from 30 to 20, then that's a pretty significant decrease in frame performance time (33 ms to 50 ms)
However if your FPS goes from 2000 to 400, that's a miniscule difference in real time (only 2ms difference).
最后,即使你自己通過(guò)自定義線程(不采用CADisplayLink),把繪制時(shí)間的空缺填補(bǔ),實(shí)際上繪制的速率并不會(huì)變快。
總結(jié)
主要講解的是數(shù)學(xué)部分的知識(shí),OpenGL ES的部分沒(méi)有引入新的技術(shù)點(diǎn)。
工作原因,以后更新會(huì)慢一些。能看到這里,你也是喜歡技術(shù)的,謝謝支持。來(lái)一波關(guān)注和喜歡如何 -> 我會(huì)加油更新。
附上源碼