學(xué)習(xí)OpenGL ES之渲染到紋理

本系列所有文章目錄

獲取示例代碼


本文將介紹渲染到紋理技術(shù)遥诉。之前的例子都是將3D物體渲染到屏幕上须床,在iOS中GLKView為我們做好了渲染到屏幕的所有準(zhǔn)備工作铐料,我們只需要調(diào)用Open GL ES的繪制方法就可以輕松的渲染到屏幕。那么我們接下來了解一下GLKView為我們做了哪些準(zhǔn)備工作豺旬。

FrameBuffer

FrameBuffer是OpenGL ES中重要基礎(chǔ)組件之一余赢,經(jīng)常被縮寫成FBO(FrameBufferObject),它用來承載GPU計算出來的數(shù)據(jù)哈垢,包括顏色(Color),深度(Depth)扛拨,遮罩(Stencil)耘分。FrameBuffer包括3個緩沖區(qū),顏色緩沖區(qū),深度緩沖區(qū)求泰,遮罩緩沖區(qū)央渣,每個緩沖區(qū)就是一塊內(nèi)存,存儲著對應(yīng)的像素數(shù)據(jù)渴频。比如顏色緩沖區(qū)芽丹,一般像素格式都是RGBA,一共4個字節(jié)卜朗,如果是一個大小1024乘以1024的FrameBuffer拔第,那么顏色緩沖區(qū)所占的內(nèi)存就是1024x1024x4個字節(jié)。深度和遮罩緩沖區(qū)也有自己的格式场钉。GLKView默認(rèn)為我們創(chuàng)建了一個FrameBuffer蚊俺,并且綁定了剛才說的3個緩沖區(qū)到這個FrameBuffer上。我們所有繪制的操作逛万,最終都被寫入到這個FrameBuffer的緩沖區(qū)中泳猬。這個FrameBuffer里的顏色緩沖區(qū)的數(shù)據(jù)最終會被呈現(xiàn)在GLKView上。

手動創(chuàng)建FrameBuffer

既然GLKView可以幫我們創(chuàng)建FrameBuffer宇植,那么我們自己是不是也可以手動創(chuàng)建FrameBuffer呢得封?自然是可以的。創(chuàng)建FrameBuffer分為下面幾個步驟指郁。

  • 創(chuàng)建FrameBuffer對象忙上,并綁定到GL_FRAMEBUFFER上,等待后續(xù)處理坡氯。
glGenFramebuffers(1, &framebuffer);
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
  • 生成顏色緩沖區(qū)并附加到FrameBuffer上晨横。這里我們使用一個紋理對象作為顏色緩沖區(qū),這意味著所有繪制到FrameBuffer的顏色數(shù)據(jù)都會存儲到framebufferColorTexture中箫柳。更酷的是我們可以把這個紋理framebufferColorTexture當(dāng)做貼圖使用手形,比如用作漫反射貼圖(diffuseMap)。
glGenTextures(1, &framebufferColorTexture);
glBindTexture(GL_TEXTURE_2D, framebufferColorTexture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, framebufferSize.width, framebufferSize.height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, framebufferColorTexture, 0);
  • 生成深度緩沖區(qū)并附加到framebuffer上悯恍。這里我們同樣使用了紋理對象作為緩沖區(qū)库糠。
glGenTextures(1, &framebufferDepthTexture);
glBindTexture(GL_TEXTURE_2D, framebufferDepthTexture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, framebufferSize.width, framebufferSize.height, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, NULL);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT
                       , GL_TEXTURE_2D, framebufferDepthTexture, 0);

本例使用的是OpenGL ES2 API,如果使用OpenGL ES3涮毫,GL_DEPTH_COMPONENT需要修改成GL_DEPTH_COMPONENT_OES瞬欧。glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT_OES, framebufferSize.width, framebufferSize.height, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, NULL);,修改第一個罢防,第二個不需要修改艘虎。

如果你不需要使用紋理作為深度緩沖區(qū),可以使用下面的寫法替代咒吐。創(chuàng)建一個RenderBuffer野建,而不是Texture属划。

GLuint depthBufferID;
glGenRenderbuffers(1, &depthBufferID);
glBindRenderbuffer(GL_RENDERBUFFER, depthBufferID);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, framebufferSize.width, framebufferSize.height);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depthBufferID);

本文不會使用遮罩(Stencil)緩沖區(qū),所以這里不做詳細(xì)介紹候生。

  • 檢查FrameBuffer的創(chuàng)建狀態(tài)同眯。
GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
if (status != GL_FRAMEBUFFER_COMPLETE) {
    // framebuffer生成失敗
}

如果狀態(tài)不是GL_FRAMEBUFFER_COMPLETE,就說明創(chuàng)建有問題唯鸭⌒胛希可以根據(jù)status排查問題。

使用FrameBuffer

接下來我們就要在創(chuàng)建好的FrameBuffer上繪制物體了目溉。步驟很簡單明肮,綁定FrameBuffer,設(shè)置Viewport停做,繪制晤愧。

glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
glViewport(0, 0, self.framebufferSize.width, self.framebufferSize.height);
glClearColor(0.8, 0.8, 0.8, 1);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
self.projectionMatrix = GLKMatrix4MakePerspective(GLKMathDegreesToRadians(90), self.framebufferSize.width / self.framebufferSize.height, 0.1, 1000.0);
self.cameraMatrix = GLKMatrix4MakeLookAt(0, 1, sin(self.elapsedTime) * 5.0 + 9.0, 0, 0, 0, 0, 1, 0);
[self drawObjects];

Viewport是繪制區(qū)域的大小,我們把Viewport設(shè)置為FrameBuffer的尺寸蛉腌,這樣繪制區(qū)域就可以撐滿整個FrameBuffer了官份。我還設(shè)置了新的clearColor,新的投影矩陣烙丛,觀察矩陣舅巷,這樣就可以在FrameBuffer中呈現(xiàn)出不一樣的繪制效果。[self drawObjects];里簡單的封裝了之前3D物體的繪制河咽。

- (void)drawObjects {
    [self.objects enumerateObjectsUsingBlock:^(GLObject *obj, NSUInteger idx, BOOL *stop) {
        [obj.context active];
        [obj.context setUniform1f:@"elapsedTime" value:(GLfloat)self.elapsedTime];
        [obj.context setUniformMatrix4fv:@"projectionMatrix" value:self.projectionMatrix];
        [obj.context setUniformMatrix4fv:@"cameraMatrix" value:self.cameraMatrix];
        [obj.context setUniform3fv:@"eyePosition" value:self.eyePosition];
        [obj.context setUniform3fv:@"light.position" value:self.light.position];
        [obj.context setUniform3fv:@"light.color" value:self.light.color];
        [obj.context setUniform1f:@"light.indensity" value:self.light.indensity];
        [obj.context setUniform1f:@"light.ambientIndensity" value:self.light.ambientIndensity];
        [obj.context setUniform3fv:@"material.diffuseColor" value:self.material.diffuseColor];
        [obj.context setUniform3fv:@"material.ambientColor" value:self.material.ambientColor];
        [obj.context setUniform3fv:@"material.specularColor" value:self.material.specularColor];
        [obj.context setUniform1f:@"material.smoothness" value:self.material.smoothness];
        
        [obj.context setUniform1i:@"useNormalMap" value:self.useNormalMap];
        
        [obj draw:obj.context];
    }];
}

使用FrameBuffer的顏色緩沖區(qū)紋理

經(jīng)過上面的步驟钠右,我們成功的在FrameBuffer上進行了繪制。此時顏色緩沖區(qū)紋理已經(jīng)存儲了剛剛的繪制結(jié)果忘蟹。接下來我們將這個紋理顯示在一個平面上飒房。Plane類繪制了一個1x1面朝z軸正方向的平面,并且接受一個diffuseMap作為漫反射貼圖媚值。我為它單獨寫了一個非常簡單的Fragment Shader (frag_framebuffer_plane.glsl)狠毯,沒有光照處理,直接顯示diffuseMap的像素褥芒。

precision highp float;

varying vec2 fragUV;

uniform sampler2D diffuseMap;

void main(void) {
    gl_FragColor = texture2D(diffuseMap, fragUV);
}

下面的代碼將繪制3D物體和一個用來顯示FrameBuffer顏色緩沖區(qū)紋理的平面嚼松。

[view bindDrawable];
glClearColor(0.7, 0.7, 0.7, 1);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
float aspect = self.view.frame.size.width / self.view.frame.size.height;
self.projectionMatrix = GLKMatrix4MakePerspective(GLKMathDegreesToRadians(90), aspect, 0.1, 1000.0);
self.cameraMatrix = GLKMatrix4MakeLookAt(0, 1, 6.5, 0, 0, 0, 0, 1, 0);
[self drawObjects];
[self drawPlane];

[view bindDrawable];將GLKView生成的FrameBuffer綁定到GL_FRAMEBUFFER,并設(shè)置好Viewport锰扶,這樣后面繪制的內(nèi)容將呈現(xiàn)在GLKView上献酗。接著重新設(shè)置投影矩陣和觀察矩陣。繪制3D物體[self drawObjects];坷牛。繪制平面[self drawPlane];罕偎。值得一提的是,繪制平面時京闰,我使用了正交投影矩陣颜及,這樣平面就不會有透視效果痴怨,這正是我想要的。

- (void)drawPlane {
    [self.displayFramebufferPlane.context active];
    [self.displayFramebufferPlane.context setUniformMatrix4fv:@"projectionMatrix" value:self.planeProjectionMatrix];
    [self.displayFramebufferPlane.context setUniformMatrix4fv:@"cameraMatrix" value:GLKMatrix4Identity];
    [self.displayFramebufferPlane draw:self.displayFramebufferPlane.context];
}

planeProjectionMatrix就是平面的正交投影矩陣器予,在createPlane中被設(shè)置,Plane使用了新的GLContext捐迫,由vertex.glslfrag_framebuffer_plane.glsl組成乾翔。framebufferColorTexture顏色緩沖區(qū)紋理在Plane初始化時被傳遞進去。這里的紋理對象使用的GLuint類型的施戴,相當(dāng)于GLKTextureInfo的name屬性反浓,是OpenGL ES原始的紋理對象。

- (void)createPlane {
    NSString *vertexShaderPath = [[NSBundle mainBundle] pathForResource:@"vertex" ofType:@".glsl"];
    NSString *fragmentShaderPath = [[NSBundle mainBundle] pathForResource:@"frag_framebuffer_plane" ofType:@".glsl"];
    GLContext *displayFramebufferPlaneContext = [GLContext contextWithVertexShaderPath:vertexShaderPath fragmentShaderPath:fragmentShaderPath];
    self.displayFramebufferPlane = [[Plane alloc] initWithGLContext:displayFramebufferPlaneContext texture:framebufferColorTexture];
    self.displayFramebufferPlane.modelMatrix = GLKMatrix4Identity;
    self.planeProjectionMatrix = GLKMatrix4MakeOrtho(-2.5, 0.5, -4.5, 0.5, -100, 100);
}

至于Plane的實現(xiàn)代碼很簡單赞哗,讀者可以自行clone代碼查看雷则。

最終效果


右上角顯示的是使用了顏色緩沖區(qū)紋理作為漫反射貼圖的平面。

渲染到紋理能做什么肪笋?

最直接的用法就是同時顯示兩個視角的觀察結(jié)果月劈,比如賽車游戲能在右上角同時查看車后的情況。還有就是鏡面效果藤乙,水面效果猜揪,深度緩沖區(qū)貼圖可以做陰影效果(顏色緩沖區(qū)也能做,不過效果還是深度緩沖區(qū)好)坛梁,所以理解好渲染到紋理對后面的特效制作還是很有幫助的而姐。

寫在最后

OpenGL ES進階篇到此就結(jié)束啦,希望通過這部分的文章划咐,大家能夠?qū)penGL ES有更深的理解拴念。下一部分高級篇會以介紹一些OpenGL ES特效的原理和實現(xiàn)為主,比如陰影效果褐缠,紋理投影效果政鼠,水面效果,粒子效果送丰,物理引擎等等缔俄,敬請期待。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末器躏,一起剝皮案震驚了整個濱河市俐载,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌登失,老刑警劉巖遏佣,帶你破解...
    沈念sama閱讀 207,113評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異揽浙,居然都是意外死亡状婶,警方通過查閱死者的電腦和手機意敛,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評論 2 381
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來膛虫,“玉大人草姻,你說我怎么就攤上這事∩缘叮” “怎么了撩独?”我有些...
    開封第一講書人閱讀 153,340評論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長账月。 經(jīng)常有香客問我综膀,道長,這世上最難降的妖魔是什么局齿? 我笑而不...
    開封第一講書人閱讀 55,449評論 1 279
  • 正文 為了忘掉前任剧劝,我火速辦了婚禮,結(jié)果婚禮上抓歼,老公的妹妹穿的比我還像新娘讥此。我一直安慰自己,他們只是感情好锭部,可當(dāng)我...
    茶點故事閱讀 64,445評論 5 374
  • 文/花漫 我一把揭開白布暂论。 她就那樣靜靜地躺著,像睡著了一般拌禾。 火紅的嫁衣襯著肌膚如雪取胎。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,166評論 1 284
  • 那天湃窍,我揣著相機與錄音闻蛀,去河邊找鬼。 笑死您市,一個胖子當(dāng)著我的面吹牛觉痛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播茵休,決...
    沈念sama閱讀 38,442評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼薪棒,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了榕莺?” 一聲冷哼從身側(cè)響起俐芯,我...
    開封第一講書人閱讀 37,105評論 0 261
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎钉鸯,沒想到半個月后吧史,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,601評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,066評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了祭饭。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,161評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡紊馏,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤灵再,帶...
    沈念sama閱讀 33,792評論 4 323
  • 正文 年R本政府宣布,位于F島的核電站亿笤,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏栋猖。R本人自食惡果不足惜净薛,卻給世界環(huán)境...
    茶點故事閱讀 39,351評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望蒲拉。 院中可真熱鬧肃拜,春花似錦、人聲如沸雌团。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,352評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽锦援。三九已至猛蔽,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間灵寺,已是汗流浹背曼库。 一陣腳步聲響...
    開封第一講書人閱讀 31,584評論 1 261
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留略板,地道東北人毁枯。 一個月前我還...
    沈念sama閱讀 45,618評論 2 355
  • 正文 我出身青樓,卻偏偏與公主長得像叮称,于是被迫代替她去往敵國和親种玛。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,916評論 2 344

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