什么是天空盒
- 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 uniforms
gl.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.0
gl.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 uniforms
gl.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 matrix
const 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 translation
viewMatrix[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
// 绘制skybox
if (!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);
// 绘制skybox
if (!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.0
gl.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 matrix
const 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 translation
const 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 uniforms
gl.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 uniforms
gl.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