Cocos2d-x 立方体纹理与环境映射

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/wlk1229/article/details/84921839

Cocos2d-x 立方体纹理

声明:本文使用的是cocos2d-x-3.17的代码

文章中的提到的测试代码下载地址https://gitee.com/Kyle12/Cocos2dRenderStudy

立方体纹理Cube Texture从名字来看就知道这是一个立方体,初始化一个立方体纹理需要使用六张图片设置立方体的六个面。立方体纹理需要使用3D坐标系定位,六个面分别是X轴正方向、X轴负方向、Y轴正方向、Y轴负方向、Z轴正方向、Z轴负方向。要读取纹理时需要一个向量这个向量从原点出发,与任何一个面的交点则是读取的数据。

 

环境纹理

环境纹理是由一个立方体纹理组成,六个面分别是环境中的六个方向,且每个面的边界与相邻面的边界重合。如下是一个天空环境的纹理,下载地址

这个纹理映射到立方体效果如下:

如果映射到一个球上面,效果如下:

程序完整代码:CubeScene.cpp/CubeScene.h,程序菜单“Test Cube Texture”->“Cube Texture”。

天空盒子

环境纹理的一个重要应用就是天空盒子,其原理也很简单,构建一个方体,将环境纹理映射到立方体六个面,然后把摄像机放在立方体的中心,这样在转动摄像机可看到整个环境。

Cocos2d-x支持立方体纹理和天空盒子,对应的类分别为TextureCube和Skybox。

这里主要讲一下天空盒子类Skybox的实现。

为了提高性能Cocos2d-x中绘制天空盒子并没有绘制立方体,Cocos2d-x只绘制了一个矩形,这个矩形对应了整个绘制窗口。绘制使用的顶点数据和索引数据如下:

Vec3 vexBuf[] ={Vec3(1, -1, -1), Vec3(1, 1, -1), Vec3(-1, 1, -1), Vec3(-1, -1, -1) };
const unsigned char indxBuf[] = {0, 1, 2, 0, 2, 3};

绘制使用的顶点着色器

uniform mat4  u_cameraRot;

attribute vec3 a_position;

varying vec3 v_reflect;

 

void main(void)

{

    //u_cameraRot矩阵会将点旋转到摄像机方向,并根据点投影的视角缩放,并不会平移点

    vec4 reflect =  u_cameraRot * vec4(a_position, 1.0);

    v_reflect = reflect.xyz;

    //深度缓存比较方式为GL_LEQUAL,较小值通过,所以远平面Z=1

    gl_Position = vec4(a_position.xy, 1.0 , 1.0);

}

 

绘制使用的片元着色器

#ifdef GL_ES

varying mediump vec3        v_reflect;

#else

varying vec3        v_reflect;

#endif

uniform samplerCube u_Env;

uniform vec4 u_color;

 

void main(void)

{

    gl_FragColor = textureCube(u_Env, v_reflect) * u_color;

}

绘制使用的顶点数据全部是在Z=-1的平面上,顶点着色器中并没有对点进行世界坐标系变换, Z=-1平面的点会当作远平面绘制。

顶点着色器和片元着色器都比较简单,片元着色器只是简单的用v_reflect从环境纹理中读取数据。v_reflect是由顶点着色器传送到片元着色器中的,在顶点着色器中v_reflect的计算需要用到u_cameraRot矩阵。u_cameraRot矩阵作用有两个,一是旋转点到摄像机方向,二是对点进行一定的缩放,缩放的程度与投影时使用的视角相关。u_cameraRot的赋值代码如下:

//获取世界坐标系下摄像机的旋转,平移,缩放矩阵。

Mat4 cameraModelMat = camera->getNodeToWorldTransform();
Mat4 projectionMat = camera->getProjectionMatrix();
// 忽略摄像机的移动
cameraModelMat.m[12] = cameraModelMat.m[13] = cameraModelMat.m[14] = 0;
// 根据投影矩阵对点进行缩放cameraModelMat.scale(1 / projectionMat.m[0], 1 / projectionMat.m[5], 1.0);

state->setUniformMat4("u_cameraRot"cameraModelMat);

关于缩放

如果不进行旋转和缩放,因为顶点时Z=-1的平面,最终Skybox绘制的是立方体纹理Z轴负方向的纹理,因为窗口本身比例不一定为正方形,直接绘制会因为比例不对应导致图像变形。为了使得图像长宽比例显示正常,需要根据投影视角进行缩放。

 

在透视投影中

projectionMat.m[0]为X轴方向视角的cot值,视角越大值越小

projectionMat.m[5]为Y轴方向视角的cot值,视角越大值越小

因为都是视角越大值越小,所以缩放的时候取反比例。

 

在正交投影中

projectionMat.m[0]/ projectionMat.m[5]为X轴/Y轴方向缩放到[-1,1]之间的缩放系数,这两一般都比较小(一般都是几百分之一),其反比值将会很大,有几百。这么大的缩放倍数完全不适合绘制天空盒子,所以再Cocos2d中正交投影不能用于天空盒子。

以下是天空盒子的示例,程序完整代码: SkyBoxScene.cpp/SkyBoxScene.h,程序菜单“Test Cube Texture”->“Skye Box Test”。

 

环境映射

天空纹理的另外一个应用就是环境映射,例如有一个光滑的不锈钢铁球,其表面是会反射周围的环境。

环境映射的原理如下图:

这里眼镜与顶点方向的向量经过法向量反射,然后用反射向量取环境纹理的值。

眼镜的位置:在程序中我们看到的位置由摄像机决定的,摄像机的位置也就是眼镜的位置。

以下是环境映射的点着色器和片元着色器:

点着色器

uniform vec3 eyeCoord;

attribute vec4 a_position;

attribute vec3 a_normal;

varying vec3 v_reflect;

void main(void)

{

    gl_Position = CC_MVPMatrix * a_position;

    //CC_MVMatrix是模型矩阵,只是将点转换到世界坐标系下

    vec4 positionWorldViewSpace = CC_MVMatrix * a_position;

    vec3 vEyeVertex     = normalize(positionWorldViewSpace.xyz - eyeCoord);

    //必须规范化,因为求反射向量时需要使用规范化坐标。

    vec3 v_normalVector = normalize(CC_NormalMatrix * a_normal);

    v_reflect           = normalize(reflect(vEyeVertex, v_normalVector));

}

 

片元着色器

varying vec3 v_reflect;

uniform samplerCube u_cubeTex;

void main(void)

{

    gl_FragColor = textureCube(u_cubeTex, v_reflect) ;

}

 

以下是环境映射的例子,程序完整代码:CubeMapScene.cpp/CubeMapScene.h,程序菜单“Test Cube Texture”->“Cube Map Test”。

 

当反射面是一个平面时,环境映射可以实现类似镜子的效果,如下图:

 

猜你喜欢

转载自blog.csdn.net/wlk1229/article/details/84921839