On Github etodd / shaders
←↑↓→
follow along at etodd.github.io/shaders
animation from the excellent simon schreibt's render hell
var camera = new THREE.OrthographicCamera(-1, 1, 1, -1, -1000, 1000); var geometry = new THREE.Geometry(); geometry.vertices.push(new THREE.Vector3(0, 0.8, 0)); geometry.vertices.push(new THREE.Vector3(-0.8, -0.8, 0)); geometry.vertices.push(new THREE.Vector3(0.8, -0.8, 0)); var scene = new THREE.Scene(); var mat = new THREE.PointCloudMaterial({ size: 10, sizeAttenuation: false }); scene.add(new THREE.PointCloud(geometry, mat)); var renderer = new THREE.WebGLRenderer(); renderer.setSize(window.innerWidth - 4, window.innerHeight - 4); document.body.appendChild(renderer.domElement); function render() { requestAnimationFrame(render); // continue the draw loop renderer.render(scene, camera); } render();
var camera = new THREE.PerspectiveCamera ( 45, // field of view (degrees) window.innerWidth / window.innerHeight, // aspect ratio 1, // near plane 1000 // far plane ); camera.position.z = 500; var geometry = new THREE.Geometry(); // cube! geometry.vertices.push(new THREE.Vector3(-80, -80, -80)); geometry.vertices.push(new THREE.Vector3(-80, 80, -80)); geometry.vertices.push(new THREE.Vector3(80, -80, -80)); geometry.vertices.push(new THREE.Vector3(80, 80, -80)); geometry.vertices.push(new THREE.Vector3(-80, -80, 80)); geometry.vertices.push(new THREE.Vector3(-80, 80, 80)); geometry.vertices.push(new THREE.Vector3(80, -80, 80)); geometry.vertices.push(new THREE.Vector3(80, 80, 80));
input vertices, do math, output vertices
<script type="x-shader/x-vertex" id="vs"> void main() { gl_PointSize = 2.0; gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1); } </script>
var material = new THREE.ShaderMaterial( { vertexShader: document.getElementById('vs').textContent, });
mat4x4 world; mat4x4 view; mat4x4 projection; mat4x4 final = projection * view * world;
you can also multiply vectors with them if they are the right size
mat4x4 world; vec3 position; position = world * position; // ERROR position = world * vec4(position, 1); // okay
you can access individual components of vectors
vec3 position; float height = position.y; // or: height = position[1];
access multiple components simultaneously
vec4 position; position.xy = vec2(0, 0);
mix and match
vec4 a, b; a.zyx = b.yyy;
start with a flat plane in three.js
var geometry = new THREE.Geometry(); for (var x = -50; x < 50; x++) { for (var z = -50; z < 50; z++) geometry.vertices.push(new THREE.Vector3(x, 0, z)); }
var uniforms = { time: { type: 'f', value: 0 }, // f for float }; var material = new THREE.ShaderMaterial( { vertexShader: document.getElementById('vs').textContent, uniforms: uniforms, }); // snip... var clock = new THREE.Clock(); function render() { requestAnimationFrame(render); uniforms.time.value = clock.getElapsedTime(); renderer.render(scene, camera); } render();
uniform float time; void main() { gl_PointSize = 2.0; vec3 p = position; p.y += sin(time) * 5.0; gl_Position = projectionMatrix * modelViewMatrix * vec4(p, 1); }
vertex declaration specifies what data is attached to each vertex
position vec3 normal vec3 texture coordinate vec2 blend weights vec4 instance transform vec4 flux compression floatvar attributes = { offset: { type: 'f', value: [] }, }; var geometry = new THREE.Geometry(); for (var x = -50; x < 50; x++) { for (var z = -50; z < 50; z++) { geometry.vertices.push(new THREE.Vector3(x, 0, z)); attributes.offset.value.push((x + z) * 0.1); } } var material = new THREE.ShaderMaterial( { vertexShader: document.getElementById('vs').textContent, uniforms: uniforms, attributes: attributes, });
uniform float time; attribute float offset; void main() { gl_PointSize = 2.0; vec3 p = position; p.y += sin(time + offset) * 5.0; gl_Position = projectionMatrix * modelViewMatrix * vec4(p, 1); }
usually precalculated at design-time or during loading
of course three.js can do it for you, and even display them for debugging
uniform float time; void main() { gl_PointSize = 2.0; vec3 p = position + normal * sin(time); gl_Position = projectionMatrix * modelViewMatrix * vec4(p, 1); }
automatically handled by the gpu
void main() { gl_FragColor = vec4(0.0, 1.0, 1.0, 1.0); }
var attributes = { vertexColor: { type: 'v3', value: [] }, }; var geometry = new THREE.Geometry(); geometry.vertices.push(new THREE.Vector3(0, 2.0, 0)); geometry.vertices.push(new THREE.Vector3(-2.0, -2.0, 0)); geometry.vertices.push(new THREE.Vector3(2.0, -2.0, 0)); attributes.vertexColor.value.push(new THREE.Vector3(1, 0, 0)); attributes.vertexColor.value.push(new THREE.Vector3(0, 1, 0)); attributes.vertexColor.value.push(new THREE.Vector3(0, 0, 1)); geometry.faces.push(new THREE.Face3(0, 1, 2));
vertex shader:
attribute vec3 vertexColor; varying vec3 varyingColor; void main() { gl_PointSize = 2.0; gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1); varyingColor = vertexColor; }
fragment shader:
varying vec3 varyingColor; void main() { gl_FragColor = vec4(varyingColor, 1.0); }
we could display the xyz values as rgb. vertex shader:
varying vec3 varyingNormal; void main() { gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1); varyingNormal = normal; }
fragment shader. in glsl we can also address vector components with rgba
varying vec3 varyingNormal; void main() { gl_FragColor.rgb = varyingNormal.xyz; gl_FragColor.a = 1.0; }
dot(a, b) = a.x*b.x + a.y*b.y + a.z*b.z
if a and b are normalized, result = cosine of the angle between a and b
uniform vec3 lightDirection; varying vec3 varyingNormal; void main() { float lighting = dot(varyingNormal, lightDirection); gl_FragColor = vec4(lighting, lighting, lighting, 1.0); }
look at the vertex shader
varying vec3 varyingNormal; void main() { gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1); varyingNormal = normal; }
varying vec3 varyingNormal; void main() { gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1); vec4 tmp = modelMatrix * vec4(normal, 0); varyingNormal = tmp.xyz; }
geometry.faceVertexUvs[0] = []; geometry.faceVertexUvs[0].push( [ new THREE.Vector2(1, 0), new THREE.Vector2(0.5, 1), new THREE.Vector2(0, 0), ]); var uniforms = { texture1: { type: 't', value: THREE.ImageUtils.loadTexture('texture.jpg') }, };
varying vec2 textureCoordinate; void main() { gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1); textureCoordinate = uv; }
uniform sampler2D texture1; varying vec2 textureCoordinate; void main() { gl_FragColor = vec4(texture2D(texture1, textureCoordinate).rgb, 1.0); }
uniform float time; varying vec2 textureCoordinate; void main() { gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1); textureCoordinate = uv + vec2(time, time); }