mirror of
https://github.com/greggman/twgl.js.git
synced 2025-12-08 19:26:07 +00:00
313 lines
7.8 KiB
JavaScript
313 lines
7.8 KiB
JavaScript
'use strict';
|
|
|
|
import * as twgl from '../../dist/7.x/twgl-full.module.js';
|
|
|
|
const onePointVS = `
|
|
uniform mat4 u_worldViewProjection;
|
|
|
|
attribute vec4 a_position;
|
|
attribute vec2 a_texcoord;
|
|
|
|
varying vec4 v_position;
|
|
varying vec2 v_texCoord;
|
|
|
|
void main() {
|
|
v_texCoord = a_texcoord;
|
|
gl_Position = u_worldViewProjection * a_position;
|
|
}
|
|
`;
|
|
|
|
const onePointFS = `
|
|
precision mediump float;
|
|
|
|
varying vec4 v_position;
|
|
varying vec2 v_texCoord;
|
|
|
|
uniform vec4 u_diffuseMult;
|
|
uniform sampler2D u_diffuse;
|
|
|
|
void main() {
|
|
vec4 diffuseColor = texture2D(u_diffuse, v_texCoord) * u_diffuseMult;
|
|
if (diffuseColor.a < 0.1) {
|
|
discard;
|
|
}
|
|
gl_FragColor = diffuseColor;
|
|
}
|
|
`;
|
|
|
|
const envMapVS = `
|
|
uniform mat4 u_viewInverse;
|
|
uniform mat4 u_world;
|
|
uniform mat4 u_worldViewProjection;
|
|
uniform mat4 u_worldInverseTranspose;
|
|
|
|
attribute vec4 a_position;
|
|
attribute vec3 a_normal;
|
|
|
|
varying vec3 v_normal;
|
|
varying vec3 v_surfaceToView;
|
|
|
|
void main() {
|
|
v_normal = (u_worldInverseTranspose * vec4(a_normal, 0)).xyz;
|
|
v_surfaceToView = (u_viewInverse[3] - (u_world * a_position)).xyz;
|
|
gl_Position = u_worldViewProjection * a_position;
|
|
}
|
|
`;
|
|
|
|
const envMapFS = `
|
|
precision mediump float;
|
|
|
|
uniform samplerCube u_texture;
|
|
|
|
varying vec3 v_surfaceToView;
|
|
varying vec3 v_normal;
|
|
|
|
void main() {
|
|
vec3 normal = normalize(v_normal);
|
|
vec3 surfaceToView = normalize(v_surfaceToView);
|
|
vec4 color = textureCube(u_texture, -reflect(surfaceToView, normal));
|
|
gl_FragColor = color;
|
|
}
|
|
`;
|
|
|
|
function main(gl) {
|
|
twgl.setDefaults({attribPrefix: "a_"});
|
|
const m4 = twgl.m4;
|
|
const onePointProgramInfo = twgl.createProgramInfo(gl, [onePointVS, onePointFS]);
|
|
const envMapProgramInfo = twgl.createProgramInfo(gl, [envMapVS, envMapFS]);
|
|
|
|
const shapes = [
|
|
twgl.primitives.createCubeBufferInfo(gl, 2),
|
|
twgl.primitives.createSphereBufferInfo(gl, 1, 24, 12),
|
|
twgl.primitives.createPlaneBufferInfo(gl, 2, 2),
|
|
twgl.primitives.createTruncatedConeBufferInfo(gl, 1, 0, 2, 24, 1),
|
|
];
|
|
|
|
function rand(min, max) {
|
|
if (max === undefined) {
|
|
max = min;
|
|
min = 0;
|
|
}
|
|
return min + Math.random() * (max - min);
|
|
}
|
|
|
|
// Shared values
|
|
const camera = m4.identity();
|
|
const view = m4.identity();
|
|
const viewProjection = m4.identity();
|
|
|
|
const textures = twgl.createTextures(gl, {
|
|
// a power of 2 image
|
|
hftIcon: { src: "../images/hft-icon-16.png", mag: gl.NEAREST },
|
|
// a non-power of 2 image
|
|
clover: { src: "../images/clover.jpg" },
|
|
// a cross origin image
|
|
crossOrigin: {
|
|
min: gl.NEAREST,
|
|
mag: gl.NEAREST,
|
|
src: "https://farm6.staticflickr.com/5795/21506301808_efb27ed699_q_d.jpg",
|
|
crossOrigin: "", // either this or use twgl.setDefaults
|
|
},
|
|
// A cubemap from 6 images
|
|
yokohama: {
|
|
target: gl.TEXTURE_CUBE_MAP,
|
|
src: [
|
|
'../images/yokohama/posx.jpg',
|
|
'../images/yokohama/negx.jpg',
|
|
'../images/yokohama/posy.jpg',
|
|
'../images/yokohama/negy.jpg',
|
|
'../images/yokohama/posz.jpg',
|
|
'../images/yokohama/negz.jpg',
|
|
],
|
|
},
|
|
// A cubemap from 1 image (can be 1x6, 2x3, 3x2, 6x1)
|
|
goldengate: {
|
|
target: gl.TEXTURE_CUBE_MAP,
|
|
src: '../images/goldengate.jpg',
|
|
},
|
|
// A 2x2 pixel texture from a JavaScript array
|
|
checker: {
|
|
mag: gl.NEAREST,
|
|
min: gl.LINEAR,
|
|
src: [
|
|
255, 255, 255, 255,
|
|
192, 192, 192, 255,
|
|
192, 192, 192, 255,
|
|
255, 255, 255, 255,
|
|
],
|
|
},
|
|
// a 1x8 pixel texture from a typed array.
|
|
stripe: {
|
|
mag: gl.NEAREST,
|
|
min: gl.LINEAR,
|
|
format: gl.LUMINANCE,
|
|
src: new Uint8Array([
|
|
255,
|
|
128,
|
|
255,
|
|
128,
|
|
255,
|
|
128,
|
|
255,
|
|
128,
|
|
]),
|
|
width: 1,
|
|
},
|
|
// a cubemap from array
|
|
cubemapFromArray: {
|
|
target: gl.TEXTURE_CUBE_MAP,
|
|
format: gl.RGBA,
|
|
src: [
|
|
0xF0, 0x80, 0x80, 0xFF,
|
|
0x80, 0xE0, 0x80, 0xFF,
|
|
0x80, 0x80, 0xD0, 0xFF,
|
|
0xC0, 0x80, 0x80, 0xFF,
|
|
0x80, 0xB0, 0x80, 0xFF,
|
|
0x80, 0x80, 0x00, 0xFF,
|
|
],
|
|
},
|
|
});
|
|
|
|
// This is soley to make it easy to pick textures at random
|
|
const twoDTextures = [
|
|
textures.checker,
|
|
textures.stripe,
|
|
textures.hftIcon,
|
|
textures.clover,
|
|
textures.crossOrigin,
|
|
];
|
|
|
|
const cubeTextures = [
|
|
textures.yokohama,
|
|
textures.goldengate,
|
|
textures.cubemapFromArray,
|
|
];
|
|
|
|
const objects = [];
|
|
const drawObjects = [];
|
|
const numObjects = 100;
|
|
for (let ii = 0; ii < numObjects; ++ii) {
|
|
let uniforms;
|
|
let programInfo;
|
|
let shape;
|
|
const renderType = rand(0, 2) | 0;
|
|
switch (renderType) {
|
|
case 0: // checker
|
|
shape = shapes[ii % shapes.length];
|
|
programInfo = onePointProgramInfo;
|
|
uniforms = {
|
|
u_diffuseMult: [rand(1), rand(1), rand(1), 1],
|
|
u_diffuse: twoDTextures[rand(0, twoDTextures.length) | 0],
|
|
u_viewInverse: camera,
|
|
u_world: m4.identity(),
|
|
u_worldInverseTranspose: m4.identity(),
|
|
u_worldViewProjection: m4.identity(),
|
|
};
|
|
break;
|
|
case 1: // yokohama
|
|
shape = rand(0, 2) < 1 ? shapes[1] : shapes[3];
|
|
programInfo = envMapProgramInfo;
|
|
uniforms = {
|
|
u_texture: cubeTextures[rand(0, cubeTextures.length) | 0],
|
|
u_viewInverse: camera,
|
|
u_world: m4.identity(),
|
|
u_worldInverseTranspose: m4.identity(),
|
|
u_worldViewProjection: m4.identity(),
|
|
};
|
|
break;
|
|
default:
|
|
throw "wAT!";
|
|
}
|
|
drawObjects.push({
|
|
programInfo: programInfo,
|
|
bufferInfo: shape,
|
|
uniforms: uniforms,
|
|
});
|
|
objects.push({
|
|
translation: [rand(-10, 10), rand(-10, 10), rand(-10, 10)],
|
|
ySpeed: rand(0.1, 0.3),
|
|
zSpeed: rand(0.1, 0.3),
|
|
uniforms: uniforms,
|
|
});
|
|
}
|
|
|
|
const state = {
|
|
clientWidth: 300,
|
|
clientHeight: 150,
|
|
};
|
|
|
|
// We only have one message so just replace the one message listener
|
|
self.onmessage = function(evt) {
|
|
Object.assign(state, evt.data);
|
|
};
|
|
|
|
function render(time) {
|
|
time *= 0.001;
|
|
|
|
const clientWidth = state.clientWidth;
|
|
const clientHeight = state.clientHeight;
|
|
|
|
// Make the canvas match its display size
|
|
if (gl.canvas.width !== clientWidth || gl.canvas.height !== clientHeight) {
|
|
gl.canvas.width = clientWidth;
|
|
gl.canvas.height = clientHeight;
|
|
}
|
|
|
|
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
|
|
|
|
gl.enable(gl.DEPTH_TEST);
|
|
gl.enable(gl.BLEND);
|
|
gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
|
|
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
|
|
|
|
const radius = 20;
|
|
const orbitSpeed = time * 0.1;
|
|
const projection = m4.perspective(30 * Math.PI / 180, clientWidth / clientHeight, 0.5, 100);
|
|
const eye = [Math.cos(orbitSpeed) * radius, 4, Math.sin(orbitSpeed) * radius];
|
|
const target = [0, 0, 0];
|
|
const up = [0, 1, 0];
|
|
|
|
m4.lookAt(eye, target, up, camera);
|
|
m4.inverse(camera, view);
|
|
m4.multiply(projection, view, viewProjection);
|
|
|
|
objects.forEach(function(obj) {
|
|
const uni = obj.uniforms;
|
|
const world = uni.u_world;
|
|
m4.identity(world);
|
|
m4.rotateY(world, time * obj.ySpeed, world);
|
|
m4.rotateZ(world, time * obj.zSpeed, world);
|
|
m4.translate(world, obj.translation, world);
|
|
m4.rotateX(world, time, world);
|
|
m4.transpose(m4.inverse(world, uni.u_worldInverseTranspose), uni.u_worldInverseTranspose);
|
|
m4.multiply(viewProjection, uni.u_world, uni.u_worldViewProjection);
|
|
});
|
|
|
|
twgl.drawObjectList(gl, drawObjects);
|
|
|
|
requestAnimationFrame(render);
|
|
}
|
|
requestAnimationFrame(render);
|
|
}
|
|
|
|
self.onmessage = function(evt) {
|
|
if (!self.requestAnimationFrame) {
|
|
self.postMessage('no requestAnimationFrame in worker');
|
|
return;
|
|
}
|
|
|
|
const canvas = evt.data.canvas;
|
|
if (!canvas) {
|
|
self.postMessage('no canvas in worker');
|
|
return;
|
|
}
|
|
|
|
const gl = canvas.getContext("webgl");
|
|
if (!gl) {
|
|
self.postMessage('no webgl in worker');
|
|
return;
|
|
}
|
|
main(gl);
|
|
};
|
|
|