一、原理
1.创建一个立方体贴图,代码见3.1
2.关闭深度写入,先绘制一个盒子,纹理为立方体贴图
gl.depthMask(false);
gl.useProgram(skyboxProgram)
gl.program = skyboxProgram
//绘制天空盒
await drawSkyBox(projMatrix, viewMatrix);
shader:
void main(){
gl_FragColor=textureCube(u_CubeMapTexture, v_Position);
}`;
3.打开深度写入,绘制其他物体
gl.depthMask(true);
gl.useProgram(boxProgram)
gl.program = boxProgram
await drawBox(projMatrix, viewMatrix)
4.移动时不让天空盒移动,即将mvp矩阵的平移分量设为0
mat4 relativeViewMatrix=u_ViewMatrix;
relativeViewMatrix[3].x=0.0;
relativeViewMatrix[3].y=0.0;
relativeViewMatrix[3].z=0.0;
二、效果
##三、 代码 3.1 立方体贴图
* 初始化立方体纹理
* @param {*} srcArr
*/
async function initCubeTexture(srcArr) {
//加载纹理
const imgArr = []
for (let i = 0; i < 6; i++) {
var image = new Image();
image.src = srcArr[i];
image = await reloadImage(image);
imgArr.push(image)
}
const cubeTexture = createCubeMapTexture(imgArr)
//绑定
gl.bindTexture(gl.TEXTURE_CUBE_MAP, cubeTexture);
//赋值
gl.uniform1i(gl.program.cubeMapTexture, 0)
}
/**glEnable(GL_TEXTURE_CUBE_MAP_SEAMLESS);
* 创建立方体纹理
* @param {*图片数组} imgArr
* @returns
*/
function createCubeMapTexture(imgArr) {
//创建纹理
const texture = gl.createTexture()
gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, 0);
//激活0号纹理单元
gl.activeTexture(gl.TEXTURE0)
//绑定立方体纹理
gl.bindTexture(gl.TEXTURE_CUBE_MAP, texture);
//给立方体纹理每个面分配图像
for (let i = 0; i < 6; i++) {
gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X + i,
0,
gl.RGB,
gl.RGB,
gl.UNSIGNED_BYTE,
imgArr[i])
}
//设置纹理采样方式
gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
// gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_WARP_S, gl.CLAMP_TO_EDGE);
// gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_WARP_T, gl.CLAMP_TO_EDGE);
// gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_WARP_R, gl.CLAMP_TO_EDGE);
//设置立方体纹理为空
gl.bindTexture(gl.TEXTURE_CUBE_MAP, null);
return texture
}
3.2.绘制天空盒和箱子
//顶点着色器
var skyBox_VS = /*glsl*/ `
attribute vec4 a_Position;
uniform mat4 u_ModelMatrix;
uniform mat4 u_ViewMatrix;
uniform mat4 u_ProjectMatrix;
varying vec3 v_Position;
void main(){
mat4 relativeViewMatrix=u_ViewMatrix;
relativeViewMatrix[3].x=0.0;
relativeViewMatrix[3].y=0.0;
relativeViewMatrix[3].z=0.0;
gl_Position = u_ProjectMatrix*relativeViewMatrix*u_ModelMatrix * a_Position;
v_Position=a_Position.rgb;
}`;
//片元着色器
var skyBox_FS = /*glsl*/ `
#ifdef GL_ES
precision mediump float;
#endif
uniform samplerCube u_CubeMapTexture;
uniform vec3 u_AmbitionColor;
varying vec3 v_Position;
void main(){
gl_FragColor=textureCube(u_CubeMapTexture, v_Position);
}`;
var v_shader=/*glsl*/`
attribute vec4 a_Position;
attribute vec2 a_Uv;
uniform mat4 u_ModelMatrix;
uniform mat4 u_ViewMatrix;
uniform mat4 u_ProjectMatrix;
varying vec2 v_Uv;
void main(){
gl_Position=u_ProjectMatrix*u_ViewMatrix*u_ModelMatrix*a_Position;
v_Uv=a_Uv;
}
`
var f_shader=/*glsl*/`
#ifdef GL_ES
precision mediump float;
#endif
varying vec2 v_Uv;
uniform sampler2D u_Texture;
void main(){
gl_FragColor=texture2D(u_Texture,v_Uv);
}
`
//声明js需要的相关变量
var canvas = document.getElementById("canvas");
var gl = getWebGLContext(canvas);
var boxProgram;
var skyboxProgram
async function main() {
if (!gl) {
console.log("你的浏览器不支持WebGL");
return;
}
skyboxProgram = createProgram(gl, skyBox_VS, skyBox_FS);
if (!skyboxProgram) {
console.warn("创建程序失败!");
return;
}
boxProgram=createProgram(gl,v_shader,f_shader)
if(!boxProgram){
console.warn("创建立方体程序失败!");
return;
}
//设置透视投影矩阵
var projMatrix = new Matrix4();
projMatrix.setPerspective(30, canvas.width / canvas.height, 0.1, 100);
//设置视角矩阵的相关信息(视点,视线,上方向)
var viewMatrix = new Matrix4();
viewMatrix.setLookAt(0,0,4, 0, 0,0, 0, 1, 0);
//开启隐藏面清除
gl.enable(gl.DEPTH_TEST);
// gl.enable(gl.CULL_FACE)
gl.program = skyboxProgram;
gl.useProgram(skyboxProgram);
//获取内置变量的信息
getVariableLocation();
var n = createCube(gl);
if (n < 0) {
console.log("无法创建缓冲区");
return;
}
await initCubeTexture(
[
"./image/skybox/right.jpg",
"./image/skybox/left.jpg",
"./image/skybox/top.jpg",
"./image/skybox/bottom.jpg",
"./image/skybox/front.jpg",
"./image/skybox/back.jpg",
]
)
gl.useProgram(boxProgram)
gl.program=boxProgram
//获取内置变量的信息
getVariableLocation();
var n = createCube(gl);
if (n < 0) {
console.log("无法创建缓冲区");
return;
}
//初始化纹理
await initTexture(gl, "./image/box.jpg", 0);
document.addEventListener("keydown", (e) => {
if (e.key === "ArrowUp") {
viewMatrix.translate(0,0,1)
} else if (e.key === "ArrowDown") {
viewMatrix.translate(0,0,-1)
} else if (e.key === "ArrowLeft") {
viewMatrix.rotate(-1,0,1,0)
} else if (e.key === "ArrowRight") {
viewMatrix.rotate(1,0,1,0)
}
});
// await draw(projMatrix,viewMatrix)
//设置底色
//根据时间绘制
var tick = async function () {
await draw(projMatrix,viewMatrix)
//重复请求
requestAnimationFrame(tick)
}
tick()
}
async function draw(projMatrix,viewMatrix){
//清空颜色和深度缓冲区
gl.clearColor(0.0, 0.0, 0.0, 1.0);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
gl.depthMask(false);
gl.useProgram(skyboxProgram)
gl.program=skyboxProgram
//绘制三角形
await drawSkyBox(projMatrix,viewMatrix);
gl.depthMask(true);
gl.useProgram(boxProgram)
gl.program=boxProgram
await drawBox(projMatrix,viewMatrix)
}
async function drawSkyBox(projMatrix,viewMatrix) {
const program = gl.program;
//设置视角矩阵的相关信息(视点,视线,上方向)
//将试图矩阵传给u_ViewMatrix变量
gl.uniformMatrix4fv(program.projectMatrix, false, projMatrix.elements);
//设置模型矩阵的相关信息
var modelMatrix = new Matrix4();
gl.uniformMatrix4fv(program.modelMatrix, false, modelMatrix.elements);
gl.uniformMatrix4fv(program.viewMatrix, false, viewMatrix.elements);
//绘制图形
gl.drawElements(gl.TRIANGLES, 36, gl.UNSIGNED_BYTE, 0);
}
async function drawBox(projMatrix,viewMatrix){
const program = gl.program;
//设置模型矩阵的相关信息
var modelMatrix = new Matrix4();
gl.uniformMatrix4fv(program.modelMatrix, false, modelMatrix.elements);
gl.uniformMatrix4fv(program.viewMatrix, false, viewMatrix.elements);
//设置透视投影矩阵
var projMatrix = new Matrix4();
projMatrix.setPerspective(30, canvas.width / canvas.height, 0.1, 100);
//将试图矩阵传给u_ViewMatrix变量
gl.uniformMatrix4fv(program.projectMatrix, false, projMatrix.elements);
//绘制图形
gl.drawElements(gl.TRIANGLES, 36, gl.UNSIGNED_BYTE, 0);
}