獲取示例代碼
本文將為大家介紹如何使用CubeMap制作反射效果峭沦,反射效果可以讓材質具備光滑的質感,比如汽車的烤漆逃糟,就非常適合使用反射效果吼鱼。先上2張效果圖。我使用的是Blender內置的猴頭模型進行的渲染測試绰咽。
Cube Map
我們首先來了解一下什么是CubeMap菇肃。即使你不了解CubeMap,但你一定知道全景圖取募。隨便打開一個XX地圖琐谤,進入全景模式,就可以360度無死角的觀察周邊的環(huán)境了玩敏。地圖的全景模式大多使用的是Cube Map的方式斗忌,使用上下左右前后各6張圖质礼,分別貼在以觀察者為中心的正方體上,這樣就可以形成一個假的3D環(huán)境了织阳。讀者可以從這個網站下載用于CubeMap的圖片眶蕉,下載下來的圖片都已經標記好了應該在的位置,使用起來還是很方便的唧躲。neg開頭的表示在負軸上造挽,pos表示在正軸上。比如posx就是x正軸上的面所使用的貼圖惊窖。
這個網站還可以預覽Cube Map的全景效果刽宪。點擊WebGL Preview即可。
在Shader中使用Cube Map
Shader提供了表示CubeMap的內置類型samplerCube
界酒,samplerCube
和sampler2D
一樣圣拄,都是貼圖,不同的是需要使用textureCube
進行采樣毁欣,采樣的時候需要傳遞規(guī)范化后的三維向量而不是二維的UV庇谆。采樣的代碼如下。
textureCube(envMap, reflectVec)
textureCube
會采樣reflectVec
向量在Cube Map上指向的點的像素凭疮》苟可以理解為求解向量和標準正方體的相交點。例子中我們根據(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很簡單,只有這部分的變化衰腌。
準備Cube Map
接下來我們在OC代碼中為Shader準備Cube Map新蟆,Cube Map需要6張圖,我通過前面說的網站下載了一套Cube Map貼圖右蕊,按照posx琼稻,negx,posy,negy,posz屏箍,negz的順序命名為cube-1,cube-2嘀掸,....,GLKit生成CubeMap的API需要按照這樣順序將圖片傳遞給它规惰,生成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貼圖都是一樣的堕花。最后在渲染時調用這個方法文狱。這里我綁定到了通道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的反射效果就完成了,該技術的重點就是Cube Map壕曼,掌握了它苏研,其他都很簡單。大家看完可能會發(fā)現(xiàn)這種反射效果無法反射周邊的幾何體腮郊,只能反射指定的CubeMap摹蘑。如果我想制作一面鏡子,可以反射周邊的物體轧飞,該怎么辦呢衅鹿?這就是我下一篇文章將介紹的內容,如何制作實時的鏡面反射效果过咬。