獲取示例代碼
本文作為OpenGL ES高級篇的開篇,將為大家介紹如何把一張紋理貼圖投影到復(fù)雜的幾何體上难审。下面是例子運(yùn)行的結(jié)果提针。
下面是未開啟紋理投影的效果圖。
這張是用來投影的紋理圖封断。
涉及到的重要知識點(diǎn)
- 基本幾何體的渲染
- 法線貼圖
- 紋理坐標(biāo)
- 投影矩陣
前兩個知識點(diǎn)是為了渲染場景,例子渲染的場景很簡單,使用3個來自obj文件的正方體,加上4張貼圖纳令,只要看過前面的文章,可以很容易的理解代碼克胳。紋理坐標(biāo)和投影矩陣是實(shí)現(xiàn)這個效果的關(guān)鍵平绩。
基本原理
這個效果和真實(shí)生活中的投影儀很像。假設(shè)我們把投影儀當(dāng)做一個攝像機(jī)漠另,并且設(shè)置好投影矩陣捏雌,如下圖所示。那么投影儀就具備了MVP中的VP兩個矩陣了笆搓。
3D物體的頂點(diǎn)經(jīng)過投影儀的VP和自身的M變換后就會變成投影儀空間的坐標(biāo)性湿。因?yàn)樗薪?jīng)過投影變換的點(diǎn)如果在可視范圍內(nèi),它的x满败,y都會落在-1和1之間肤频,所以我們可以將投影儀空間的坐標(biāo)當(dāng)做UV來使用,UV的范圍是01算墨,-11的范圍只要加1再乘以0.5就可以很方便的變換成0~1的范圍宵荒。這樣我們就可以根據(jù)頂點(diǎn)在投影儀空間的坐標(biāo)來獲取UV了。
Fragment Shader
本文使用的Fragment Shader在fragment.glsl
文件中净嘀。首先我們添加投影的紋理貼圖和投影矩陣报咳。
// projectors
uniform mat4 projectorMatrix;
uniform sampler2D projectorMap;
uniform bool useProjector;
useProjector
主要用作開關(guān)投影效果。projectorMatrix
由投影儀的投影矩陣和觀察矩陣相乘而來挖藏。
接著根據(jù)投影儀空間的坐標(biāo)計算UV暑刃。
vec4 positionInProjectorSpace = projectorMatrix * modelMatrix * vec4(fragPosition, 1.0);
positionInProjectorSpace /= positionInProjectorSpace.w;
vec2 projectorUV = (positionInProjectorSpace.xy + 1.0) * 0.5;
positionInProjectorSpace /= positionInProjectorSpace.w;
之所以要除以w
,是因?yàn)槿绻闶褂玫氖峭敢曂队熬仃嚹っ撸敲从锌赡躻不為1岩臣,這時候需要xyzw
都除以w
保證xy
范圍的正確性。
最后我們判斷UV的坐標(biāo)來決定是否接受到了投影宵膨。UV超出0~1范圍的說明不在投影儀的可視范圍內(nèi)架谎,所以不需要進(jìn)行投影處理。這里我使用4:6的比例簡單的混合了兩種顏色柄驻。你也可以把它當(dāng)做光照顏色來處理狐树,使用它計算diffuse和specular的顏色。
if (projectorUV.x >= 0.0 && projectorUV.x <=1.0 && projectorUV.y >= 0.0 && projectorUV.y <=1.0) {
projectorColor = texture2D(projectorMap, projectorUV);
gl_FragColor = vec4(finalColor * 0.4 + projectorColor.rgb * 0.6, 1.0);
} else {
gl_FragColor = vec4(finalColor, 1.0);
}
上面便是Fragment Shader中和投影效果相關(guān)的代碼鸿脓。
生成并刷新投影儀的矩陣
我們回到OC代碼來生成投影儀需要的VP矩陣抑钟。每次渲染時我都會刷新projectorMatrix
涯曲,讓它圍繞y軸旋轉(zhuǎn)。只要讓camera的up向量圍繞y軸旋轉(zhuǎn)就可以做到這一點(diǎn)在塔。隨著projectorMatrix
的旋轉(zhuǎn)幻件,投影的紋理也會隨之旋轉(zhuǎn)。
@property (assign, nonatomic) GLKMatrix4 projectorMatrix;
...
// update projector matrix
GLKMatrix4 projectorProjectionMatrix = GLKMatrix4MakeOrtho(-2, 2, -2, 2, -100, 100);
GLKMatrix4 projectorCameraMatrix = GLKMatrix4MakeLookAt(0, 4, 0, 0, 0, 0, cos(self.elapsedTime), 0, sin(self.elapsedTime));
self.projectorMatrix = GLKMatrix4Multiply(projectorProjectionMatrix, projectorCameraMatrix);
然后把projectorMatrix
賦值給uniform就可以了蛔溃。
[obj.context setUniformMatrix4fv:@"projectorMatrix" value: self.projectorMatrix];
本例中我使用的是正交投影绰沥,如果你想嘗試透視投影,可以自行修改測試贺待。
投影紋理
針對投影紋理需要處理的事情很簡單徽曲,初始化&設(shè)置uniform。
UIImage *projectorImage = [UIImage imageNamed:@"squarepants.jpg"];
self.projectorMap = [GLKTextureLoader textureWithCGImage:projectorImage.CGImage options:nil error:nil];
...
[obj.context bindTexture:self.projectorMap to:GL_TEXTURE2 uniformName:@"projectorMap"];
紋理投影效果有什么用麸塞?
比如槍擊游戲里的彈痕就可以使用這種技術(shù)來實(shí)現(xiàn)秃臣,將彈痕貼圖投影到任何你打中的地方。而且投影貼圖完全可以是動態(tài)的哪工,加上法線貼圖等等奥此。你可以發(fā)揮自己的想象,做出一些更有意思的效果雁比。