Webgl shaders

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!