獲取示例代碼
本文將為大家介紹如何使用CubeMap制作反射效果,反射效果可以讓材質(zhì)具備光滑的質(zhì)感,比如汽車的烤漆,就非常適合使用反射效果。先上2張效果圖。我使用的是Blender內(nèi)置的猴頭模型進(jìn)行的渲染測試。
Cube Map
我們首先來了解一下什么是CubeMap。即使你不了解CubeMap,但你一定知道全景圖。隨便打開一個XX地圖,進(jìn)入全景模式,就可以360度無死角的觀察周邊的環(huán)境了。地圖的全景模式大多使用的是Cube Map的方式,使用上下左右前后各6張圖,分別貼在以觀察者為中心的正方體上,這樣就可以形成一個假的3D環(huán)境了。讀者可以從這個網(wǎng)站下載用于CubeMap的圖片,下載下來的圖片都已經(jīng)標(biāo)記好了應(yīng)該在的位置,使用起來還是很方便的。neg開頭的表示在負(fù)軸上,pos表示在正軸上。比如posx就是x正軸上的面所使用的貼圖。
這個網(wǎng)站還可以預(yù)覽Cube Map的全景效果。點擊WebGL Preview即可。
在Shader中使用Cube Map
Shader提供了表示CubeMap的內(nèi)置類型samplerCube
,samplerCube
和sampler2D
一樣,都是貼圖,不同的是需要使用textureCube
進(jìn)行采樣,采樣的時候需要傳遞規(guī)范化后的三維向量而不是二維的UV。采樣的代碼如下。
textureCube(envMap, reflectVec)
textureCube
會采樣reflectVec
向量在Cube Map上指向的點的像素??梢岳斫鉃榍蠼庀蛄亢蜆?biāo)準(zhǔn)正方體的相交點。例子中我們根據(jù)法向量和視線向量計算用于采樣的reflectVec
反射向量。然后把采樣的顏色用于環(huán)境色的計算。
// 計算環(huán)境光
vec3 ambient = vec3(light.ambientIndensity) * material.ambientColor;
vec3 reflectVec = normalize(reflect(-eyeVector, transformedNormal));
ambient += 0.5 * diffuseStrength * textureCube(envMap, reflectVec).rgb;
Shader很簡單,只有這部分的變化。
準(zhǔn)備Cube Map
接下來我們在OC代碼中為Shader準(zhǔn)備Cube Map,Cube Map需要6張圖,我通過前面說的網(wǎng)站下載了一套Cube Map貼圖,按照posx,negx,posy,negy,posz,negz的順序命名為cube-1,cube-2,....,GLKit生成CubeMap的API需要按照這樣順序?qū)D片傳遞給它,生成CubeMap的代碼如下。
- (void)createCubeTexture {
NSMutableArray *files = [NSMutableArray new];
for (int i = 0; i < 6; ++i) {
NSString *filename = [NSString stringWithFormat:@"cube-%d", i + 1];
NSString *filePath = [[NSBundle mainBundle] pathForResource:filename ofType:@"jpg"];
[files addObject:filePath];
}
NSError *error;
self.cubeTexture = [GLKTextureLoader cubeMapWithContentsOfFiles:files options:nil error:&error];
}
將Cube Map傳遞給Shader
為了將Cube Map傳遞給Shader,我為GLContext
寫了一個新的方法。
- (void)bindCubeTexture:(GLKTextureInfo *)textureInfo to:(GLenum)textureChannel uniformName:(NSString *)uniformName {
glActiveTexture(textureChannel);
glBindTexture(GL_TEXTURE_CUBE_MAP, textureInfo.name);
GLuint textureID = (GLuint)textureChannel - (GLuint)GL_TEXTURE0;
[self setUniform1i:uniformName value:textureID];
}
和2D貼圖主要的不同就是綁定到的target不一樣,這里綁定到了GL_TEXTURE_CUBE_MAP
上,其他的操作和2D貼圖都是一樣的。最后在渲染時調(diào)用這個方法。這里我綁定到了通道3的紋理上。
[obj.context bindCubeTexture:self.cubeTexture to:GL_TEXTURE3 uniformName:@"envMap"];
創(chuàng)建猴頭模型
使用之前編寫的WavefrontOBJ類可以很方便的加載猴頭的模型。
- (void)createMonkey {
UIImage *normalImage = [UIImage imageNamed:@"metal.jpg"];
GLKTextureInfo *normalMap = [GLKTextureLoader textureWithCGImage:normalImage.CGImage options:nil error:nil];
UIImage *diffuseImage = [UIImage imageNamed:@"metal.jpg"];
GLKTextureInfo *diffuseMap = [GLKTextureLoader textureWithCGImage:diffuseImage.CGImage options:nil error:nil];
NSString *objFile = [[NSBundle mainBundle] pathForResource:@"smoothMonkey" ofType:@"obj"];
WavefrontOBJ *sphere = [WavefrontOBJ objWithGLContext:self.glContext objFile:objFile diffuseMap:diffuseMap normalMap:normalMap];
sphere.modelMatrix = GLKMatrix4Identity;
[self.objects addObject:sphere];
}
到此,基于CubeMap的反射效果就完成了,該技術(shù)的重點就是Cube Map,掌握了它,其他都很簡單。大家看完可能會發(fā)現(xiàn)這種反射效果無法反射周邊的幾何體,只能反射指定的CubeMap。如果我想制作一面鏡子,可以反射周邊的物體,該怎么辦呢?這就是我下一篇文章將介紹的內(nèi)容,如何制作實時的鏡面反射效果。