Some time ago Owen pointed out to me that it is much better for
various reasons to have many small special-purpose shader programs
rather than a few that contain many if statements for the various
situations. GlowScript now uses a total of 13 shader programs, 8
vertex shaders and 5 fragment shaders. There are three major
categories:
1) Display of opaque objects, of three types (fixed solids such as
box, sphere, etc.; curves -- a sequence of points; triangles and quads
for dynamic shapes)
2) Display of transparent objects by depth peeling (currently curves
cannot be transparent); see
http://www.khronos.org/message_boards/viewtopic.php?f=44&t=4905
3) Mouse picking (by assigning false colors to objects and using no
lighting calculations, so that the front-most pixel under the mouse
has a false color representing its id number)
The GlowScript files are found at
https://bitbucket.org/davidscherer/glowscript. In the Source folder,
lib/glow/WebGLRenderer.js has the CPU code for driving the shaders,
and the shaders are in Source/shaders (the experimental shaders
extent_vertex and final_fragment are not currently used).
A vertex object in CPU memory has arrays of position, normal, color,
opacity, texel coordinates, bumpmap coordinates, shininess, and
emissive (looks like it glows). The constructor for a vertex object
adds this information to Float32Arrays used for rendering.
As with all GlowScript objects, changing any attribute of a vertex has
the side effect of setting a changed flag. At the start of every
render, we run through the list of changed objects (box, sphere,
vertex, whatever) and update information to be passed to the GPUs.
This means that a user program can change the position of an object
many times between renders, but we pay the price of updating the GPU
data just once, at the start of the next render. (This is the same
scheme that is used by VPython.)
In the case of changes to a vertex, that means updating the
Float32Arrays. We then run through the triangle and quad objects and
categorize them as to whether they are totally opaque or have
transparent elements, and whether they have textures and/or bumpmaps,
and for each category we construct a list of vertex indices. Then for
example to render all of the opaque objects that have the same
texture, we just give the GPUs pointers to the vertex information and
to the index list for these objects (converted to Uint16Array), so
that all of the triangles and quads (which are rendered as though they
were two triangles) get rendered as one object, which is very fast.
I should mention that a position or color Float32Array might be many
thousands of vertices long, but if you give a Uint16Array index set of
just 6 numbers, corresponding to two triangles, it is the list of the
Uint16Array that governs, even though the indices point into giant
arrays. The length of the index array need not match the length of the
arrays pointed into.
There are some performance optimizations that I haven't done yet. In
particular, just to get going, I reload all of the Float32Array data
every render cycle using gl.bufferData, but I should reload just the
changed data using gl.bufferSubData.
Bruce