twgl.js/examples/transform-feedback.html
Gregg Tavares 67551111b4 Fix CSS
There was a time when 100vh was correct but mobile browsers changed.
2025-10-13 14:54:12 -07:00

366 lines
10 KiB
HTML

<!DOCTYPE html>
<html>
<head>
<meta charset="utf8">
<!--
@license twgl.js Copyright (c) 2015, Gregg Tavares All Rights Reserved.
Available via the MIT license.
see: http://github.com/greggman/twgl.js for details
-->
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes">
<meta property="og:title" content="TWGL.js - transform feedback" />
<meta property="og:type" content="website" />
<meta property="og:image" content="http://twgljs.org/examples/screenshots/transform-feedback.png" />
<meta property="og:description" content="TWGL.js - transform feedback" />
<meta property="og:url" content="http://twgljs.org" />
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:site" content="@greggman">
<meta name="twitter:creator" content="@greggman">
<meta name="twitter:domain" content="twgljs.org">
<meta name="twitter:title" content="TWGL.js - transform feedback">
<meta name="twitter:url" content="http://twgljs.org/examples/transform-feedback.html">
<meta name="twitter:description" content="TWGL.js - transform feedback">
<meta name="twitter:image:src" content="http://twgljs.org/examples/screenshots/transform-feedback.png">
<link href="/resources/images/twgljs-icon.png" rel="shortcut icon" type="image/png">
<title>twgl.js - transform feedback</title>
<style>
html, body {
margin: 0;
font-family: monospace;
height: 100%;
}
canvas {
display: block;
width: 100%;
height: 100%;
}
#b {
position: absolute;
top: 10px;
width: 100%;
text-align: center;
z-index: 2;
}
</style>
</head>
<body>
<canvas id="c"></canvas>
<div id="b"><a href="http://twgljs.org">twgl.js</a> - transform feedback</div>
<div id="no-webgl2" style="display: none;">
<div>Sorry but your browser doesn't appear to support WebGL2</div>
</div>
</body>
<script id="tf-vs" type="nonjs">
#version 300 es
out vec3 a_position;
out vec3 a_normal;
out vec2 a_texcoord;
flat out uint a_color;
uniform float u_vertexCount;
uniform float u_around;
uniform float u_hue;
const float PI = radians(180.);
vec3 hsv2rgb(vec3 c) {
c = vec3(c.x, clamp(c.yz, 0.0, 1.0));
vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);
vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www);
return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y);
}
void main() {
float numQuads = floor(u_vertexCount / 6.);
float down = numQuads / u_around;
float quadId = floor(float(gl_VertexID) / 6.);
float qx = mod(quadId, u_around);
float qy = floor(quadId / u_around);
// 0--1 3
// | / /|
// |/ / |
// 2 4--5
//
// 0 1 0 1 0 1
// 0 0 1 0 1 1
float edgeId = mod(float(gl_VertexID), 6.);
float ux = mod(edgeId, 2.);
float vy = mod(floor(edgeId / 2.) + floor(edgeId / 3.), 2.);
float qu = (qx + ux) / u_around;
float qv = (qy + vy) / down;
float r = sin(qv * PI);
float x = cos(qu * PI * 2.) * r;
float z = sin(qu * PI * 2.) * r;
a_position = vec3(x, cos(qv * PI), z);
a_normal = normalize(a_position);
a_texcoord = vec2(qu, qv);
float hue = sin(qu * PI) * .1 + u_hue;
float sat = mix(0.5, 1., mod(quadId, 2.));
float val = 1.;
uvec4 ucolor = uvec4(vec4(hsv2rgb(vec3(hue, sat, val)), 1.) * 255.);
a_color = (ucolor.r << 0) |
(ucolor.g << 8) |
(ucolor.b << 16) |
(ucolor.a << 24) ;
}
</script>
<script id="tf-fs" type="notjs">
#version 300 es
precision mediump float;
out vec4 o;
void main() {
o = vec4(0);
}
</script>
<script id="vs" type="notjs">
uniform mat4 u_worldViewProjection;
uniform vec3 u_lightWorldPos;
uniform mat4 u_world;
uniform mat4 u_viewInverse;
uniform mat4 u_worldInverseTranspose;
attribute vec4 a_position;
attribute vec3 a_normal;
attribute vec2 a_texcoord;
attribute vec4 a_color;
varying vec4 v_position;
varying vec2 v_texCoord;
varying vec3 v_normal;
varying vec3 v_surfaceToLight;
varying vec3 v_surfaceToView;
varying vec4 v_color;
void main() {
v_texCoord = a_texcoord;
v_position = (u_worldViewProjection * a_position);
v_color = a_color;
v_normal = (u_worldInverseTranspose * vec4(a_normal, 0)).xyz;
v_surfaceToLight = u_lightWorldPos - (u_world * a_position).xyz;
v_surfaceToView = (u_viewInverse[3] - (u_world * a_position)).xyz;
gl_Position = v_position;
}
</script>
<script id="fs" type="notjs">
precision mediump float;
varying vec4 v_position;
varying vec2 v_texCoord;
varying vec3 v_normal;
varying vec3 v_surfaceToLight;
varying vec3 v_surfaceToView;
varying vec4 v_color;
uniform vec4 u_lightColor;
uniform vec4 u_diffuseMult;
uniform sampler2D u_diffuse;
uniform vec4 u_specular;
uniform float u_shininess;
uniform float u_specularFactor;
vec4 lit(float l ,float h, float m) {
return vec4(1.0,
abs(l),//max(l, 0.0),
(l > 0.0) ? pow(max(0.0, h), m) : 0.0,
1.0);
}
void main() {
vec4 diffuseColor = texture2D(u_diffuse, v_texCoord) * u_diffuseMult * v_color;
vec3 a_normal = normalize(v_normal);
vec3 surfaceToLight = normalize(v_surfaceToLight);
vec3 surfaceToView = normalize(v_surfaceToView);
vec3 halfVector = normalize(surfaceToLight + surfaceToView);
vec4 litR = lit(dot(a_normal, surfaceToLight),
dot(a_normal, halfVector), u_shininess);
vec4 outColor = vec4((
u_lightColor * (diffuseColor * litR.y +
u_specular * litR.z * u_specularFactor)).rgb,
diffuseColor.a);
gl_FragColor = outColor;
}
</script>
<script src="../3rdparty/chroma.min.js"></script>
<script type="module">
import * as twgl from '../dist/7.x/twgl-full.module.js';
function main() {
twgl.setDefaults({attribPrefix: "a_"});
const m4 = twgl.m4;
const gl = document.getElementById("c").getContext("webgl2");
if (!gl) {
document.querySelector("#no-webgl2").style.display = "";
return;
}
// shaders to render with
const programInfo = twgl.createProgramInfo(gl, ["vs", "fs"]);
// shaders to generate a mesh
const feedbackProgramInfo = twgl.createProgramInfo(gl, ["tf-vs", "tf-fs"], {
// note: you can pass a bufferInfo instead of this array
transformFeedbackVaryings: [
"a_position",
"a_normal",
"a_texcoord",
"a_color",
],
});
function generateMesh(tf, bufferInfo, numVerts, around, hue) {
// Generate a mesh using transform feedback
gl.enable(gl.RASTERIZER_DISCARD);
gl.useProgram(feedbackProgramInfo.program);
gl.bindTransformFeedback(gl.TRANSFORM_FEEDBACK, tf);
gl.beginTransformFeedback(gl.TRIANGLES);
twgl.setUniforms(feedbackProgramInfo, {
u_vertexCount: numVerts,
u_around: around,
u_hue: hue,
});
twgl.drawBufferInfo(gl, bufferInfo);
gl.endTransformFeedback();
gl.bindTransformFeedback(gl.TRANSFORM_FEEDBACK, null);
gl.disable(gl.RASTERIZER_DISCARD);
}
const shapes = [ ];
// Make shapes using transform feedback
[
{ around: 4, down: 3, hue: .2, },
{ around: 6, down: 4, hue: .4, },
{ around: 12, down: 6, hue: .6, },
{ around: 20, down: 10, hue: .8, },
{ around: 40, down: 20, hue: .0, },
].forEach(e => {
const around = e.around;
const down = e.down;
const numQuads = around * down;
const numVerts = numQuads * 6;
const bufferInfo = twgl.createBufferInfoFromArrays(gl, {
position: numVerts * 3,
normal: numVerts * 3,
texcoord: numVerts * 2,
color: { data: numVerts * 4, type: Uint8Array, },
});
const tf = twgl.createTransformFeedback(gl, feedbackProgramInfo, bufferInfo);
generateMesh(tf, bufferInfo, numVerts, down, e.hue);
shapes.push(bufferInfo);
});
function rand(min, max) {
return min + Math.random() * (max - min);
}
// Shared values
const lightWorldPosition = [1, 8, -10];
const lightColor = [1, 1, 1, 1];
const camera = m4.identity();
const view = m4.identity();
const viewProjection = m4.identity();
const tex = twgl.createTexture(gl, {
min: gl.NEAREST,
mag: gl.NEAREST,
format: gl.LUMINANCE,
src: [
255, 128, 255, 128, 255, 128, 255, 128,
128, 255, 128, 255, 128, 255, 128, 255,
255, 128, 255, 128, 255, 128, 255, 128,
128, 255, 128, 255, 128, 255, 128, 255,
255, 128, 255, 128, 255, 128, 255, 128,
128, 255, 128, 255, 128, 255, 128, 255,
255, 128, 255, 128, 255, 128, 255, 128,
128, 255, 128, 255, 128, 255, 128, 255,
],
});
const objects = [];
const drawObjects = [];
const numObjects = 100;
for (let ii = 0; ii < numObjects; ++ii) {
const uniforms = {
u_lightWorldPos: lightWorldPosition,
u_lightColor: lightColor,
u_diffuseMult: [1, 1, 1, 1],
u_specular: [1, 1, 1, 1],
u_shininess: 50,
u_specularFactor: 1,
u_diffuse: tex,
u_viewInverse: camera,
u_world: m4.identity(),
u_worldInverseTranspose: m4.identity(),
u_worldViewProjection: m4.identity(),
};
drawObjects.push({
programInfo: programInfo,
bufferInfo: shapes[ii % shapes.length],
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,
});
}
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.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
const projection = m4.perspective(30 * Math.PI / 180, gl.canvas.clientWidth / gl.canvas.clientHeight, 0.5, 100);
const eye = [1, 4, -20];
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);
}
main();
</script>
</html>