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);
}