Cube Map 系列之:手把手教你 实现 立方体贴图
什么是Cube Map
在开始立方体贴图之前,我们先简单了解下cube map。
cube map 包含了六个纹理,分别表示了立方体的六个面;
相较二维的纹理使用坐标uv来获取纹理信息,这里我们需要使用三维的方向向量来获取纹理信息(一些地方称为法线 normal,但我认为方向向量更合理)。
Cube Map可以用于:
- 正方体表面贴图
- 用于环境贴图(反射贴图),模拟镜面反射结果
- 天空盒
什么是立方体贴图
故名思意,将cube map的六个纹理分别贴到立方体的六个面上,就算立方体贴图。效果如下:
- 纹理图

- 立方体贴图

我们下面从两个部分来进行讲解
- 使用文本生成对应的纹理图
- 使用cubemap将对应的纹理分别贴到立方体的六个面上
生成立方体纹理
- 纹理图效果如下

- 关键代码(使用canvas生成对应的图片)
function generateFace(ctx, faceColor, textColor, text){const {width, height} = ctx.canvas;ctx.fillStyle = faceColor;ctx.fillRect(0, 0, width, height);ctx.font = `${width * 0.7}px sans-serif`;ctx.textAlign = 'center';ctx.textBaseline = "middle";ctx.fillStyle = textColor;ctx.fillText(text, width / 2, height / 2);
}
- 关键代码(组织数据,获得图片,并使用html显示)
const ctx = document.createElement("canvas").getContext("2d");ctx.canvas.width = 128;
ctx.canvas.height = 128;const faceInfos = [{ faceColor: '#F00', textColor: '#0FF', text: '+X' },{ faceColor: '#FF0', textColor: '#00F', text: '-X' },{ faceColor: '#0F0', textColor: '#F0F', text: '+Y' },{ faceColor: '#0FF', textColor: '#F00', text: '-Y' },{ faceColor: '#00F', textColor: '#FF0', text: '+Z' },{ faceColor: '#F0F', textColor: '#0F0', text: '-Z' },
];faceInfos.forEach((faceInfo) => {const { faceColor, textColor, text } = faceInfo;generateFace(ctx, faceColor, textColor, text);ctx.canvas.toBlob((blob => {const img = new Image();img.src = URL.createObjectURL(blob);document.body.appendChild(img);}))
})
}
完整代码
CubeMap
将纹理贴到立方体
关键代码说明
1. 创建gl
const canvas = document.querySelector("#canvas");
const gl = canvas.getContext("webgl");
2. 初始化片元着色器和顶点着色器
const V_SHADER_SOURCE = '' +'attribute vec4 a_position;' +'uniform mat4 u_matrix;' +'varying vec3 v_normal;' +'void main(){' +'gl_Position = u_matrix * a_position;' +'v_normal = normalize(a_position.xyz);' +'}'const F_SHADER_SOURCE = '' +'precision mediump float;' +'varying vec3 v_normal;' +'uniform samplerCube u_texture;' +'void main(){' +'gl_FragColor = textureCube(u_texture, normalize(v_normal));' +'}'if (!initShaders(gl, V_SHADER_SOURCE, F_SHADER_SOURCE)){console.log('Failed to initialize shaders.');return;
}
3. 配置attribute信息a_position
// 获取a_position
const positionLocation = gl.getAttribLocation(gl.program, "a_position");// 创建数据buffer 并绑定pisitions数据
const positionBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
gl.bufferData(gl.ARRAY_BUFFER, positions, gl.STATIC_DRAW);// 开启顶点属性 并设置其对应的属性 从而从数据buffer中获取对应的数据
gl.enableVertexAttribArray(positionLocation);
const size = 3;
const type = gl.FLOAT;
const normalize = false;
const stride = 0;
const offset = 0;
gl.vertexAttribPointer(positionLocation, size, type, normalize, stride, offset);
4. 配置uniform信息u_texture
// 创建纹理对象 并绑定到 cube_map
const texture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_CUBE_MAP, texture);// 生成纹理图片 并调用 gl.texImage2D进行绑定
const ctx = document.createElement("canvas").getContext("2d");ctx.canvas.width = 128;
ctx.canvas.height = 128;const faceInfos = [{ target: gl.TEXTURE_CUBE_MAP_POSITIVE_X, faceColor: '#F00', textColor: '#0FF', text: '+X' },{ target: gl.TEXTURE_CUBE_MAP_NEGATIVE_X, faceColor: '#FF0', textColor: '#00F', text: '-X' },{ target: gl.TEXTURE_CUBE_MAP_POSITIVE_Y, faceColor: '#0F0', textColor: '#F0F', text: '+Y' },{ target: gl.TEXTURE_CUBE_MAP_NEGATIVE_Y, faceColor: '#0FF', textColor: '#F00', text: '-Y' },{ target: gl.TEXTURE_CUBE_MAP_POSITIVE_Z, faceColor: '#00F', textColor: '#FF0', text: '+Z' },{ target: gl.TEXTURE_CUBE_MAP_NEGATIVE_Z, faceColor: '#F0F', textColor: '#0F0', text: '-Z' },
];faceInfos.forEach((faceInfo) => {const { target, faceColor, textColor, text } = faceInfo;generateFace(ctx, faceColor, textColor, text);// Upload the canvas to the cube map face.const level = 0;const internalFormat = gl.RGBA;const format = gl.RGBA;const type = gl.UNSIGNED_BYTE;gl.texImage2D(target, level, internalFormat, format, type, ctx.canvas);})// 生成cubemap纹理 并进行传递
gl.generateMipmap(gl.TEXTURE_CUBE_MAP);
gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR);
const textureLocation = gl.getUniformLocation(gl.program, "u_texture");
gl.uniform1i(textureLocation, 0);
5. 动态更新uniform信息u_matrix
// 获取每一帧的时间差
time *= 0.001;
const deltaTime = time - then;
then = time;// 计算沿x y 轴的旋转角度
modelXRotationRadians += -0.7 * deltaTime;
modelYRotationRadians += -0.4 * deltaTime;// 计算投影矩阵
const aspect = gl.canvas.clientWidth / gl.canvas.clientHeight;
const projectionMatrix =m4.perspective(fieldOfViewRadians, aspect, 1, 2000);// 计算相机矩阵
const cameraPosition = [0, 0, 2];
const up = [0, 1, 0];
const target = [0, 0, 0];
const cameraMatrix = m4.lookAt(cameraPosition, target, up);// 获取view矩阵
const viewMatrix = m4.inverse(cameraMatrix);// 计算得到vp矩阵
const viewProjectionMatrix = m4.multiply(projectionMatrix, viewMatrix);// 返回有旋转角度的 vp矩阵
let matrix = m4.xRotate(viewProjectionMatrix, modelXRotationRadians);
return m4.yRotate(matrix, modelYRotationRadians);
6. 开始绘制
const matrixLocation = gl.getUniformLocation(gl.program, "u_matrix");function drawScene(time){gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);gl.enable(gl.CULL_FACE);gl.enable(gl.DEPTH_TEST);gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);gl.useProgram(gl.program);// 动态更新矩阵信息gl.uniformMatrix4fv(matrixLocation, false, updateMatrix(time));// Draw the geometry.gl.drawArrays(gl.TRIANGLES, 0, 6 * 6);requestAnimationFrame(drawScene);
}
效果

完整代码
CubeMap
参考资料
WebGPUEngine-Wiki
WebGL Cubemaps
WebGL Environment Maps (reflections)
WebGL SkyBox
本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!
