學(xué)習(xí)OpenGL ES之基于CubeMap的反射效果

本系列所有文章目錄

獲取示例代碼


本文將為大家介紹如何使用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,samplerCubesampler2D一樣,都是貼圖,不同的是需要使用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)容,如何制作實時的鏡面反射效果。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

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