mirror of
https://github.com/webgpu/webgpufundamentals.git
synced 2026-05-16 05:41:01 -04:00
242 lines
7.7 KiB
HTML
242 lines
7.7 KiB
HTML
<!DOCTYPE html>
|
|
<html>
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes">
|
|
<title>WebGL Cube Multiple</title>
|
|
<style>
|
|
html, body { margin: 0; height: 100% }
|
|
canvas { width: 100%; height: 100%; display: block; }
|
|
|
|
#fail {
|
|
position: fixed;
|
|
left: 0;
|
|
top: 0;
|
|
width: 100%;
|
|
height: 100%;
|
|
display: flex;
|
|
justify-content: center;
|
|
align-items: center;
|
|
background: red;
|
|
color: white;
|
|
font-weight: bold;
|
|
font-family: monospace;
|
|
font-size: 16pt;
|
|
text-align: center;
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<canvas></canvas>
|
|
<div id="fail" style="display: none">
|
|
<div class="content"></div>
|
|
</div>
|
|
</body>
|
|
<script type="module">
|
|
// see https://webgpufundamentals.org/webgpu/lessons/webgpu-utils.html#wgpu-matrix
|
|
import {vec3, mat4} from '../3rdparty/wgpu-matrix.module.js';
|
|
|
|
function main() {
|
|
const gl = document.querySelector('canvas').getContext('webgl');
|
|
if (!gl) {
|
|
fail('need webgl');
|
|
return;
|
|
}
|
|
|
|
const vSrc = `
|
|
uniform mat4 u_worldViewProjection;
|
|
uniform mat4 u_worldInverseTranspose;
|
|
|
|
attribute vec4 a_position;
|
|
attribute vec3 a_normal;
|
|
attribute vec2 a_texcoord;
|
|
|
|
varying vec2 v_texCoord;
|
|
varying vec3 v_normal;
|
|
|
|
void main() {
|
|
gl_Position = u_worldViewProjection * a_position;
|
|
v_texCoord = a_texcoord;
|
|
v_normal = (u_worldInverseTranspose * vec4(a_normal, 0)).xyz;
|
|
}
|
|
`;
|
|
|
|
const fSrc = `
|
|
precision highp float;
|
|
|
|
varying vec2 v_texCoord;
|
|
varying vec3 v_normal;
|
|
|
|
uniform sampler2D u_diffuse;
|
|
uniform vec3 u_lightDirection;
|
|
|
|
void main() {
|
|
vec4 diffuseColor = texture2D(u_diffuse, v_texCoord);
|
|
vec3 a_normal = normalize(v_normal);
|
|
float l = dot(a_normal, u_lightDirection) * 0.5 + 0.5;
|
|
gl_FragColor = vec4(diffuseColor.rgb * l, diffuseColor.a);
|
|
}
|
|
`;
|
|
|
|
function createBuffer(gl, data, type = gl.ARRAY_BUFFER) {
|
|
const buf = gl.createBuffer();
|
|
gl.bindBuffer(type, buf);
|
|
gl.bufferData(type, data, gl.STATIC_DRAW);
|
|
return buf;
|
|
}
|
|
|
|
const positions = new Float32Array([1, 1, -1, 1, 1, 1, 1, -1, 1, 1, -1, -1, -1, 1, 1, -1, 1, -1, -1, -1, -1, -1, -1, 1, -1, 1, 1, 1, 1, 1, 1, 1, -1, -1, 1, -1, -1, -1, -1, 1, -1, -1, 1, -1, 1, -1, -1, 1, 1, 1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, -1, -1, -1]);
|
|
const normals = new Float32Array([1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 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, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1]);
|
|
const texcoords = new Float32Array([1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1]);
|
|
const indices = new Uint16Array([0, 1, 2, 0, 2, 3, 4, 5, 6, 4, 6, 7, 8, 9, 10, 8, 10, 11, 12, 13, 14, 12, 14, 15, 16, 17, 18, 16, 18, 19, 20, 21, 22, 20, 22, 23]);
|
|
|
|
const positionBuffer = createBuffer(gl, positions);
|
|
const normalBuffer = createBuffer(gl, normals);
|
|
const texcoordBuffer = createBuffer(gl, texcoords);
|
|
const indicesBuffer = createBuffer(gl, indices, gl.ELEMENT_ARRAY_BUFFER);
|
|
|
|
const tex = gl.createTexture();
|
|
gl.bindTexture(gl.TEXTURE_2D, tex);
|
|
gl.texImage2D(
|
|
gl.TEXTURE_2D,
|
|
0, // level
|
|
gl.RGBA,
|
|
2, // width
|
|
2, // height
|
|
0,
|
|
gl.RGBA,
|
|
gl.UNSIGNED_BYTE,
|
|
new Uint8Array([
|
|
255, 255, 128, 255,
|
|
128, 255, 255, 255,
|
|
255, 128, 255, 255,
|
|
255, 128, 128, 255,
|
|
]));
|
|
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
|
|
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
|
|
|
|
function createShader(gl, type, source) {
|
|
const sh = gl.createShader(type);
|
|
gl.shaderSource(sh, source);
|
|
gl.compileShader(sh);
|
|
if (!gl.getShaderParameter(sh, gl.COMPILE_STATUS)) {
|
|
throw new Error(gl.getShaderInfoLog(sh));
|
|
}
|
|
return sh;
|
|
}
|
|
|
|
function createProgram(gl, vs, fs) {
|
|
const prg = gl.createProgram();
|
|
gl.attachShader(prg, vs);
|
|
gl.attachShader(prg, fs);
|
|
gl.linkProgram(prg);
|
|
if (!gl.getProgramParameter(prg, gl.LINK_STATUS)) {
|
|
throw new Error(gl.getProgramInfoLog(prg));
|
|
}
|
|
return prg;
|
|
}
|
|
|
|
const vs = createShader(gl, gl.VERTEX_SHADER, vSrc);
|
|
const fs = createShader(gl, gl.FRAGMENT_SHADER, fSrc);
|
|
|
|
const program = createProgram(gl, vs, fs);
|
|
|
|
const u_lightDirectionLoc = gl.getUniformLocation(program, 'u_lightDirection');
|
|
const u_diffuseLoc = gl.getUniformLocation(program, 'u_diffuse');
|
|
const u_worldInverseTransposeLoc = gl.getUniformLocation(program, 'u_worldInverseTranspose');
|
|
const u_worldViewProjectionLoc = gl.getUniformLocation(program, 'u_worldViewProjection');
|
|
|
|
const positionLoc = gl.getAttribLocation(program, 'a_position');
|
|
const normalLoc = gl.getAttribLocation(program, 'a_normal');
|
|
const texcoordLoc = gl.getAttribLocation(program, 'a_texcoord');
|
|
|
|
const numObjects = 100;
|
|
const objectInfos = [];
|
|
|
|
for (let i = 0; i < numObjects; ++i) {
|
|
const across = Math.sqrt(numObjects) | 0;
|
|
const x = (i % across - (across - 1) / 2) * 3;
|
|
const y = ((i / across | 0) - (across - 1) / 2) * 3;
|
|
|
|
objectInfos.push({
|
|
translation: [x, y, 0],
|
|
});
|
|
}
|
|
|
|
function resizeCanvasToDisplaySize(canvas) {
|
|
const width = canvas.clientWidth;
|
|
const height = canvas.clientHeight;
|
|
const needResize = width !== canvas.width || height !== canvas.height;
|
|
if (needResize) {
|
|
canvas.width = width;
|
|
canvas.height = height;
|
|
}
|
|
return needResize;
|
|
}
|
|
|
|
function render(time) {
|
|
time *= 0.001;
|
|
resizeCanvasToDisplaySize(gl.canvas);
|
|
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
|
|
|
|
gl.enable(gl.DEPTH_TEST);
|
|
gl.enable(gl.CULL_FACE);
|
|
gl.clearColor(0.5, 0.5, 0.5, 1.0);
|
|
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
|
|
|
|
gl.useProgram(program);
|
|
|
|
const projection = mat4.perspective(30 * Math.PI / 180, gl.canvas.clientWidth / gl.canvas.clientHeight, 0.5, 100);
|
|
const eye = [1, 4, -46];
|
|
const target = [0, 0, 0];
|
|
const up = [0, 1, 0];
|
|
|
|
const view = mat4.lookAt(eye, target, up);
|
|
const viewProjection = mat4.multiply(projection, view);
|
|
|
|
gl.uniform3fv(u_lightDirectionLoc, vec3.normalize([1, 8, -10]));
|
|
gl.uniform1i(u_diffuseLoc, 0);
|
|
|
|
gl.activeTexture(gl.TEXTURE0);
|
|
gl.bindTexture(gl.TEXTURE_2D, tex);
|
|
|
|
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
|
|
gl.vertexAttribPointer(positionLoc, 3, gl.FLOAT, false, 0, 0);
|
|
gl.enableVertexAttribArray(positionLoc);
|
|
|
|
gl.bindBuffer(gl.ARRAY_BUFFER, normalBuffer);
|
|
gl.vertexAttribPointer(normalLoc, 3, gl.FLOAT, false, 0, 0);
|
|
gl.enableVertexAttribArray(normalLoc);
|
|
|
|
gl.bindBuffer(gl.ARRAY_BUFFER, texcoordBuffer);
|
|
gl.vertexAttribPointer(texcoordLoc, 2, gl.FLOAT, false, 0, 0);
|
|
gl.enableVertexAttribArray(texcoordLoc);
|
|
|
|
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indicesBuffer);
|
|
|
|
objectInfos.forEach(({translation}, ndx) => {
|
|
const world = mat4.translation(translation);
|
|
mat4.rotateX(world, time * 0.9 + ndx, world);
|
|
mat4.rotateY(world, time + ndx, world);
|
|
|
|
gl.uniformMatrix4fv(u_worldInverseTransposeLoc, false, mat4.transpose(mat4.inverse(world)));
|
|
gl.uniformMatrix4fv(u_worldViewProjectionLoc, false, mat4.multiply(viewProjection, world));
|
|
|
|
gl.drawElements(gl.TRIANGLES, 6 * 6, gl.UNSIGNED_SHORT, 0);
|
|
});
|
|
|
|
requestAnimationFrame(render);
|
|
}
|
|
requestAnimationFrame(render);
|
|
}
|
|
|
|
function fail(msg) {
|
|
const elem = document.querySelector('#fail');
|
|
const contentElem = elem.querySelector('.content');
|
|
elem.style.display = '';
|
|
contentElem.textContent = msg;
|
|
}
|
|
|
|
main();
|
|
</script>
|
|
</html> |