"use strict";
/* global document, twgl, requestAnimationFrame */
const vs = `
uniform mat4 u_model;
uniform mat4 u_view;
uniform mat4 u_projection;
uniform vec3 u_camera;
attribute vec4 position;
attribute vec3 normal;
varying vec3 v_normal;
varying vec3 v_eyeToSurface;
void main() {
vec4 world = u_model * position;
gl_Position = u_projection * u_view * world;
v_eyeToSurface = world.xyz - u_camera;
v_normal = (u_model * vec4(normal, 0)).xyz;
}
`;
const fs = `
precision mediump float;
varying vec3 v_eyeToSurface;
varying vec3 v_normal;
uniform sampler2D u_textures[6];
void cubemap(vec3 r, out float texId, out vec2 st) {
vec3 uvw;
vec3 absr = abs(r);
if (absr.x > absr.y && absr.x > absr.z) {
// x major
float negx = step(r.x, 0.0);
uvw = vec3(r.zy, absr.x) * vec3(mix(-1.0, 1.0, negx), -1, 1);
texId = negx;
} else if (absr.y > absr.z) {
// y major
float negy = step(r.y, 0.0);
uvw = vec3(r.xz, absr.y) * vec3(1.0, mix(1.0, -1.0, negy), 1.0);
texId = 2.0 + negy;
} else {
// z major
float negz = step(r.z, 0.0);
uvw = vec3(r.xy, absr.z) * vec3(mix(1.0, -1.0, negz), -1, 1);
texId = 4.0 + negz;
}
st = vec2(uvw.xy / uvw.z + 1.) * .5;
}
vec4 texCubemap(vec3 uvw) {
float texId;
vec2 st;
cubemap(uvw, texId, st);
vec4 color = vec4(0);
for (int i = 0; i < 6; ++i) {
vec4 side = texture2D(u_textures[i], st);
float select = step(float(i) - 0.5, texId) *
step(texId, float(i) + .5);
color = mix(color, side, select);
}
return color;
}
void main() {
vec3 normal = normalize(v_normal);
vec3 eyeToSurface = normalize(v_eyeToSurface);
gl_FragColor = texCubemap(reflect(eyeToSurface, normal));
}
`;
const m4 = twgl.m4;
const gl = document.getElementById("c").getContext("webgl");
// compile shaders, look up locations
const programInfo = twgl.createProgramInfo(gl, [vs, fs]);
// create buffers
const models = [
twgl.primitives.createSphereBufferInfo(gl, 1, 12, 8),
twgl.primitives.createCubeBufferInfo(gl, 1.5),
twgl.primitives.createTorusBufferInfo(gl, .7, .5, 12, 8),
];
const textures = twgl.createTextures(gl, {
posx: { minMag: gl.LINEAR, wrap: gl.CLAMP_TO_EDGE, crossOrigin: "anonymous", src:'https://twgljs.org/examples/images/yokohama/posx.jpg', },
negx: { minMag: gl.LINEAR, wrap: gl.CLAMP_TO_EDGE, crossOrigin: "anonymous", src:'https://twgljs.org/examples/images/yokohama/negx.jpg', },
posy: { minMag: gl.LINEAR, wrap: gl.CLAMP_TO_EDGE, crossOrigin: "anonymous", src:'https://twgljs.org/examples/images/yokohama/posy.jpg', },
negy: { minMag: gl.LINEAR, wrap: gl.CLAMP_TO_EDGE, crossOrigin: "anonymous", src:'https://twgljs.org/examples/images/yokohama/negy.jpg', },
posz: { minMag: gl.LINEAR, wrap: gl.CLAMP_TO_EDGE, crossOrigin: "anonymous", src:'https://twgljs.org/examples/images/yokohama/posz.jpg', },
negz: { minMag: gl.LINEAR, wrap: gl.CLAMP_TO_EDGE, crossOrigin: "anonymous", src:'https://twgljs.org/examples/images/yokohama/negz.jpg', },
});
const uniforms = {
u_textures: [
textures.posx,
textures.negx,
textures.posy,
textures.negy,
textures.posz,
textures.negz,
],
};
function render(time) {
time *= 0.001;
twgl.resizeCanvasToDisplaySize(gl.canvas);
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
gl.enable(gl.DEPTH_TEST);
gl.enable(gl.CULL_FACE);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
const fov = 30 * Math.PI / 180;
const aspect = gl.canvas.clientWidth / gl.canvas.clientHeight;
const zNear = 0.5;
const zFar = 20;
const projection = m4.perspective(fov, aspect, zNear, zFar);
const eye = [
Math.sin(time) * 7,
Math.sin(time * .5) * 3,
Math.cos(time) * 7,
];
const target = [0, 0, 0];
const up = [0, 1, 0];
const camera = m4.lookAt(eye, target, up);
const view = m4.inverse(camera);
uniforms.u_camera = eye;
uniforms.u_projection = projection;
uniforms.u_view = view;
gl.useProgram(programInfo.program);
models.forEach((bufferInfo, ndx) => {
let u = ndx / (models.length - 1) * 2 - 1;
let model = m4.translation([u * (models.length - 1), 0, 0]);
model = m4.rotateY(model, time * (ndx + 1) * 0.7);
uniforms.u_model = m4.rotateX(model, time * (ndx + 1) * 0.2);
twgl.setBuffersAndAttributes(gl, programInfo, bufferInfo);
twgl.setUniforms(programInfo, uniforms);
gl.drawElements(gl.TRIANGLES, bufferInfo.numElements, gl.UNSIGNED_SHORT, 0);
});
requestAnimationFrame(render);
}
requestAnimationFrame(render);
body {
margin: 0;
}
canvas {
display: block;
width: 100vw;
height: 100vh;
}
<canvas id="c"></canvas>
<script src="https://twgljs.org/dist/4.x/twgl-full.min.js"></script>