Webgl立方体贴图 与 天空球

立方体贴图 与 天空球

webgl 支持将多个2D纹理组合成一个立方体纹理CubeMap。立方体纹理由 6 个 2D 纹理组成,每个2D 纹理对应立方体的一个面,立方体纹理可以理解为 6 个面都是图像的立方体。

贴图步骤

在使用立方体纹理之前,需要把 6 个面的图像都加载进内存中。

纹理目标(Texture target) 方位
GL_TEXTURE_CUBE_MAP_POSITIVE_X
GL_TEXTURE_CUBE_MAP_NEGATIVE_X
GL_TEXTURE_CUBE_MAP_POSITIVE_Y
GL_TEXTURE_CUBE_MAP_NEGATIVE_Y
GL_TEXTURE_CUBE_MAP_POSITIVE_Z
GL_TEXTURE_CUBE_MAP_NEGATIVE_Z
1
2
3
4
5
6
gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X, 0, gl.RGB, gl.RGB, gl.UNSIGNED_BYTE, document.getElementById('sky0'));
gl.texImage2D(gl.TEXTURE_CUBE_MAP_NEGATIVE_X, 0, gl.RGB, gl.RGB, gl.UNSIGNED_BYTE, document.getElementById('sky1'));
gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_Y, 0, gl.RGB, gl.RGB, gl.UNSIGNED_BYTE, document.getElementById('sky2'));
gl.texImage2D(gl.TEXTURE_CUBE_MAP_NEGATIVE_Y, 0, gl.RGB, gl.RGB, gl.UNSIGNED_BYTE, document.getElementById('sky3'));
gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_Z, 0, gl.RGB, gl.RGB, gl.UNSIGNED_BYTE, document.getElementById('sky4'));
gl.texImage2D(gl.TEXTURE_CUBE_MAP_NEGATIVE_Z, 0, gl.RGB, gl.RGB, gl.UNSIGNED_BYTE, document.getElementById('sky5'));

顶点着色器 和 片元着色器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<script id="vertex-shader-3d" type="x-shader/x-vertex">
precision mediump float;
// 接收顶点坐标 (x, y, z)
attribute vec3 a_Position;
uniform mat4 u_Matrix;

varying vec3 textCoord;
void main(){
gl_Position = u_Matrix * vec4(a_Position,1);
// 将顶点原始坐标赋值给纹理坐标。
textCoord = a_Position;
}
</script>


<script id="fragment-shader-3d" type="x-shader/x-fragment">
precision mediump float;
//接收纹理坐标坐标 (x, y, z)
varying vec3 textCoord;
//samplerCube变量,用来接收立方体纹理。
uniform samplerCube u_Skybox;

void main()
{
gl_FragColor = textureCube(u_Skybox, textCoord);

}
</script>

3D立方体使用纹理盒有一个巨大的好处就是不需要额外指定纹理坐标。只要盒子是被放置在世界坐标系的原点,盒子本身的坐标就可以作为纹理坐标使用,因为在3D世界中位置本身就是一个向量,表示一个方向,我们要的就是这个方向。

天空盒

天空盒的原理就是:把远处的山、天空的图片贴到一个正方体上面,然后把摄像机放到正方体的中间。

img

为了实现天空盒,要对上面立方体贴图的代码进行改造

先对立方体放大 50 倍

1
2
var cubeArray = createCube();
var scaleMatrix = m4.scaling(10, 10, 10);

要将背面剔除方式改成正面剔除

1
2
gl.enable(gl.CULL_FACE);
gl.cullFace(gl.FRONT);

在内部观察时,会发现图像渲染是左右相反的,可以在顶点着色器中,将纹理坐标的X值取反。

GIF 2021-12-1 19-36-56

1
v_TextCoord = vec3(-a_Position.x, a_Position.y, a_Position.z);  

修改后,图像渲染的方向就正常了。

GIF 2021-12-1 19-42-16

完整代码


Webgl立方体贴图 与 天空球
http://example.com/2024/05/23/webgl/Webgl立方体贴图 与 天空球/
Author
John Doe
Posted on
May 23, 2024
Licensed under