There are lots of '3d' libraries out there already, so why write a new one?
Well, first of all as an exercise in learning... but secondly, also because none of them have a really good story with shaders.
So anyway, long story short I've created a tiny little webgl module. It'll get bigger over time, but for the time being it renders geometry and has a flexible shader model: http://www.github.com/shadowmint/zgl.ts
To render a shader involves creating a scene, shader, and program, then off you go~
// Load webgl context
glc = zgl.context('webgl');
glc.viewport();
// Setup~
var scene = new zgl.scene.Scene(glc);
// Setup time
var time_offset = zgl.buffer.factory(1);
time_offset.data[0] = 0.0;
// Create a set of renderers to use
var renderer = scene.renderer();
// Load program
renderer.program = function(glc) {
var gl = glc.gl;
var vertexShader = zgl.shader.fromScript("2d-vertex-shader");
var fragmentShader = zgl.shader.fromScript("2d-fragment-shader");
var program = new zgl.program.Program(glc, {
vertex: vertexShader,
fragment: fragmentShader
});
// Bind shader attributes
program.bind('a_vertex', zgl.BufferType.VERTEX_FLOAT3);
program.bind('a_color', zgl.BufferType.VERTEX_FLOAT4);
program.bind('a_uv', zgl.BufferType.VERTEX_FLOAT2);
program.bind('u_proj', zgl.BufferType.UNIFORM_MAT4);
program.bind('u_model', zgl.BufferType.UNIFORM_MAT4);
program.bind('u_sampler', zgl.BufferType.UNIFORM_SAMPLER);
program.bind('u_time_offset', zgl.BufferType.UNIFORM_FLOAT);
// Timer is not generated from the geom source; make sure
// we populate that shader attribute each frame.
program.extra = function(p) {
program.buffer('u_time_offset', time_offset);
};
return program;
};
// Set camera
scene.camera.position(0, 2, -9);
// Set background
scene.background = [0.0, 0.0, 0.0, 1.0];
// Create some geometry
var texture = document.getElementById('texture');
var quad = new zgl.prefab.Quad({
texture: texture,
size: [1.0, 1.0, 1.0],
position: [0.0, -1.0, 0.0]
});
// Add geometry to renderer
renderer.add(quad.compile(glc, scene.textures));
// Set the camera view
scene.camera.modelview.unity().rotateZ(3.1415).translate(0, 0, -0.8);
// Update the timer each frame
var timer = 0;
scene.update = function() {
timer += 1;
time_offset.data[0] = timer * 0.1;
};
// Render each frame
glc.run(function(step) {
scene.render(step);
});
Practically speaking, that means we can get down to actually writing shaders without worrying about all the house keeping! Woo~
For example, here's a cute little fragment shader to render a bunch of moving coloured circles on top of each other:
void main() {
float r = sin(v_time_offset/5.0 + gl_FragCoord[0] / 200.0) *
sin(v_time_offset/10.0 + gl_FragCoord[1] / 200.0);
float g = cos(v_time_offset/25.0 + 1.0 - gl_FragCoord[0] / 200.0) *
sin(v_time_offset/25.0 + 1.0 + gl_FragCoord[1] / 200.0);
float b = cos(v_time_offset + gl_FragCoord[1] / 200.0) *
sin(v_time_offset/10.0 + 1.0 + gl_FragCoord[0] / 200.0);
gl_FragColor = vec4(r,g,b,1.0);
}
Now all the ground work is done, I can finally get into writing some interesting and fun shader toys~ Yay!