OpenGL 4.0 GLSL 用立方体贴图和 环境贴图 模拟反射效果

我们可以用纹理代表物体周围的环境,然后把纹理贴到物体上,实现映射周围的场景目的,这项技术也被乘坐环境贴图(EnvironmentMapping)。环境贴图通常来模拟反射或者折射效果。

最常见的环境贴图(Environment Mapping)是立方体cube map

立方体贴图(cube map texture)通常由六个分开的图像组成,在设置环绕方式时采用GL_CLAMP_TO_EDGE,这样可以避免在cubeedge中避免边框颜色。

 

一个反射效果如下图:

OpenGL 4.0 GLSL 用立方体贴图和 环境贴图 模拟反射效果_第1张图片


vertex  shader

#version 430

layout (location = 0) in vec3 VertexPosition;
layout (location = 1) in vec3 VertexNormal;
layout (location = 2) in vec2 VertexTexCoord;

out vec3 ReflectDir;

uniform bool DrawSkyBox;
uniform vec3 WorldCameraPosition;

uniform mat4 ModelViewMatrix;
uniform mat4 ModelMatrix;
uniform mat3 NormalMatrix;
uniform mat4 ProjectionMatrix;
uniform mat4 MVP;

void main()
{
    if( DrawSkyBox ) {
        ReflectDir = VertexPosition;
    } else {
        vec3 worldPos = vec3( ModelMatrix * vec4(VertexPosition,1.0) );
        vec3 worldNorm = vec3(ModelMatrix * vec4(VertexNormal, 0.0));
        vec3 worldView = normalize( WorldCameraPosition - worldPos );

        ReflectDir = reflect(-worldView, worldNorm );
    }

    gl_Position = MVP * vec4(VertexPosition,1.0);
}

片元shader

#version 430

in vec3 ReflectDir;

layout(binding=0) uniform samplerCube CubeMapTex;

uniform bool DrawSkyBox;
uniform float ReflectFactor;
uniform vec4 MaterialColor;

layout( location = 0 ) out vec4 FragColor;

void main() {
    // Access the cube map texture
    vec4 cubeMapColor = texture(CubeMapTex, ReflectDir);

    if( DrawSkyBox )
        FragColor = cubeMapColor;
    else
        FragColor = mix( MaterialColor, cubeMapColor, ReflectFactor);
}


在顶点shader中,主要计算反射方向(ReflectDir),然后在片元shader中通过ReflectDir来获取cubemap。

在片元shader中如果不绘制天空盒(DrawSkyBox是false),则我们可以在世界坐标系中计算viewer的反射方向。即通过  reflect(-worldView, worldNorm ) 来计算ReflectDir。

之所以在世界坐标系中计算是因为:如果在相机坐标系的话,当相机移动了反射方向也不会改变。

 

在vertexshader中,我们转化position到世界坐标系中存储到worldPos,我们用同样方法转换法线normal。注意:用  ModelMatrix转化normal时其第四个分量一定是0(避免ModelMatrix转换分量的影响),还有,模型矩阵一定不能包含任何非均匀缩放元素(anynon-uniform scaling component)。否则,法线的转换不正确。见:http://blog.csdn.net/zhuyingqingfen/article/details/19335427

在fragmentshader将会用ReflectDir访问立方体贴图。可以想象成这样:一束光线从viewer开始,射到物体表面然后反射,最终射到cubemap上(最终的片元颜色就是射到cubemap上那点的颜色 值);

 

如果我们渲染skybox(DrawSkyBoxis true),那么我们用顶点位置作为反射方向。为什么?,因为当天空盒被渲染的时候,我们想要天空盒上的位置(对应于cubemap上的位置,天空盒确实是cubemap 渲染出来的)。在片元shader中,ReflectDir将会用作访问cubemap 的纹理坐标。因此,如果我们想访问cubemap上的位置(对应于中心在原点的一个cube的位置),我们需要这个指向这个位置的向量,我们需要的这个向量就是这个点减去原点位置(0,0,0),因而我们只需要这个点的位置。

 

天空盒经常被渲染成viewer在天空盒的中心位置,天空盒会随着viewer移动(所以,viewer总会在天空盒的中心位置)

 

 

在这篇教程中有两点需要注意:

1.      渲染的物体(如茶壶)只会反射环境贴图的场景,而不会反射任何场景中其他的物体。如果想反射其他物体,我们需要产生一个环境贴图(通过把相机放到物体的位置上,然后让相机朝向各个坐标抽方向(正负),渲染六次),然后用这个环境贴图反射到物体上。当然如果场景中的任何一个物体发生移动,我们需要重新生成环境贴图。(这在实时交互程序中一般不可取)。

2.      在上面的shader中,我们计算的反射方向是取的cube map上的点作为反射方向(DrawSkyBox为true即认为物体(上面的茶壶)在原点,cube map 上的一点减去原点即为反射方向,但实际上是相机在原点而不是茶壶),这就意味着我们忽略了物体的位置,认为环境贴图在无穷远处。

你可能感兴趣的:(OpenGL 4.0 GLSL 用立方体贴图和 环境贴图 模拟反射效果)