Cube Map 系列之:手把手教你 实现天空盒(Sky Box)
什么是天空盒

- An skybox is a box with textures on it to look like the sky in all directions or rather to look like what is very far away including the horizon.
- 天空盒是一个使用纹理贴图构建的盒子,人在其中朝任何一个方向看去,其纹理彷佛天空一样包裹着视野。
制作天空盒的问题以及如何解决
首先,前面制作环境光贴图和立方体贴图的时候,我们的方式是使用物体的空间坐标,通过投影计算获取纹理坐标,然后进行渲染,但是在实现天空盒如果按上述方式,则会存在一些问题。
问题描述以及解决方式
- 参考下面的图,我们可以看见:
- 问题:我们希望天空盒不会超出我们的视界之外,以避免某些位置出现空缺,但是这会导致一些物体被天空盒遮挡
- 常见的解决方式是:先绘制天空盒并关闭深度测试,但这会带来性能的损耗,避免不必要的绘制;
- 我们的解决方式:
- 将天空盒的
gl_Position固定在[-1, 1]区间,从而可以确保覆盖整个屏幕 - 将天空盒的
gl_Position的z设定为1,从而确保不影响后面的深度测试 - 通过
vp逆矩阵,求得gl_Position的模型坐标,并获取对应天空盒纹理

开始制作天空盒
效果图

开肝
下面我们开始在环境光贴图代码的基础上,进行修改
- 修改顶点坐标信息以及顶点attribute的绑定方式(将size从3修改为2)
function getGeometry(){return new Float32Array([-1, -1,1, -1,-1, 1,-1, 1,1, -1,1, 1,]);
}// Fill the buffer with the values that define a cube.
function setGeometry(gl, positions) {const positionLocation = gl.getAttribLocation(gl.program, "a_position");const positionBuffer = gl.createBuffer();gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);gl.bufferData(gl.ARRAY_BUFFER, positions, gl.STATIC_DRAW);gl.enableVertexAttribArray(positionLocation);const size = 2;const type = gl.FLOAT;const normalize = false;const stride = 0;const offset = 0;gl.vertexAttribPointer(positionLocation, size, type, normalize, stride, offset);
}
- 注释
setNormals(gl, getNormals()) - 修改顶点着色器
// 传递进来的a_position范围为[-1, 1] 确保覆盖整个屏幕
// 将`gl_Position`的z设定为`1`,从而确保不影响后面的深度测试const V_SHADER_SOURCE = '' +'attribute vec4 a_position;' +'varying vec4 v_position;' +'void main(){' +'v_position = a_position;' +'gl_Position = a_position;' +'gl_Position.z = 1.0;}'
- 计算
viewDirectionProjectionInverse矩阵 并传递
function setVPInverse(gl, time){const skyboxLocation = gl.getUniformLocation(gl.program, "u_skybox");const viewDirectionProjectionInverseLocation =gl.getUniformLocation(gl.program, "u_viewDirectionProjectionInverse");// Set the uniformsgl.uniformMatrix4fv(viewDirectionProjectionInverseLocation,false,getVPInverse(time));gl.uniform1i(skyboxLocation, 0);
}
- 修改片元着色器
const F_SHADER_SOURCE = '' +'precision mediump float;' +'uniform samplerCube u_skybox;' +'uniform mat4 u_viewDirectionProjectionInverse;' +'varying vec4 v_position;' +'void main(){' +'vec4 t = u_viewDirectionProjectionInverse * v_position;' +'gl_FragColor = textureCube(u_skybox, normalize(t.zyx / t.w));' +'}'
- 设定深度测试函数
setVPInverse(gl, time);
// let our quad pass the depth test at 1.0
gl.depthFunc(gl.LEQUAL);
// Draw the geometry.
gl.drawArrays(gl.TRIANGLES, 0, 6 * 6);
完整代码
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>CubeMap</title>
</head>
<body>
<script src="https://webglfundamentals.org/webgl/resources/m4.js"></script>
<script src="https://webglfundamentals.org/webgl/resources/webgl-utils.js"></script>
<canvas id="canvas" style="height: 100%; width:100%"></canvas>
<script>const V_SHADER_SOURCE = '' +'attribute vec4 a_position;' +'varying vec4 v_position;' +'void main(){' +'v_position = a_position;' +'gl_Position = a_position;' +'gl_Position.z = 1.0;}'const F_SHADER_SOURCE = '' +'precision mediump float;' +'uniform samplerCube u_skybox;' +'uniform mat4 u_viewDirectionProjectionInverse;' +'varying vec4 v_position;' +'void main(){' +'vec4 t = u_viewDirectionProjectionInverse * v_position;' +'gl_FragColor = textureCube(u_skybox, normalize(t.zyx / t.w));' +'}'function main(){// Get A WebGL context/** @type {HTMLCanvasElement} */const canvas = document.querySelector("#canvas");const gl = canvas.getContext("webgl");if (!gl) {return;}if (!initShaders(gl, V_SHADER_SOURCE, F_SHADER_SOURCE)){console.log('Failed to initialize shaders.');return;}setGeometry(gl, getGeometry());// setNormals(gl, getNormals())setTexture(gl)function radToDeg(r) {return r * 180 / Math.PI;}function degToRad(d) {return d * Math.PI / 180;}const fieldOfViewRadians = degToRad(60);let modelXRotationRadians = degToRad(0);let modelYRotationRadians = degToRad(0);// Get the starting time.let then = 0;requestAnimationFrame(drawScene);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);setVPInverse(gl, time);// let our quad pass the depth test at 1.0gl.depthFunc(gl.LEQUAL);// Draw the geometry.gl.drawArrays(gl.TRIANGLES, 0, 6 * 6);requestAnimationFrame(drawScene);}function setVPInverse(gl, time){const skyboxLocation = gl.getUniformLocation(gl.program, "u_skybox");const viewDirectionProjectionInverseLocation =gl.getUniformLocation(gl.program, "u_viewDirectionProjectionInverse");// Set the uniformsgl.uniformMatrix4fv(viewDirectionProjectionInverseLocation,false,getVPInverse(time));gl.uniform1i(skyboxLocation, 0);}function getVPInverse(time){time *= 0.001;const deltaTime = time - then;then = time;webglUtils.resizeCanvasToDisplaySize(gl.canvas);modelXRotationRadians += -0.7 * deltaTime;modelYRotationRadians += -0.4 * deltaTime;// Compute the projection matrixconst aspect = gl.canvas.clientWidth / gl.canvas.clientHeight;const projectionMatrix =m4.perspective(fieldOfViewRadians, aspect, 1, 2000);const cameraPosition = [Math.cos(time * .1), 0, Math.sin(time * .1)];const target = [0, 0, 0];const up = [0, 1, 0];// Compute the camera's matrix using look at.const cameraMatrix = m4.lookAt(cameraPosition, target, up);// Make a view matrix from the camera matrix.const viewMatrix = m4.inverse(cameraMatrix);// We only care about direction so remove the translationviewMatrix[12] = 0;viewMatrix[13] = 0;viewMatrix[14] = 0;const viewDirectionProjectionMatrix =m4.multiply(projectionMatrix, viewMatrix);return m4.inverse(viewDirectionProjectionMatrix);}}/*** create a program object and make current* @param gl GL context* @param vShader a vertex shader program (string)* @param fShader a fragment shader program(string)*/function initShaders(gl, vShader, fShader){const program = createProgram(gl, vShader, fShader);if (!program){console.log("Failed to create program");return false;}gl.useProgram(program);gl.program = program;return true;}/*** create a program object and make current* @param gl GL context* @param vShader a vertex shader program (string)* @param fShader a fragment shader program(string)*/function createProgram(gl, vShader, fShader){const vertexShader = loadShader(gl, gl.VERTEX_SHADER, vShader);const fragmentShader = loadShader(gl, gl.FRAGMENT_SHADER, fShader);if (!vertexShader || !fragmentShader){return null;}const program = gl.createProgram();if (!program){return null;}gl.attachShader(program, vertexShader);gl.attachShader(program, fragmentShader);gl.linkProgram(program);const linked = gl.getProgramParameter(program, gl.LINK_STATUS);if (!linked){const error = gl.getProgramInfoLog(program);console.log('Failed to link program: ' + error);gl.deleteProgram(program);gl.deleteShader(vertexShader);gl.deleteShader(fragmentShader);}return program;}/**** @param gl GL context* @param type the type of the shader object to be created* @param source shader program (string)*/function loadShader(gl, type, source){const shader = gl.createShader(type);if (shader == null){console.log('unable to create shader');return null;}gl.shaderSource(shader, source);gl.compileShader(shader);const compiled = gl.getShaderParameter(shader, gl.COMPILE_STATUS);if (!compiled){const error = gl.getShaderInfoLog(shader);console.log('Failed to compile shader: ' + error);gl.deleteShader(shader);return null;}return shader;}function getGeometry(){return new Float32Array([-1, -1,1, -1,-1, 1,-1, 1,1, -1,1, 1,]);}// Fill the buffer with the values that define a cube.function setGeometry(gl, positions) {const positionLocation = gl.getAttribLocation(gl.program, "a_position");const positionBuffer = gl.createBuffer();gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);gl.bufferData(gl.ARRAY_BUFFER, positions, gl.STATIC_DRAW);gl.enableVertexAttribArray(positionLocation);const size = 2;const type = gl.FLOAT;const normalize = false;const stride = 0;const offset = 0;gl.vertexAttribPointer(positionLocation, size, type, normalize, stride, offset);}function setTexture(gl){// Create a texture.const texture = gl.createTexture();gl.bindTexture(gl.TEXTURE_CUBE_MAP, texture);const ctx = document.createElement("canvas").getContext("2d");ctx.canvas.width = 128;ctx.canvas.height = 128;const faceInfos = [{target: gl.TEXTURE_CUBE_MAP_POSITIVE_X,url: 'resources/pos-x.jpg',},{target: gl.TEXTURE_CUBE_MAP_NEGATIVE_X,url: 'resources/neg-x.jpg',},{target: gl.TEXTURE_CUBE_MAP_POSITIVE_Y,url: 'resources/pos-y.jpg',},{target: gl.TEXTURE_CUBE_MAP_NEGATIVE_Y,url: 'resources/neg-y.jpg',},{target: gl.TEXTURE_CUBE_MAP_POSITIVE_Z,url: 'resources/pos-z.jpg',},{target: gl.TEXTURE_CUBE_MAP_NEGATIVE_Z,url: 'resources/neg-z.jpg',},];faceInfos.forEach((faceInfo) => {const {target, url} = faceInfo;// Upload the canvas to the cube map face.const level = 0;const internalFormat = gl.RGBA;const width = 512;const height = 512;const format = gl.RGBA;const type = gl.UNSIGNED_BYTE;gl.texImage2D(target, level, internalFormat, width, height, 0, format, type, null);const image = new Image();image.src = url;image.addEventListener("load", function (){gl.bindTexture(gl.TEXTURE_CUBE_MAP, texture);gl.texImage2D(target, level, internalFormat, format, type, image);gl.generateMipmap(gl.TEXTURE_CUBE_MAP);})})gl.generateMipmap(gl.TEXTURE_CUBE_MAP);gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR);}function setNormals(gl, normal){const normalBuffer = gl.createBuffer();gl.bindBuffer(gl.ARRAY_BUFFER, normalBuffer);gl.bufferData(gl.ARRAY_BUFFER, normal, gl.STATIC_DRAW);//const normalLocation = gl.getAttribLocation(gl.program, "a_normal");gl.enableVertexAttribArray(normalLocation);const size = 3;const type = gl.FLOAT;const normalize = false;const stride = 0;const offset = 0;gl.vertexAttribPointer(normalLocation, size, type, normalize, stride, offset);}function getNormals() {return new Float32Array([0, 0, -1,0, 0, -1,0, 0, -1,0, 0, -1,0, 0, -1,0, 0, -1,0, 0, 1,0, 0, 1,0, 0, 1,0, 0, 1,0, 0, 1,0, 0, 1,0, 1, 0,0, 1, 0,0, 1, 0,0, 1, 0,0, 1, 0,0, 1, 0,0, -1, 0,0, -1, 0,0, -1, 0,0, -1, 0,0, -1, 0,0, -1, 0,-1, 0, 0,-1, 0, 0,-1, 0, 0,-1, 0, 0,-1, 0, 0,-1, 0, 0,1, 0, 0,1, 0, 0,1, 0, 0,1, 0, 0,1, 0, 0,1, 0, 0,]);}main()</script>
</body>
</html>
添加Cube对象以及环境光贴图
- 下面我们按照之前的操作,重新添加Cube对象,并进行环境光贴图
效果图

完整代码
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>CubeMap</title>
</head>
<body>
<script src="https://webglfundamentals.org/webgl/resources/m4.js"></script>
<script src="https://webglfundamentals.org/webgl/resources/webgl-utils.js"></script>
<canvas id="canvas" style="height: 100%; width:100%"></canvas>
<script>const V_SHADER_SOURCE = '' +'attribute vec4 a_position;' +'varying vec4 v_position;' +'void main(){' +'v_position = a_position;' +'gl_Position = a_position;' +'gl_Position.z = 1.0;}'const F_SHADER_SOURCE = '' +'precision mediump float;' +'uniform samplerCube u_skybox;' +'uniform mat4 u_viewDirectionProjectionInverse;' +'varying vec4 v_position;' +'void main(){' +'vec4 t = u_viewDirectionProjectionInverse * v_position;' +'gl_FragColor = textureCube(u_skybox, normalize(t.zyx / t.w));' +'}'const V_Environment_SHADER_SOURCE = '' +'attribute vec4 a_position;' +'attribute vec3 a_normal;' +'uniform mat4 u_projection;' +'uniform mat4 u_view;' +'uniform mat4 u_world;' +'varying vec3 v_worldPosition;' +'varying vec3 v_worldNormal;' +'void main(){' +'gl_Position = u_projection * u_view * u_world * a_position;' +'v_worldPosition = (u_world * a_position).xyz;' +'v_worldNormal = mat3(u_world) * a_normal;' +'}'const F_Environment_SHADER_SOURCE = '' +'precision mediump float;' +'varying vec3 v_worldPosition;' +'varying vec3 v_worldNormal;' +'uniform samplerCube u_texture;' +'uniform vec3 u_worldCameraPosition;' +'void main(){' +'vec3 worldNormal = normalize(v_worldNormal);' +'vec3 eyeToSurfaceDir = normalize(v_worldPosition - u_worldCameraPosition);' +'vec3 direction = reflect(eyeToSurfaceDir, worldNormal);' +' gl_FragColor = textureCube(u_texture, direction);' +'}'function main(){// Get A WebGL context/** @type {HTMLCanvasElement} */const canvas = document.querySelector("#canvas");const gl = canvas.getContext("webgl");if (!gl) {return;}//setTexture(gl)function radToDeg(r) {return r * 180 / Math.PI;}function degToRad(d) {return d * Math.PI / 180;}const fieldOfViewRadians = degToRad(60);// Get the starting time.let then = 0;requestAnimationFrame(drawScene);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);// 绘制cube// 绘制skyboxif (!initShaders(gl, V_Environment_SHADER_SOURCE, F_Environment_SHADER_SOURCE)){console.log('Failed to initialize shaders.');return;}const {projectionMatrix,viewMatrix,worldMatrix,cameraPosition,viewDirectionProjectionInverseMatrix} = getUniforms(time);gl.useProgram(gl.program);setGeometry2(gl, getGeometry2());setNormals(gl, getNormals())setCubeUniform(gl, projectionMatrix, viewMatrix, worldMatrix, cameraPosition);gl.depthFunc(gl.LESS);// Draw the geometry.gl.drawArrays(gl.TRIANGLES, 0, 6 * 6);// 绘制skyboxif (!initShaders(gl, V_SHADER_SOURCE, F_SHADER_SOURCE)){console.log('Failed to initialize shaders.');return;}gl.useProgram(gl.program);setGeometry(gl, getGeometry());setSkyBoxUniform(gl, viewDirectionProjectionInverseMatrix)// let our quad pass the depth test at 1.0gl.depthFunc(gl.LEQUAL);// Draw the geometry.gl.drawArrays(gl.TRIANGLES, 0, 6 * 6);requestAnimationFrame(drawScene);}function getUniforms(time){time *= 0.001;const deltaTime = time - then;then = time;webglUtils.resizeCanvasToDisplaySize(gl.canvas);// Compute the projection matrixconst aspect = gl.canvas.clientWidth / gl.canvas.clientHeight;const projectionMatrix =m4.perspective(fieldOfViewRadians, aspect, 1, 2000);const cameraPosition = [Math.cos(time * .1) * 2, 0, Math.sin(time * .1) * 2];const target = [0, 0, 0];const up = [0, 1, 0];// Compute the camera's matrix using look at.const cameraMatrix = m4.lookAt(cameraPosition, target, up);// Make a view matrix from the camera matrix.const viewMatrix = m4.inverse(cameraMatrix);let worldMatrix = m4.xRotation(time * 0.11);;// We only care about direction so remove the translationconst viewDirectionMatrix = m4.copy(viewMatrix);viewDirectionMatrix[12] = 0;viewDirectionMatrix[13] = 0;viewDirectionMatrix[14] = 0;const viewDirectionProjectionMatrix =m4.multiply(projectionMatrix, viewDirectionMatrix);const viewDirectionProjectionInverseMatrix =m4.inverse(viewDirectionProjectionMatrix);return {projectionMatrix, viewMatrix, worldMatrix, cameraPosition, viewDirectionProjectionInverseMatrix}}function setCubeUniform(gl, projectionMatrix, viewMatrix, worldMatrix, cameraPosition){const projectionLocation = gl.getUniformLocation(gl.program, "u_projection");const viewLocation = gl.getUniformLocation(gl.program, "u_view");const worldLocation = gl.getUniformLocation(gl.program, "u_world");const textureLocation = gl.getUniformLocation(gl.program, "u_texture");const worldCameraPositionLocation = gl.getUniformLocation(gl.program, "u_worldCameraPosition");// Set the uniformsgl.uniformMatrix4fv(projectionLocation, false, projectionMatrix);gl.uniformMatrix4fv(viewLocation, false, viewMatrix);gl.uniformMatrix4fv(worldLocation, false, worldMatrix);gl.uniform3fv(worldCameraPositionLocation, cameraPosition);gl.uniform1i(textureLocation, 0);}function setSkyBoxUniform(gl, viewDirectionProjectionInverseMatrix){const skyboxLocation = gl.getUniformLocation(gl.program, "u_skybox");const viewDirectionProjectionInverseLocation =gl.getUniformLocation(gl.program, "u_viewDirectionProjectionInverse");// Set the uniformsgl.uniformMatrix4fv(viewDirectionProjectionInverseLocation,false,viewDirectionProjectionInverseMatrix);gl.uniform1i(skyboxLocation, 0);}}/*** create a program object and make current* @param gl GL context* @param vShader a vertex shader program (string)* @param fShader a fragment shader program(string)*/function initShaders(gl, vShader, fShader){const program = createProgram(gl, vShader, fShader);if (!program){console.log("Failed to create program");return false;}gl.useProgram(program);gl.program = program;return true;}/*** create a program object and make current* @param gl GL context* @param vShader a vertex shader program (string)* @param fShader a fragment shader program(string)*/function createProgram(gl, vShader, fShader){const vertexShader = loadShader(gl, gl.VERTEX_SHADER, vShader);const fragmentShader = loadShader(gl, gl.FRAGMENT_SHADER, fShader);if (!vertexShader || !fragmentShader){return null;}const program = gl.createProgram();if (!program){return null;}gl.attachShader(program, vertexShader);gl.attachShader(program, fragmentShader);gl.linkProgram(program);const linked = gl.getProgramParameter(program, gl.LINK_STATUS);if (!linked){const error = gl.getProgramInfoLog(program);console.log('Failed to link program: ' + error);gl.deleteProgram(program);gl.deleteShader(vertexShader);gl.deleteShader(fragmentShader);}return program;}/**** @param gl GL context* @param type the type of the shader object to be created* @param source shader program (string)*/function loadShader(gl, type, source){const shader = gl.createShader(type);if (shader == null){console.log('unable to create shader');return null;}gl.shaderSource(shader, source);gl.compileShader(shader);const compiled = gl.getShaderParameter(shader, gl.COMPILE_STATUS);if (!compiled){const error = gl.getShaderInfoLog(shader);console.log('Failed to compile shader: ' + error);gl.deleteShader(shader);return null;}return shader;}function getGeometry(){return new Float32Array([-1, -1,1, -1,-1, 1,-1, 1,1, -1,1, 1,]);}// Fill the buffer with the values that define a cube.function setGeometry(gl, positions) {const positionLocation = gl.getAttribLocation(gl.program, "a_position");const positionBuffer = gl.createBuffer();gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);gl.bufferData(gl.ARRAY_BUFFER, positions, gl.STATIC_DRAW);gl.enableVertexAttribArray(positionLocation);const size = 2;const type = gl.FLOAT;const normalize = false;const stride = 0;const offset = 0;gl.vertexAttribPointer(positionLocation, size, type, normalize, stride, offset);}function setTexture(gl){// Create a texture.const texture = gl.createTexture();gl.bindTexture(gl.TEXTURE_CUBE_MAP, texture);const ctx = document.createElement("canvas").getContext("2d");ctx.canvas.width = 128;ctx.canvas.height = 128;const faceInfos = [{target: gl.TEXTURE_CUBE_MAP_POSITIVE_X,url: 'resources/pos-x.jpg',},{target: gl.TEXTURE_CUBE_MAP_NEGATIVE_X,url: 'resources/neg-x.jpg',},{target: gl.TEXTURE_CUBE_MAP_POSITIVE_Y,url: 'resources/pos-y.jpg',},{target: gl.TEXTURE_CUBE_MAP_NEGATIVE_Y,url: 'resources/neg-y.jpg',},{target: gl.TEXTURE_CUBE_MAP_POSITIVE_Z,url: 'resources/pos-z.jpg',},{target: gl.TEXTURE_CUBE_MAP_NEGATIVE_Z,url: 'resources/neg-z.jpg',},];faceInfos.forEach((faceInfo) => {const {target, url} = faceInfo;// Upload the canvas to the cube map face.const level = 0;const internalFormat = gl.RGBA;const width = 512;const height = 512;const format = gl.RGBA;const type = gl.UNSIGNED_BYTE;gl.texImage2D(target, level, internalFormat, width, height, 0, format, type, null);const image = new Image();image.src = url;image.addEventListener("load", function (){gl.bindTexture(gl.TEXTURE_CUBE_MAP, texture);gl.texImage2D(target, level, internalFormat, format, type, image);gl.generateMipmap(gl.TEXTURE_CUBE_MAP);})})gl.generateMipmap(gl.TEXTURE_CUBE_MAP);gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR);}function setNormals(gl, normal){const normalBuffer = gl.createBuffer();gl.bindBuffer(gl.ARRAY_BUFFER, normalBuffer);gl.bufferData(gl.ARRAY_BUFFER, normal, gl.STATIC_DRAW);//const normalLocation = gl.getAttribLocation(gl.program, "a_normal");gl.enableVertexAttribArray(normalLocation);const size = 3;const type = gl.FLOAT;const normalize = false;const stride = 0;const offset = 0;gl.vertexAttribPointer(normalLocation, size, type, normalize, stride, offset);}function getNormals() {return new Float32Array([0, 0, -1,0, 0, -1,0, 0, -1,0, 0, -1,0, 0, -1,0, 0, -1,0, 0, 1,0, 0, 1,0, 0, 1,0, 0, 1,0, 0, 1,0, 0, 1,0, 1, 0,0, 1, 0,0, 1, 0,0, 1, 0,0, 1, 0,0, 1, 0,0, -1, 0,0, -1, 0,0, -1, 0,0, -1, 0,0, -1, 0,0, -1, 0,-1, 0, 0,-1, 0, 0,-1, 0, 0,-1, 0, 0,-1, 0, 0,-1, 0, 0,1, 0, 0,1, 0, 0,1, 0, 0,1, 0, 0,1, 0, 0,1, 0, 0,]);}function getGeometry2(){return new Float32Array([-0.5, -0.5, -0.5,-0.5, 0.5, -0.5,0.5, -0.5, -0.5,-0.5, 0.5, -0.5,0.5, 0.5, -0.5,0.5, -0.5, -0.5,-0.5, -0.5, 0.5,0.5, -0.5, 0.5,-0.5, 0.5, 0.5,-0.5, 0.5, 0.5,0.5, -0.5, 0.5,0.5, 0.5, 0.5,-0.5, 0.5, -0.5,-0.5, 0.5, 0.5,0.5, 0.5, -0.5,-0.5, 0.5, 0.5,0.5, 0.5, 0.5,0.5, 0.5, -0.5,-0.5, -0.5, -0.5,0.5, -0.5, -0.5,-0.5, -0.5, 0.5,-0.5, -0.5, 0.5,0.5, -0.5, -0.5,0.5, -0.5, 0.5,-0.5, -0.5, -0.5,-0.5, -0.5, 0.5,-0.5, 0.5, -0.5,-0.5, -0.5, 0.5,-0.5, 0.5, 0.5,-0.5, 0.5, -0.5,0.5, -0.5, -0.5,0.5, 0.5, -0.5,0.5, -0.5, 0.5,0.5, -0.5, 0.5,0.5, 0.5, -0.5,0.5, 0.5, 0.5,]);}// Fill the buffer with the values that define a cube.function setGeometry2(gl, positions) {const positionLocation = gl.getAttribLocation(gl.program, "a_position");const positionBuffer = gl.createBuffer();gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);gl.bufferData(gl.ARRAY_BUFFER, positions, gl.STATIC_DRAW);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);}main()</script>
</body>
</html>
参考资料
WebGPUEngine-Wiki
WebGL Cubemaps
WebGL Environment Maps (reflections)
WebGL SkyBox
相关文章:
Cube Map 系列之:手把手教你 实现天空盒(Sky Box)
什么是天空盒 An skybox is a box with textures on it to look like the sky in all directions or rather to look like what is very far away including the horizon.天空盒是一个使用纹理贴图构建的盒子,人在其中朝任何一个方向看去,其纹理彷佛天空…...
腾讯VS百度:在AI上下大赌注
来源:猛兽财经 作者:猛兽财经 腾讯控股(00700)最近已经把基础模型和生成式人工智能应用方面的行业突破视为其业务的新增长机会了,并且正在大力投资人工智能,从而增强其现有产品的竞争力和拓展新的机会,比如腾讯已经把…...
字节原来这么容易进,是面试官放水,还是公司实在是太缺人?
本人211非科班,之前在字节和腾讯实习过,这次其实没抱着什么特别大的希望投递,没想到字节可以再给我一次机会,还是挺开心的。 本来以为有个机会就不错啦!没想到能成功上岸,在这里要特别感谢帮我内推的同学&…...
生死疲劳|因为此书莫言获得诺贝尔奖
📚书名:《生死疲劳》 ✏️作者:莫言 历经六世的生死轮回, 三代人无尽的生死疲劳; 触碰极致的痛苦与快乐, 感受不灭的热情与希望。 🔥虽然本书长达39万字,但阅读过程却是无比的酣畅…...
Linux系统编程总结
day2 vim的三种工作模式 命令模式 vi hello.c zz 保存退出 2.编辑模式 i a o s (有大写)可以写东西 3.末行模式: 文本和末行模式不能直接切换 要切换回命令模式 再到末行模式,w:保存 q:退出 按两次esc回到命令模式 vim的基本…...
javascript基础二:Javscript字符串的常用方法有哪些?
在日常开发中,我们对字符串也是操作蛮多,这里我们来整理下字符串的一下最常用的方法 一、操作方法 字符串常用的操作方法归纳为增、删、改、查 增 这里增的意思并不是说直接增添内容,而是创建字符串的一个副本,再进行操作 除了…...
面了个 Java 实习生,小伙很优秀!
大家好,我是鱼皮,前几天给自己的公司面试了一位 Java 暑期实习生,候选人目前是大三。 整个过程我都录屏了,并且在征得候选人的同意后,把面试过程分享出来。一方面是希望对其他在学编程找工作的小伙伴有一些启发和参考…...
Java -并发(多线程)-Interview面试题收集
1、多线程并发 1)多线程中 synchronized 锁升级的原理是什么? synchronized 锁升级原理:在锁对象的对象头里面有一个 threadid 字段,在第一次访问的时候 threadid 为空,jvm 让其持有偏向锁,并将 threadid…...
HashMap的merge()方法
最近遇到一个需求,需要统计各个会员的正在履行合同的合同租金总计,以此作为制定会员等级的标准。但是之前这个方法其实是有的,只是写的乱七八糟,具体的代码就不太方便放上来,就说说大致的代码思路吧。 原代码思路是先查…...
用 mysql_secure_installation 工具来进行密码重置操作(有效)
mysql_secure_installation 工具用于在 MariaDB 中进行一些安全设置,包括重置 root 用户的密码。您可以按照以下步骤使用该工具来重置 root 用户的密码: 1. 以管理员身份登录到您的系统。 2. 执行以下命令以运行 mysql_secure_installation 工具&#…...
【Scala---02】Scala 类与对象 『 类 | 属性 | 访问权限 | 方法 | 继承 | 伴生对象伴生类』
文章目录 1. 定义类2. 属性3. 访问权限4. 方法4.1 方法 vs 函数4.2 方法重写4.3 方法重载4.4 构造方法(1) 构造器定义(2) 构造器的参数列表(3) 构造器私有化 5. 继承6. 伴生对象 & 伴生类6.1 伴生对象的由来6.2 伴生对象 & 伴生类 7. 后续 1. 定义类 Java文件…...
一文掌握python列表的所有使用方法(零基础学python(一))
列表 Python 中的列表是一种可变的数据类型,它可以存储多个值,并且可以随时添加、删除或修改其中的元素。以下是 Python 列表的基本操作和示例代码: 创建列表 可以使用方括号 [] 来创建一个空列表,也可以在方括号中添加元素来创建一个非空列表。例如: empty_list = [] …...
头歌计算机组成原理实验—运算器设计(6)第6关:5位无符号阵列乘法器设计
第6关:5位无符号阵列乘法器设计 实验目的 帮助学生掌握阵列乘法器的实现原理,能够分析阵列乘法器的性能,能在 Logisim 中绘制阵列乘法器电路。 视频讲解 实验内容 在 Logisim 中打开 alu.circ 文件,在5位阵列乘法器中实现斜向…...
Java的运行原理
在Java中引入了虚拟机的概念,即在机器和编译程序之间加入了一层抽象的虚拟的机器。这台虚拟的机器在任何平台上都提供给编译程序一个的共同的接口。编译程序只需要面向虚拟机,生成虚拟机能够理解的代码,然后由解释器来将虚拟机代码转换为特定…...
在已有VPC中创建EKS集群
1. 美东1 默认配置 创建在master-vcp中节点放在两上Public Subnet上,便于SSH登录维护Attach上默认安全组sg-071f18562f41b5804,打通各种常规的网络访问规则cat << EOF > master-eks-cluster.yaml apiVersion: eksctl.io/v1alpha5 kind: ClusterConfig metadata:name…...
Spring boot 注解@Async不生效 无效 不起作用
今天在做公司项目时,有一个发邮件的需求。所以写了一个发送邮件的方法后来发现发邮件很慢,导致接口响应也很慢。于是我便想到要使用异步调用去处理这个方法。于是我把注解Async 加到了自己service类下的一个发邮件的一个方法,后来发现并没有生…...
如何封装一个js文件?
封装js文件时的注意事项 避免全局变量污染:使用闭包或模块模式封装代码,不将变量暴露在全局作用域。 // 闭包方式 (function () {var a 1;function foo() {// ...} })();// 模块模式 var module (function () {var a 1;function foo() {// ...}return {foo: fo…...
计算卸载-论文05-双层优化(无线充电与卸载)
标题:《A Divide-and-Conquer Bilevel Optimization Algorithm for Jointly Pricing Computing Resources and Energy in Wireless Powered MEC》 期刊:IEEE TRANSACTIONS ON CYBERNETICS,2022 一、理论梳理 问题:相比于移动云…...
RSBBS 报表接口 query跳转 RRI
这里只讲RSBBS的goto query,不讲query里面的替换路径。 报表接口就是从一个query跳转到另一个目的地。从下面能看到,可以跳转到一个BW系统下的query,或者能跳转到ERP系统的一个ABAP report也行,或者可以通过archive link去从quer…...
失业五个月,终于有offer了!但这家公司的风评惨不忍睹,要接吗?
往年,程序员们找工作可以说是不怎么费力的,不少求职者还会比对几家offer,看薪酬、看加不加班、看通勤时间等等等等,最后选择自己最满意的那一家过去。 但是今年,情况确实完全不一样,用网友的话形容就是“往…...
云启出海,智联未来|阿里云网络「企业出海」系列客户沙龙上海站圆满落地
借阿里云中企出海大会的东风,以**「云启出海,智联未来|打造安全可靠的出海云网络引擎」为主题的阿里云企业出海客户沙龙云网络&安全专场于5.28日下午在上海顺利举办,现场吸引了来自携程、小红书、米哈游、哔哩哔哩、波克城市、…...
相机Camera日志实例分析之二:相机Camx【专业模式开启直方图拍照】单帧流程日志详解
【关注我,后续持续新增专题博文,谢谢!!!】 上一篇我们讲了: 这一篇我们开始讲: 目录 一、场景操作步骤 二、日志基础关键字分级如下 三、场景日志如下: 一、场景操作步骤 操作步…...
解决Ubuntu22.04 VMware失败的问题 ubuntu入门之二十八
现象1 打开VMware失败 Ubuntu升级之后打开VMware上报需要安装vmmon和vmnet,点击确认后如下提示 最终上报fail 解决方法 内核升级导致,需要在新内核下重新下载编译安装 查看版本 $ vmware -v VMware Workstation 17.5.1 build-23298084$ lsb_release…...
【Redis技术进阶之路】「原理分析系列开篇」分析客户端和服务端网络诵信交互实现(服务端执行命令请求的过程 - 初始化服务器)
服务端执行命令请求的过程 【专栏简介】【技术大纲】【专栏目标】【目标人群】1. Redis爱好者与社区成员2. 后端开发和系统架构师3. 计算机专业的本科生及研究生 初始化服务器1. 初始化服务器状态结构初始化RedisServer变量 2. 加载相关系统配置和用户配置参数定制化配置参数案…...
HTML 列表、表格、表单
1 列表标签 作用:布局内容排列整齐的区域 列表分类:无序列表、有序列表、定义列表。 例如: 1.1 无序列表 标签:ul 嵌套 li,ul是无序列表,li是列表条目。 注意事项: ul 标签里面只能包裹 li…...
TRS收益互换:跨境资本流动的金融创新工具与系统化解决方案
一、TRS收益互换的本质与业务逻辑 (一)概念解析 TRS(Total Return Swap)收益互换是一种金融衍生工具,指交易双方约定在未来一定期限内,基于特定资产或指数的表现进行现金流交换的协议。其核心特征包括&am…...
C++中string流知识详解和示例
一、概览与类体系 C 提供三种基于内存字符串的流,定义在 <sstream> 中: std::istringstream:输入流,从已有字符串中读取并解析。std::ostringstream:输出流,向内部缓冲区写入内容,最终取…...
pikachu靶场通关笔记22-1 SQL注入05-1-insert注入(报错法)
目录 一、SQL注入 二、insert注入 三、报错型注入 四、updatexml函数 五、源码审计 六、insert渗透实战 1、渗透准备 2、获取数据库名database 3、获取表名table 4、获取列名column 5、获取字段 本系列为通过《pikachu靶场通关笔记》的SQL注入关卡(共10关࿰…...
Map相关知识
数据结构 二叉树 二叉树,顾名思义,每个节点最多有两个“叉”,也就是两个子节点,分别是左子 节点和右子节点。不过,二叉树并不要求每个节点都有两个子节点,有的节点只 有左子节点,有的节点只有…...
Angular微前端架构:Module Federation + ngx-build-plus (Webpack)
以下是一个完整的 Angular 微前端示例,其中使用的是 Module Federation 和 npx-build-plus 实现了主应用(Shell)与子应用(Remote)的集成。 🛠️ 项目结构 angular-mf/ ├── shell-app/ # 主应用&…...
