美文网首页OpenGLOpenGL+MetalOpenGL ES图形处理
学习OpenGL ES之基于CubeMap的反射效果

学习OpenGL ES之基于CubeMap的反射效果

作者: handyTOOL | 来源:发表于2017-08-08 14:26 被阅读375次

    本系列所有文章目录

    获取示例代码


    本文将为大家介绍如何使用CubeMap制作反射效果,反射效果可以让材质具备光滑的质感,比如汽车的烤漆,就非常适合使用反射效果。先上2张效果图。我使用的是Blender内置的猴头模型进行的渲染测试。


    Cube Map

    我们首先来了解一下什么是CubeMap。即使你不了解CubeMap,但你一定知道全景图。随便打开一个XX地图,进入全景模式,就可以360度无死角的观察周边的环境了。地图的全景模式大多使用的是Cube Map的方式,使用上下左右前后各6张图,分别贴在以观察者为中心的正方体上,这样就可以形成一个假的3D环境了。读者可以从这个网站下载用于CubeMap的图片,下载下来的图片都已经标记好了应该在的位置,使用起来还是很方便的。neg开头的表示在负轴上,pos表示在正轴上。比如posx就是x正轴上的面所使用的贴图。


    这个网站还可以预览Cube Map的全景效果。点击WebGL Preview即可。


    在Shader中使用Cube Map

    Shader提供了表示CubeMap的内置类型samplerCubesamplerCubesampler2D一样,都是贴图,不同的是需要使用textureCube进行采样,采样的时候需要传递规范化后的三维向量而不是二维的UV。采样的代码如下。

    textureCube(envMap, reflectVec)
    

    textureCube会采样reflectVec向量在Cube Map上指向的点的像素。可以理解为求解向量和标准正方体的相交点。例子中我们根据法向量和视线向量计算用于采样的reflectVec反射向量。然后把采样的颜色用于环境色的计算。

    // 计算环境光
    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"];
    

    创建猴头模型

    使用之前编写的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,掌握了它,其他都很简单。大家看完可能会发现这种反射效果无法反射周边的几何体,只能反射指定的CubeMap。如果我想制作一面镜子,可以反射周边的物体,该怎么办呢?这就是我下一篇文章将介绍的内容,如何制作实时的镜面反射效果。

    相关文章

      网友评论

        本文标题:学习OpenGL ES之基于CubeMap的反射效果

        本文链接:https://www.haomeiwen.com/subject/xkktrxtx.html