什么是天空盒

  • 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.
  • 天空盒是一个使用纹理贴图构建的盒子,人在其中朝任何一个方向看去,其纹理彷佛天空一样包裹着视野。

制作天空盒的问题以及如何解决

首先,前面制作环境光贴图和立方体贴图的时候,我们的方式是使用物体的空间坐标,通过投影计算获取纹理坐标,然后进行渲染但是在实现天空盒如果按上述方式,则会存在一些问题。

问题描述以及解决方式

  • 参考下面的图,我们可以看见:
  1. 问题:我们希望天空盒不会超出我们的视界之外,以避免某些位置出现空缺,但是这会导致一些物体被天空盒遮挡
  2. 常见的解决方式是:先绘制天空盒并关闭深度测试,但这会带来性能的损耗,避免不必要的绘制;
  3. 我们的解决方式:
  • 将天空盒的gl_Position固定在[-1, 1]区间,从而可以确保覆盖整个屏幕
  • 将天空盒的gl_Positionz设定为1,从而确保不影响后面的深度测试
  • 通过vp逆矩阵,求得gl_Position的模型坐标,并获取对应天空盒纹理

开始制作天空盒

效果图

开肝

下面我们开始在环境光贴图代码的基础上,进行修改

  1. 修改顶点坐标信息以及顶点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);
}
  1. 注释setNormals(gl, getNormals())
  2. 修改顶点着色器
// 传递进来的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;}'
  1. 计算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);
}
  1. 修改片元着色器
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));' +'}'
  1. 设定深度测试函数
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)相关推荐

  1. 【程序员面试系列】手把手教你如何面试,你要的我都有(工作项目篇)

    作者:Dimple Solgan:当你的才华还无法撑起你的野心时候,那应该静下心来好好学习 前面两篇文章的总结,我们学会了面试前简历的准备.技术知识准备和算法题准备.不知道你是否看完了呢,如果没看完的 ...

  2. matlab 条形图误差线,数据可视化系列:手把手教你绘制带误差线的条形图

    原标题:数据可视化系列:手把手教你绘制带误差线的条形图 条形图可以用于展示数据不同分类下的均值.中位数.标准差和置信区间等,Excel可以实现,但对于带误差线的条形图而言,还是比较麻烦的.R语言的基础 ...

  3. 【面试系列】手把手教你如何面试,你要的我都有(简历篇)

    连续好几周,在一些渠道看到关于年底优化的故事,很多小伙伴要么自己中招,要么眼睁睁看着身边同事中招,充满焦虑.亦或者有些小伙伴本身就有被优化的打算,也趁此抓住机会重新寻找,为后面更好的发展做准备. 且不 ...

  4. 机器学习系列之手把手教你实现一个 naiveBayes

    https://www.ibm.com/developerworks/cn/analytics/library/machine-learning-hands-on3-naivebayes/index. ...

  5. 机器学习系列之手把手教你实现一个分类回归树

    https://www.ibm.com/developerworks/cn/analytics/library/machine-learning-hands-on5-cart-tree/index.h ...

  6. 机器学习系列之手把手教你实现一个决策树

    https://www.ibm.com/developerworks/cn/analytics/library/machine-learning-hands-on4-decision-tree/ind ...

  7. 【gateway系列】手把手教你gateway整合nacos注册中心

    目录 准备 Gateway服务 Member服务 运行 往期相关推荐: 网关路由规则和nacos配置中心实战: 一步步带你学习gateway路由规则实践 nacos整合配置中心 准备 准备引入相关依赖 ...

  8. 手把手系列之七——手把手教你做清凉的椰汁红豆糕

    中午在外面吃了辣的菜,下午一定要弄点清凉的甜品中和一下.回家已经挺累的了,不想多动,随便做个椰汁红豆糕,实在是好吃又简单. 材料:红豆25克.粟粉50克.椰浆100ML.牛奶100ML.清水200ML ...

  9. 我的AI论文有救了系列!手把手教你写人工智能论文!

    最近我一个AI硕士碰表示,导师放养,不知道怎么解决论文问题? 我相信被这问题困扰的,不止一个. 我觉得先抓主要矛盾: 从选题→看文献找创新点→行文→查重→交稿→答辩这个流程来看,90%同学都是卡在选题 ...

最新文章

  1. 【蓝桥java】递归基础之39级台阶
  2. SaaS创业型企业如何打破销售瓶颈?
  3. 每日一问一周汇总:第2期
  4. android 桌面循环滚动字幕,循环滚动字幕
  5. 计算机网络考试目的是什么,计算机网络基本原理考试大纲 -、课程的性质及其设置的目的与要求.doc...
  6. oracle 时间集合,oracle 日期函数集合(集中版本)第2/2页
  7. python颜色列表代码seaborn_在Python中Seaborn – 根据色调名称更改条形颜色
  8. Python:Python程序设计思维、计算生态、用户体验、程序设计模式
  9. PYTHON 笔记:函数的参数(关键字参数,默认参数,可变长参数,可变长的关键字参数)
  10. js作为参数,并且返回值;js的回调模式 callback
  11. 程序员应该做的事(转自CSDN)
  12. shell初学之nginx(域名)
  13. mysql数据库p_PbootCMS Sqlite数据库转Mysql数据库教程
  14. 阿里ACP考试题(只供参考)
  15. DP动态规划思想讲解
  16. 实例079RTF文件的保存
  17. 快速搜索Wox工具之Everything Service没有运行报错,解决办法!
  18. 计算机表格 求差,Excel表格中求差函数公式怎么用
  19. 扔掉代码,程序员月薪达到了10k+
  20. 家庭局域网网站服务器,1000元打造家庭局域网

热门文章

  1. html表单下拉菜单_HTML表单:下拉菜单
  2. matlab对excel数据进行排序求和
  3. matlab .txt转.mat,.csv转.mat
  4. 【LaTeX 教程】06. LaTeX 插入表格
  5. Sublime Text 编译运行Java
  6. Navicat Premium for mac 破解
  7. 微信壁纸小程序源码 自动采集小米壁纸
  8. EU GMP附录一与关键区域空气微生物取样方案及相关法规标准解读
  9. 自己做的html怎么在别人电脑上看,如何在自己的电脑查看别人电脑的ip地址?教你,马上成为电脑高手...
  10. vscode中文高亮