Planet WebGL

February 08, 2010

Learning WebGL

WebGL around the net, 8 Feb 2010

One big one today, and two smaller ones:

by giles at February 08, 2010 02:45 PM

February 05, 2010

Learning WebGL

WebGL around the net, 5 Feb 2010

Some interesting new projects today!

  • In the earliest WebGL demos, Vladimir Vukićević used the Sylvester library for vector and matrix maths; it’s convenient, well-written, well-documented, and open source. Pretty much everyone else followed suit. However, its very general-purpose nature means that it can’t be as fast as a specialised library focusing on the specific kinds of calculations we need for 3D graphics. So, he’s started a new project called mjs, to provide just the really simple vector and matrix maths we need, and to do it really quickly. He’s getting pretty encouraging results on his benchmarks, with JITed calculations being more than ten times as quick, and un-JITed at least twice as fast. [UPDATE I've tried porting the upcoming lesson 14 to it, and it works fine. No performance figures yet, though.]
  • Another important part of building 3D scenes is getting models designed in 3D graphics programs into your JavaScript code. People are working on tools to import existing 3D graphics formats like 3DS, but an alternative route is to get the 3D modelling tool to output JSON or some other format you can easily read. Paul Brunt’s GLGE has some support for doing this from Blender, but Dennis Ippel has just announced a project that makes exporting from Blender to WebGL code its main focus. So far it exports to SceneJS, but a generic WebGL version is on its way. It’s not ready to download yet, but will be soon.
  • Another 3D library for WebGL, this one with its own editor! CopperLicht is definitely worth a look.

by giles at February 05, 2010 02:29 PM

February 04, 2010

Learning WebGL

WebGL around the net, 4 Feb 2010

For today, one new link and a couple of older things that I managed to miss first time around:

  • A new interview with a WebGL library creator at 3D test: this time, it’s Marco Di Benedetto on SpiderGL. Well worth a read.
  • GLGE has forums! It looks like a good place not just for discussing the library, but also WebGL in general.
  • Peter Strohm has added more to his WebGL lessons in German; there are now tutorials on adding interaction, and textures.
  • gwt-g3d, one of the three independent projects to create a Google Web Toolkit binding for WebGL, now includes ports of my first 12 lessons as part of its demos. I’m flattered :-) — but additionally, that suggests things have moved on quite a bit since it was announced at the end of December. Its competitors, WGT and GwtGL seem to be coming along nicely too.

by giles at February 04, 2010 07:59 PM

February 02, 2010

Learning WebGL

WebGL around the net, 2 Feb 2010

Finally, some good news about the next tutorial on this blog: the page for lesson 14 — which covers specular highlights — is pretty much done, as is the video for people who don’t have WebGL-enabled browsers. Now it’s “just” a question of writing about it :-S

by giles at February 02, 2010 02:03 PM

February 01, 2010

Learning WebGL

WebGL around the net, 1 Feb 2010

by giles at February 01, 2010 02:28 PM

C3DL Development News

Asteroids in 3D… and a bit of 2D

So I’ve been working on this little demo in preparation for the upcoming (very very soon…docs left only) release of C3DL’s webGL release. I adapted it from Peter Callaghan’s Asteroids game and the models were from an old demo made as part of our user testing last year. This demo is kind of cool because it demonstrates many of the features of C3DL and puts it all together in one. Namely it uses:
  • collada model loading – you can export to collada from a good number of modeling programs including blender, 3ds max and google sketchup
  • particle systems – watch the rocks when they explode!
  • lighting – ok… this is not featured well… I’ll try to see if I can do better with it
  • picking – click on rock to identify which one
I will probably add something to that uses the effects system as that is one key component not highlighted. Its not quite done yet but I figured I’d link it anyways since I won’t have time to finish it till probably Wednesday…. I still have to add scoring and end game functionalities, the keys should be changed to a and d instead of using arrows, and I think my health bar is messed up when it drops below 25 but… its a start. I also have not tested well on webkit (there was a bug of constantly rotating ship at one point but have not been able to reproduce it) and I can’t seem to run chrome on my desktop atm so no testing there. This demo uses webGL in the main display and I painted the hud with 2D canvas. Click on the asteroids to shoot them. Use arrow keys to rotate the ship. https://cs.senecac.on.ca/~catherine.leung/c3dl/Asteroids3/asteroids.html NOTE: To see this demo you need a web browser that supports webGL and it must be enabled.

by Cathy Leung at February 01, 2010 11:12 AM

January 28, 2010

C3DL Development News

Another demo updated

I’ve just finished updating the particle systems demo. There wasn’t anything wrong with the demo itself, but there was an array in the particle system class that wasn’t being initialized properly that suddenly started causing Minefield to crash. The strange thing is that until recently Minefield wasn’t complaining about it. But as of the nightly build on the 23rd it didn’t just complain, it outright crashed. By populating the array with 0s when it is initialized, I’ve got particles working again. The misleading problem was that this happened at the same time I was fixing an issue with colours for Chromium was preventing it from displaying. It started crashing one browser at the same time I fixed a problem for another. It turns out that was caused because colour values were being read out of a file and split into an array, but they were never explicitly made into floating point values. Minefield and Webkit understood them as such anyway, but Chromium didn’t like that. A temporary solution was to multiply the values by 1 before using them, but that isn’t a very good solution. Once I actually traced the problem to its source, a quick parseFloat took care of it. Slightly slower, but more correct.

by peter at January 28, 2010 09:25 PM

Learning WebGL

WebGL around the net, 28 Jan 2010

A lot of new stuff over the last three days:

  • Over at 3D test, an interview with Paul Brunt of GLGE fame — well worth reading.
  • On thing that’s going to be important for WebGL going forward is the ability to load up objects that have been designed in 3D modeling tools like Blender, Autodesk 3D Studio or Google SketchUp. There are two ways of doing this — by converting the tools’ files into a JavaScript-friendly format like JSON, or by writing code to load the files directly in JavaScript. Tim Knip has started work on the latter kind: js3ds, a 3DS import library for JavaScript.
  • A very neat (Firefox-only) demo from murphy: using a web page as a texture on a WebGL object.
  • An impressive (new?) X3DOM demo that I’ve not seen before: floating objects with shadows.
  • Some interesting news about the WebGL spec — a while back, Vladimir Vukićević suggested that the WebGLArrayBuffer and WebGL*Array types would be useful outside WebGL. He’s now started moving their definition into a specification of their own. If this goes ahead, and they are broken out of WebGL, then we’ll probably have to change our pages once again — after all, if they’re not WebGL-specific then they won’t have “WebGL” in their names. One to keep an eye on…
  • Finally, Benjamin DeLillo reports that he’s moved WebGLU to github (an excellent idea) and shows that the latest version of his library makes his 50-line demo an even more impressive 25 lines!

by giles at January 28, 2010 02:43 PM

January 26, 2010

Benjamin DeLillo

Remember that 50 line WebGL demo? Now it's 25 lines

So a while back I did a 50 line demo to show how little code it takes to make WebGL content when using the WebGLU library. Since then I've simplified and consolidated WebGLU in addition to adding new features. One of the biggest (code length wise) was to load shaders from a file and add a default shader as well as a couple other simple shaders. Hopefully WebGLU will grow to include many cool and

by Bjartr (noreply@blogger.com) at January 26, 2010 04:54 AM

A long delayed WebGLU update, some 360 degree video, and that's just for starters

Yes, I'm still here and actively developing my WebGLU library. For those of you who don't know what WebGLU is, it's a library for making WebGL application development easy and fun with minimal coding.  Since my last post WebGLU has gone through a major refactor adding several new features and cleaning up old ones. Plus, I've got a neat demo to show off. Both a live version and a video are

by Bjartr (noreply@blogger.com) at January 26, 2010 04:53 AM

WebGLU now on GitHub

WebGLU, the library that makes WebGL development easy, has been moved to GitHub. I've gotten several requests for this move. Seems like the move couldn't hurt, so after some deliberation I've decided to do it. In performing this move I've cleaned up the examples folder so that it now contains working examples which include texture mapping, keyframed animation, and the videosphere. I've also

by Bjartr (noreply@blogger.com) at January 26, 2010 03:31 AM

January 25, 2010

Learning WebGL

WebGL around the net, 25 Jan 2010

Three framework updates today!

  • Benjamin DeLillo has been quiet for a while, but he’s been working hard on WebGLU, adding (among other things) keyframe animation, image and video-based textures, shader loading from external files, and the beginning stages of import capabilities for .obj files (a common format for 3D models). His blog post gives more information, including a very cool example of a 360° video.
  • Paul Brunt’s GLGE improves in leaps and bounds; he’s now added fog effects.
  • Lindsay Kay’s SceneJS has now gained the ability to override certain rendering parameters in specific subtrees of the scene graph.

by giles at January 25, 2010 07:46 PM

January 24, 2010

Learning WebGL

WebGL Lesson 13 – per-fragment lighting and multiple programs

<< Lesson 12

Welcome to my number thirteen in my series of WebGL tutorials! In it, we’ll cover per-fragment lighting, which is harder work for the graphics card than the per-vertex lighting we’ve been doing so far, but gives much more realistic results. We’ll also look at how you can switch the shaders used by your code by changing which WebGL program object is in use.

Here’s what the lesson looks like when run on a browser that supports WebGL:

<object height="344" width="425"><param name="movie" value="http://www.youtube.com/v/IKFLr-7WnEA&amp;hl=en_US&amp;fs=1&amp;"><param name="allowFullScreen" value="true"><param name="allowscriptaccess" value="always"><embed allowfullscreen="true" allowscriptaccess="always" height="344" src="http://www.youtube.com/v/IKFLr-7WnEA&amp;hl=en_US&amp;fs=1&amp;" type="application/x-shockwave-flash" width="425"></embed></object>

Click here and you’ll see the live WebGL version, if you’ve got a browser that supports it; here’s how to get one if you don’t. You’ll see a sphere and cube orbiting; both will probably be white for a few moments while the textures load, but once that’s done you should see that the sphere is the moon and the cube a (not-to-scale) wooden crate; the scene is similar to the one we had for lesson 12, but we’re closer to the orbiting objects so that you can see more clearly what they look like. As before, both are illuminated by a point light source that is in between them, and if you want to change the light’s position, colour, etc., there are fields beneath the WebGL canvas, along with checkboxes to switch lighting on and off, to switch between per-fragment and per-pixel lighting, and to use or not use the textures.

Try toggling the per-fragment lighting on and off. You should be able to see the difference on the crate pretty easily; the centre is obviously brighter with it switched on. The difference with the moon is more subtle; the edges where the lighting fades out are smoother and less ragged with per-fragment lighting than they are with per-vertex. You will probably be able to see this more easily if you switch off the textures.

More on how it all works below…

The usual warning: these lessons are targeted at people with a reasonable amount of programming knowledge, but no real experience in 3D graphics; the aim is to get you up and running, with a good understanding of what’s going on in the code, so that you can start producing your own 3D Web pages as quickly as possible. If you haven’t read the previous tutorials already, you should probably do so before reading this one — here I will only explain the new stuff. The lesson is based on lesson 12, so you should make sure that you understand that one (and please do post a comment on that post if anything’s unclear about it!)

There may be bugs and misconceptions in this tutorial. If you spot anything wrong, let me know in the comments and I’ll correct it ASAP.

There are two ways you can get the code for this example; just “View Source” while you’re looking at the live version, or if you use GitHub, you can clone it (and the other lessons) from the repository there.

Let’s kick off by describing exactly why it’s worth taking up more graphics processor power by coding per-fragment lighting. You may remember the diagram to the left from lesson 7. As you know, the brightness of a surface is determined by the angle between its normal and the incoming rays of light from the light source. Now, so far, our lighting has been calculated in the vertex shader by combining the normals specified for each vertex with the lighting direction from it. This has provided a light-weighting factor, which we’ve passed from the vertex shader to the fragment shader in a varying variable, and there used to vary the brightness of the colour of the fragment appropriately. This light-weighting factor, like all varying variables, will have been linearly interpolated by the WebGL system to provide values for it for the fragments that lie between the vertices; so, in the diagram, B will be quite bright because the light is parallel with the normal there, A will be dimmer because the light is reaching it at more of an angle, and points in between will shade smoothly between bright and dim. This will look just right.

But now imagine that the light is higher up, as in the diagram to the right. A and C will be dim, as the light reaches them at an angle. We are calculating lighting at the vertices only, and so point B will have the average brightness of A and C, so it will also be dim. This is, of course, wrong — the light is parallel the surface’s normal at B, so it should actually be brighter than either of them. So, in order to calculate the lighting at fragments between vertices, we obviously need to calculate it separately for each fragment.

Calculating the lighting for each fragment means that for each one we need its location (to work out the direction of the light) and its normal; we can get these by passing them from the vertex shader to the fragment shader. They will both be linearly interpolated, so the positions will lie along a straight line between the vertices, and the normals will vary smoothly. That straight line is just what we want, and because the normals at A and C are the same, the normals will be the same for all of the fragments, which is perfect too.

So, that all explains why the cube in our web page looks better and more realistic with per-fragment lighting. But there’s another benefit, and this is that it gives a great effect to shapes made up of flat planes that are meant to approximate curved surfaces, like our sphere. If the normals at two vertices are different, then the smoothly-changing normals at the intervening fragments will give the effect of a curving surface. When considered in this way, per-fragment lighting is a form of what is called Phong shading, and this picture on Wikipedia shows the effect better than I could explain in several thousand words. You can see this in the demo; if you use per-vertex lighting, you can see that the edge of the shadow (where the point light stops having an effect and the ambient lighting takes over) looks a bit “ragged”. This is because the sphere is made up of many triangles, and you can see their edges. When you switch on per-fragment lighting, you can see that the edge of this transition is smoother, giving a better effect of roundness.

Right, that’s the theory out of the way — let’s take a look at the code! The shaders are at the top of the file, so lets take a look at them first. Because this example uses either per-vertex or per-fragment lighting, depending on the setting of the “per-vertex” checkbox, it has vertex and fragment shaders for each kind (it would be possible to write shaders that could do both, but they would be harder to read). The way that we switch between them is something we’ll come to later, but for now you should just note that we distinguish between them by using different id tags when defining them as scripts in the web page. The first to appear are the shaders for per-vertex lighting, and they’re exactly the same as the ones we’ve been using since lesson 7, so I will just show their script tags so that you can match them up with what you will see if you’re following through in the file:

<script id="per-vertex-lighting-fs" type="x-shader/x-fragment">
<script id="per-vertex-lighting-vs" type="x-shader/x-vertex">

Next comes the fragment shader for per-fragment lighting.

<script id="per-fragment-lighting-fs" type="x-shader/x-fragment">
  varying vec2 vTextureCoord;
  varying vec4 vTransformedNormal;
  varying vec4 vPosition;

  uniform bool uUseLighting;
  uniform bool uUseTextures;

  uniform vec3 uAmbientColor;

  uniform vec3 uPointLightingLocation;
  uniform vec3 uPointLightingColor;

  uniform sampler2D uSampler;

  void main(void) {
    vec3 lightWeighting;
    if (!uUseLighting) {
      lightWeighting = vec3(1.0, 1.0, 1.0);
    } else {
      vec3 lightDirection = normalize(uPointLightingLocation - vPosition.xyz);

      float directionalLightWeighting = max(dot(normalize(vTransformedNormal.xyz), lightDirection), 0.0);
      lightWeighting = uAmbientColor + uPointLightingColor * directionalLightWeighting;
    }

    vec4 fragmentColor;
    if (uUseTextures) {
      fragmentColor = texture2D(uSampler, vec2(vTextureCoord.s, 1.0 - vTextureCoord.t));
    } else {
      fragmentColor = vec4(1.0, 1.0, 1.0, 1.0);
    }
    gl_FragColor = vec4(fragmentColor.rgb * lightWeighting, fragmentColor.a);
  }
</script>

You can see that this is very similar to the vertex shaders that we’ve been using so far; it does exactly the same calculations to work out the direction of the light and to then combine that with the normal to calculated a light weighting. The difference is that the inputs to this calculation now come from varying variables rather than per-vertex attributes, and the resulting weighting is immediately combined with the texture colour from the sample rather than passed out for processing later. It’s also worth noting that we have to normalise the varying variable that contains the interpolated normal; normalising, you will remember, adjusts a vector so that its length is one unit. This is because interpolating between two length-one vectors does not necessarily give you a length-one vector, just a vector that points in the right direction. Normalising them fixes that. (Thanks to Glut for pointing that out in the comments.)

Because all of the heavy lifting is being done by the fragment shader, the vertex shader for per-fragment lighting is really simple:

<script id="per-fragment-lighting-vs" type="x-shader/x-vertex">
  attribute vec3 aVertexPosition;
  attribute vec3 aVertexNormal;
  attribute vec2 aTextureCoord;

  uniform mat4 uMVMatrix;
  uniform mat4 uPMatrix;
  uniform mat4 uNMatrix;

  varying vec2 vTextureCoord;
  varying vec4 vTransformedNormal;
  varying vec4 vPosition;

  void main(void) {
    vPosition = uMVMatrix * vec4(aVertexPosition, 1.0);
    gl_Position = uPMatrix * vPosition;
    vTextureCoord = aTextureCoord;
    vTransformedNormal = uNMatrix * vec4(aVertexNormal, 1.0);
  }
</script>

We still need to work out the vertex’s location after the application of the model-view matrix and multiply the normal by the normal matrix, but now we just stash them away in varying variables for later use in the fragment shader.

That’s it for the shaders! The rest of the code will be pretty familiar from the previous lessons, with one exception. So far, we’ve only used one vertex shader and one fragment shader per WebGL page. This one uses two pairs, one for per-vertex lighting and one for per-fragment lighting. Now, you may remember from lesson 1 that the WebGL program object that we use to pass our shader code up to the graphics card can have only one fragment shader and one vertex shader. What this means is that we need to have two programs, and switch which one we use based on the setting of the “per-fragment” checkbox.

The way we do this is simple; our initShaders function is changed to look like this:

  var currentProgram;
  var perVertexProgram;
  var perFragmentProgram;
  function initShaders() {
    perVertexProgram = createProgram("per-vertex-lighting-fs", "per-vertex-lighting-vs");
    perFragmentProgram = createProgram("per-fragment-lighting-fs", "per-fragment-lighting-vs");
  }

So, we have two programs in separate global variables, one for per-vertex lighting and one for per-fragment, and a separate currentProgram variable to store the one that’s currently in use. The createProgram we use to create them is simply a parameterised version of the code we used to have in initShaders, so I won’t duplicate it here.

We then switch in the appropriate program right at the start of the drawScene function:

  function drawScene() {
    gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT)

    perspective(45, 1.0, 0.1, 100.0);

    var perFragmentLighting = document.getElementById("per-fragment").checked;
    if (perFragmentLighting) {
      currentProgram = perFragmentProgram;
    } else {
      currentProgram = perVertexProgram;
    }
    gl.useProgram(currentProgram);

We have to do this before anything else because when we do drawing code (for example setting uniforms or attaching buffers of per-vertex attributes to attributes) we need the current program to be appropriately set up, as otherwise we might use the wrong program:

    var lighting = document.getElementById("lighting").checked;
    gl.uniform1i(currentProgram.useLightingUniform, lighting);

You can see that this means that for each call to drawScene we use one and only one program; it differs only between calls. If you’re wondering whether or not you could use different shader programs at different times within drawScene, so that different parts of the scene were drawn with different ones — perhaps some of your scene might use per-vertex lighting and some per-pixel — the answer is yes! It wasn’t needed for this example, but is perfectly valid and can be useful.

Anyway — with that explained, that’s it for this lesson! You now know how to use multiple programs to switch shaders, and how to code per-pixel lighting. Next time we’ll look at the last bit of lighting that was mentioned in lesson 7: specular highlights.

<< Lesson 12

Acknowledgments: As before, the texture-map for the moon comes from NASA’s JPL website, and the code to generate a sphere is based on this demo, which was originally by the WebKit team. Many thanks to both!

by giles at January 24, 2010 01:13 AM

January 23, 2010

C3DL Development News

Simplifying the Interface

Something we need to do is simplify the C3DL library interface. When approaching such tasks, one method I found extremely useful was to analyze the code in terms of the interface, or that is from the user’s perspective. To do this I first created a basic demo of a spinning cube by writing the necessary HTML and JavaScript files. As for the HTML, it will need to be something like this:
(I emphasized the code which potentially could be changed)
<html>

  <head>
    <title>Cavas3D Demo</title>
    <script>var SCRIPT_PATH = '../../canvas3dapi/'</script>
    <script src="../../canvas3dapi/c3dapi.js"></script>
    <script src="basic_demo.js"></script>
  </head>

  <body>
    <canvas id="demo" width="500" height="500"></canvas>
  </body>

</html>
It would be great if this could be simplified to something like this:
<html>

  <head>
    <title>Cavas3D Demo</title>
    <script src="../../canvas3dapi/c3dapi.js"></script>
  </head>

  <body>
    <canvas datasrc="basic_demo.js" id="demo" width="500" height="500">
    </canvas>
  </body>

</html>
I removed the somewhat unintuitive SCRIPT_PATH variable and moved the basic_demo.js resource into the canvas tag making it more obvious which .js file is associated to which canvas. These changes haven’t actually been made, I’m just playing with possible changes, brainstorming which parts might be able to be modified.

Now, very few changes were made here and it will probably require a significant amount of effort to make it work. But this work is justified by the fact that the user’s experience and first impression will be more positive. If the user has to spend more than a few minutes trying to get a simple example to render, they’ll probably look for alternatives.

Let’s see how the JavaScript could be changed. Right now, you need to write something like this:
(Again, I emphasized the code which potentially could be changed)
c3dl.addModel('cube.dae');
c3dl.addMainCallBack(mainDemo, 'demo');

function mainDemo(canvasName)
{
  var scn = new c3dl.Scene();		
  scn.setCanvasTag(canvasName);
  var renderer = new c3dl.OpenGLES20();
  scn.setRenderer(renderer);
  scn.init();
  
  var cam = new c3dl.FreeCamera();
  cam.setPosition([0,0,50]);
  cam.setLookAtPoint([0,0,-1]);

  var cube = new c3dl.Collada();
  cube.init('cube.dae');
  cube.setAngularVel([0.001,0.001,0.0]);

  scn.addObjectToScene(cube);
  scn.setCamera(cam);
  scn.startScene();
}
Some changes I had in mind involved getting rid of the global c3dl.addModel() and c3dl.addMainCallback() calls and changing OpenGLES20 to just Renderer. The Renderer is an interesting problem. When WebGL started out as Canvas3D, we called the renderer OpenGLES11, then later it became OpenGLES20 and now it could probably be WebGL. Considering how much it has changed and will change, we’ll have to invest some time to make this stop. One simple solution is to abstract the renderer.

For example, if we change OpenGLES20 class to Renderer, the user will no longer need to be concerned what underlying rendering method is used. If a visitor loads the user’s demo, but visitor’s browser doesn’t support WebGL (i.e. it’s I.E.), the demo still loads. This of course only works if we accommodate for this case. So if the browser is IE, we use DirectX. If the browser supports WebGL, we use that. The end result is the user’s page will create the appropriate renderer without them needing to worry about the user’s browser. So let’s look at what lines changed:
function mainDemo(canvasName)
{
  var scn = new c3dl.Scene();
  var renderer = new c3dl.Renderer();
  scn.init(renderer);
  
  var cam = new c3dl.FreeCamera();
  cam.setPosition([0,0,50]);
  cam.setLookAtPoint([0,0,-1]);

  var cube = new c3dl.Collada();
  cube.init('cube.dae');
  cube.setAngularVel([0.001,0.001,0.001]);

  scn.addObjectToScene(cube);  
  scn.setCamera(cam);
  scn.startScene();
}
In terms of the lines trimmed, I’m not certain how much of it is viable. The reason some of the seemingly redundant code is required was because I couldn’t devise any alternative at the time. Looking at it from a different perspective, I should be able to come up with something. In the end we’ll have an interface which is more flexible, easier to understand and of course simple.

by Andor Salga at January 23, 2010 10:37 PM

January 22, 2010

Learning WebGL

Retrospective changes: storing attributes and uniforms on the program

For lesson 13, I needed to be able to easily switch the WebGL program object (which manages the shaders) so that you can see the effects of per-vertex or per-pixel lighting just by toggling a checkbox. Obviously, changing which program you’re using means that the code that draws the scene needs to use the current program’s attribute and uniform locations. One easy way to do this was to keep the attribute and the uniform locations as fields of the program object; this not only made it easier to switch everything across in one go by just changing a currentProgram object, but it also gave me a chance to change things so that we get the uniform locations in one go while initialising, instead of getting them every repaint; as Easy WebGL pointed out the other day, getting uniforms can be costly in terms of processor power, so it’s worth avoiding doing it unnecessarily.

Anyhow, this was such a nice change that I decided to push it back into the older lessons. Here’s an example of what the new code looks like — first, the new initShaders from lesson 1:

  var shaderProgram;
  function initShaders() {
    var fragmentShader = getShader(gl, "shader-fs");
    var vertexShader = getShader(gl, "shader-vs");

    shaderProgram = gl.createProgram();
    gl.attachShader(shaderProgram, vertexShader);
    gl.attachShader(shaderProgram, fragmentShader);
    gl.linkProgram(shaderProgram);

    if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) {
      alert("Could not initialise shaders");
    }

    gl.useProgram(shaderProgram);

    shaderProgram.vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition");
    gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute);

    shaderProgram.pMatrixUniform = gl.getUniformLocation(shaderProgram, "uPMatrix");
    shaderProgram.mvMatrixUniform = gl.getUniformLocation(shaderProgram, "uMVMatrix");
  }

So, you can see that it stores the attributes and uniforms as fields in the program object. This means that setMatrixUniforms can be simplified, as it no longer needs to look up uniforms:

  function setMatrixUniforms() {
    gl.uniformMatrix4fv(shaderProgram.pMatrixUniform, false, new WebGLFloatArray(pMatrix.flatten()));
    gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, new WebGLFloatArray(mvMatrix.flatten()));
  }

…and also, the code in drawScene needs a small adjustment to pick up the attribute locations from the program object rather than the no-longer-existent global variables:

    gl.bindBuffer(gl.ARRAY_BUFFER, triangleVertexPositionBuffer);
    gl.vertexAttribPointer(shaderProgram.vertexPositionAttribute, triangleVertexPositionBuffer.itemSize, gl.FLOAT, false, 0, 0);
    setMatrixUniforms();
    gl.drawArrays(gl.TRIANGLES, 0, triangleVertexPositionBuffer.numItems);

Right, hopefully I can get round to writing up lesson 13 properly now, it’s been too long coming!

by giles at January 22, 2010 02:39 PM

January 21, 2010

Learning WebGL

WebGL around the net, 21 Jan 2010

Lots of YouTube stuff today:

  • Another very cool graphics-card overloading page from the demo scene guys, who specialise in creating very cool scenes using jaw-droppingly tiny amounts of code, all in the fragment shaders. This one’s called Four Dollar Plastic Laminator, it’s apparently a 1k demo (meaning it’s all coded in just one kilobyte of fragment shader!), and if your GPU’s not powerful enough to view it, you can see it on YouTube here.
  • A very cool demo in a different way; this one is unfortunately only on YouTube, but it shows a model taken from World of Warcraft, exported to X3D, and then displayed in a Web browser using X3DOM, which is built on WebGL. Very impressive!
  • Finally, two examples of terrain mapping in WebGL, which both look pretty promising.

by giles at January 21, 2010 08:08 PM

Do textures work for you?

Apparently, gl.enable(gl.TEXTURE_2D) is invalid WebGL, and is absolutely not needed (according to the spec) to make textures work.

However, pipy says in the comments that, at least for him, textures don’t appear without it. Both pipy and Kenneth Russell suspect that the cause might be the graphics driver. [UPDATE: sounds like that was it: he's managed to find an updated driver that fixes it for him. See his comment for more.]

If this is a widespread problem then until the drivers are fixed, it needs to be addressed by people building WebGL pages (or maybe even in the browsers). So it would be great to get a sample of how many people are affected: if you have a WebGL-enabled browser, could you have a quick look at this demo, and then post a comment here saying whether or not you see the wooden crate texture on the spinning cube, and what OS/graphics card/browser combination you’re using? Both positive and negative results much appreciated!

by giles at January 21, 2010 01:37 PM

January 19, 2010

C3DL Development News

Updating Demos

I’m continuing to update the demos with the copy of the library that works cross-browser (and will continue to do so). There was a slight delay in the motion capture demo due to the difference in how the browsers read xml files. We had been using:

xmlDoc = document.implementation.createDocument("","",null);
xmlDoc.async = false;
xmlDoc.load(xmlFile);

but Safari and Chrome don’t accept that. After a quick search (ignoring the pages that make it sound more complicated than it is and want to sell you code to take care of it) I found the answer to be quite simple.

xmlDoc = document.implementation.createDocument("","",null);
xmlDoc.async = false;
var xmlReq = new XMLHttpRequest();
xmlReq.open("GET", xmlFile, false);
xmlReq.send(null);
xmlDoc=xmlReq.responseXML;

It ends up as a few extra lines, but it works for all three. This was a problem with the code for that individual page, but it seems to be the kind of thing we can expect when trying to write complex code for multiple browsers.

I’ll be working on the particle systems demo next, as it also has a bit of code that is firefox only.

by peter at January 19, 2010 03:40 PM

January 18, 2010

Learning WebGL

WebGL around the net, 18 Jan 2010

  • More news from Lindsay Kay about SceneJS — he’s just released version 0.4.0, which has some serious performance improvements, most interestingly including a replacement of the Sylvester matrix/vector library with some streamlined 3D-specific code. Sounds very promising!
  • Paul Brunt’s GLGE framework is also moving rapidly forward, and now supports picking — that is, the ability for the user to select an object in the 3D scene, which is essential for most serious 3D apps.
  • News about the C3DL library too: it’s now working cross-browser.
  • On the subject of established 3D systems, there seems to be a report percolating around a couple of German news sites that Second Life 2.0 will be rewritten in WebGL. Unfortunately the only reference cited is a blog post that doesn’t mention it, so maybe someone got hold of the wrong end of the stick. Or maybe the combination of my schoolboy German plus Google Translate is leading me astray when I try to read the articles. Does anyone know any more? [UPDATE anty explains in the comments here that the author of the first article explains in the comments there that he believes that this will happen, but has no hard evidence.]

by giles at January 18, 2010 06:27 PM

January 15, 2010

Learning WebGL

WebGL around the net, 15 Jan 2010

Two updates today:

  • Eric Shepherd’s tutorial on WebGL at the Mozilla Developer Center, which I think only started a week ago, is progressing by leaps and bounds: so far, he’s covered similar ground to the first seven lessons here, but his latest is very impressive indeed: how to animate textures by mapping an Ogg video onto the faces of a rotating cube. Cool stuff! — though, perhaps unsurprisingly, it doesn’t work in Chrome yet… If you’re following the lessons here (or even if you’re not :-) I’d definitely recommend you follow Eric’s stuff too.
  • Inigo Quilez has been adding new shader demos to the shader toy page he released a little while back: most impressive is the Mandelbulb (a sort of 3D Mandelbrot set), which drew some incredibly detailed pictures on my desktop machine before it crashed the graphics card :-S

Have a great weekend!

by giles at January 15, 2010 06:50 PM

C3DL Development News

Cross-browser progress update

I finally have our library working properly on Firefox (Gecko/20100114 Minefield/3.7a1pre), Safari (Version 4.0.4 (5531.21.10, r53178)) and Chrome (Chromium 4.0.299.0 (36242))

I’ve updated the orbiter demo with the new code, and will shortly be updating some of the older demos. There are still a few minor issues to deal with, such as the moon not showing up properly sometimes, but we’re not getting exceptions anymore.

Another of these issues (Chrome only) has to do with reading floating point values from collada files. When we try to use the values, it throws an exception, but if I multiply the values by 1 before we use them, it works. I’ll have to spend a little more time trying to figure out if this is caused by something I’m doing wrong, or if it might be a bug.

by peter at January 15, 2010 06:28 PM

January 14, 2010

Learning WebGL

Changing back and forth…

As they say, it’s six of one and half a dozen of the other… I’ve made some more retrospective changes. The good news first:

On the WebGL Wiki, Gman just removed the code saying gl.enable(gl.TEXTURE_2D) from the tutorial, saying that it’s not required, or indeed valid WebGL! I checked on the mailing list, and that’s entirely correct. The TEXTURE_2D constant was misplaced in the spec, which might have been the cause of the confusion. So I’ve removed that from all the lessons, and you can get rid of it from your code too :-)

From the good news to the neutral news: a number of people who were running Safari on Macs found that they couldn’t see the texture, or indeed the lighting, for lessons 11 and 12. I’ve finally worked out why: Safari doesn’t like PNG textures, or at least the specific PNGs I was using. I need to put together a good test to reproduce this so that I can report it to the WebKit team, but for now I’ve switched to using GIFs for all of my textures, and you may want to think of doing likewise. A problem, but there’s an easy workaround for everyone to use in the meantime.

From the neutral news to the, well, not so great. As I said the other day, WebKit doesn’t seem to support the flipY flag for texImage2D. So I’ve backed out the change that I’d made to use that, and once again the textures are being flipped by code in the fragment shaders.

by giles at January 14, 2010 07:28 PM

WebGL around the net, 14 Jan 2010

Two new links for today:

  • I’d been wondering how long it would be before someone put together a multi-player WebGL game (or at least demo); it looks like Peter Strohm has managed to get the first one done! (Minefield only right now.)
  • Don’t know how I missed this one on the EWGL blog — some musings on the costliness of looking up uniform locations, explaining why you shouldn’t do it every time you repaint the canvas (which is something my own code is definitely guilty of). Useful stuff, and perhaps reason enough for another round of retrospective changes here…

On the subject of the lessons, I’ve finished the first cut of the code for lesson 13, which introduces per-fragment lighting. Let me know what you think!

by giles at January 14, 2010 06:44 PM

January 13, 2010

Learning WebGL

WebGL around the net, 13 Jan 2010

Today we’ve three bits of news about frameworks that make WebGL programming easier.

  • Paul Brunt has managed to add shadows to GLGE! Shadows require some fairly complex trickery with frame buffers to work properly, and unfortunately part of that isn’t implemented yet in Firefox (or, it seems, in the other WebGL implementations). However, Paul’s found a workaround, and the result is pretty impressive!
  • Lindsay Kay has released version 0.3.0 of SceneJS. This includes a framework to make it easy to import “assets” — that is, pre-packaged files containing the details of objects including their vertex positions, normals, and so on — into your scene. So far it only supports JSON format, but Lindsay’s working on COLLADA, which is supported by many 3D design tools. [UPDATE] More about this from Lindsay in the comments.
  • Marco Di Benedetto at the Visual Computing Lab in Pisa has released a very early version of a WebGL-based graphics library called SpiderGL; simple stuff so far, but it already has support for render-to-texture using frame buffer objects, which is pretty advanced stuff :-) [UPDATE] Marco gave me a corrected URL in the comments, so I’ve fixed the link. His new site also has a better description of SpiderGL: “The philosophy behind SpiderGL is : to provide typical structures and algorithms for realtime rendering to developers of 3D graphics web application, without forcing them to comply with some specific paradigm (i.e. scene graphs) nor preventing low level access to the underlying graphics layer (WebGL).” Sounds good to me; check out the comments for more, including a very exciting list of features!

by giles at January 13, 2010 06:56 PM

January 12, 2010

Learning WebGL

Cruft dropped

I’ve removed the compatibility cruft from all of the lessons and examples on this site, and updated the WebGL Cookbook’s page about initialising your WebGL context. Here’s to cleaner, simpler code!

The bad news is that I’ve discovered that WebKit doesn’t yet support the flipY flag that I was so happy to find in the parameter list for texImage2D. So that retrospective change will have to go. A pity :-(

by giles at January 12, 2010 06:33 PM

January 08, 2010

Learning WebGL

WebGL around the net, 8 Jan 2010

Just two quick links for today:

  • Eric Shepherd, Developer Documentation Lead at Mozilla, is starting a series of articles on WebGL; just one so far, but it’s looking promising!
  • Chris Marrin has added a new “User Contributions” section to the Khronos WebGL wiki: a great place for more permanent record of the kind of stuff I’m linking to in my WebGL roundups. I’ll have to copy some of the older ones over there. [UPDATE: I've put a few up there, but there are way too many cool demos in my roundup archives to add all of the ones that deserve it, at least last thing on a Friday evening.]

Have a great weekend!

by giles at January 08, 2010 08:53 PM

January 07, 2010

Learning WebGL

A quick retrospective change to the lessons: image flipping

In the comments to lesson 5, where we introduce textures, rotoglup pointed out that adding an operation per-fragment to flip the texture vertically (which is required because its coordinates increase as you move down the GIF image, while we want maths-like coordinates that increase as you go upwards) is a bit of an overhead for something that should be done as a one-off. Rob just commented on the same post, saying how it would be done in OpenGL, and that reminded me that I’d been intending to search the spec to see if there was a better way of flipping textures. Lo and behold, I discovered that I’d missed out on a rather useful parameter for texImage2D:

texImage2D(target, level, image, flipY, asPremultipliedAlpha)

To be fair, there was no spec when I wrote the lesson :-)

Anyway, I’ve been through all of the lessons, replaced 1.0 - vTextureCoord.t with vTextureCoord.t in the fragment shaders, and added true to the end of all of the calls to texImage2D.

It’s fun to remove bad code :-)

by giles at January 07, 2010 07:51 PM

WebGL around the net, 7 Jan 2010

A good mix of stuff today!

  • Aaron Babcock has written a version of a classic Flash game to run on the graphics card using WebGL. It’s called Falling Sand; click on one of the palette of materials at the bottom of the page and then drag on the canvas to paint it, add a few bits of different materials, and then sit back and watch… mesmerising!
  • Another hypnotic demo, this one from Emanuele Ruffaldi: a WebGL game of life.
  • Things are hotting up in the race to support WebGL in Google Web Toolkit: there’s another project to get this working, this time from Soenke Sothmann and Steffen Schafer, to add to the two we saw at the end of last year. As Soenke says, “competition stimulates business :-)”
  • Finally, a neat demo from murphy; a spinning cube taking its texture from a 2D canvas that shows the time. I particularly like this one because the code is easy to read for anyone who’s been following the lessons here :-), and it also uses a better setting for the mipmaps (which I should probably retrospectively update the tutorials to use)

That’s all for today! One final thing — tomorrow I’ll be collating the results of the browser tests to see if we can finally get rid of the compatibility cruft in our WebGL code — so if you haven’t done so already, I’d be really grateful if you could check out this page and then post a comment on this post saying what OS and browser you’re using, and what the results were. Thanks!

by giles at January 07, 2010 07:02 PM

January 05, 2010

Learning WebGL

WebGL around the net, 5 Jan 2010

Just one new link for today: QBox, by Sandro Paganotti. It’s a photo slideshow app, which displays a number of photos on the sides of a slowly-rotating cube.

by giles at January 05, 2010 07:03 PM

WebGL Lesson 12 – point lighting

<< Lesson 11

Welcome to my number twelve in my series of WebGL tutorials, the second one that isn’t based on the NeHe OpenGL tutorials. In it, we’ll go through point lighting, which is pretty simple, but is important and will lead on to interesting things later. Point lighting, as you might expect, is lighting that comes from a particular point within a scene — unlike the directional lighting we’ve been using so far, which comes from some point outside the scene.

Here’s what the lesson looks like when run on a browser that supports WebGL:

<object height="344" width="425"><param name="movie" value="http://www.youtube.com/v/t-JPgkg2itQ&amp;hl=en_US&amp;fs=1&amp;"><param name="allowFullScreen" value="true"><param name="allowscriptaccess" value="always"><embed allowfullscreen="true" allowscriptaccess="always" height="344" src="http://www.youtube.com/v/t-JPgkg2itQ&amp;hl=en_US&amp;fs=1&amp;" type="application/x-shockwave-flash" width="425"></embed></object>

Click here and you’ll see the live WebGL version, if you’ve got a browser that supports it; here’s how to get one if you don’t. You’ll see a sphere and cube orbiting; both will probably be white for a few moments while the textures load, but once that’s done you should see that the sphere is the moon and the cube a (not-to-scale) wooden crate. Both are illuminated by a point light source that is in between them. If you want to change the light’s position, colour, etc., there are fields beneath the WebGL canvas.

More on how it all works below…

The usual warning: these lessons are targeted at people with a reasonable amount of programming knowledge, but no real experience in 3D graphics; the aim is to get you up and running, with a good understanding of what’s going on in the code, so that you can start producing your own 3D Web pages as quickly as possible. If you haven’t read the previous tutorials already, you should probably do so before reading this one — here I will only explain the new stuff. The lesson is based on lesson 11, so you should make sure that you understand that one (and please do post a comment on that post if anything’s unclear about it!)

There may be bugs and misconceptions in this tutorial. If you spot anything wrong, let me know in the comments and I’ll correct it ASAP.

There are two ways you can get the code for this example; just “View Source” while you’re looking at the live version, or if you use GitHub, you can clone it (and the other lessons) from the repository there.

Let’s kick off by describing exactly what we’re trying to do with point lighting; the difference between it and directional lighting is that the light comes from a point within the scene. A moment’s thought should make it clear that this means that the angle from which it comes is different at every point in the scene. So, the obvious way to model it is to calculate the direction toward the light’s location for each vertex and then to just do exactly the same calculations as we did for directional lighting. And that’s what we do!

(You might be thinking, at this point, that perhaps it would be even better to calculate the direction to the light not just for every vertex, but for the points between vertices — that is, for the fragments. And you’d be quite right in thinking that; lighting like that is harder work for the graphics card, but it looks much better. And it’s what we’ll move on to in the next lesson :-)

Now we’ve determined what to do, it’s worth looking once again at this lesson’s demo page and noting one more thing: there’s no actual object in the scene at the point where the light is coming from. If you want to have an object that appears to be casting light (say, the sun in the centre of the solar system) then you need to define the light source and the object separately. Doing the object should be pretty easy based on the previous lessons, so in this walkthough I’ll only explain how the point light source works. As you might expect from the description above, it’s actually really simple; most of the differences between this page and lesson 11’s are simply to draw the cube and make it and the sphere orbit…

As usual, we’ll start at the bottom of the source HTML file and work our way up through the differences between this file and lesson 11’s. The first set of changes are in the HTML body, where the fields where you could enter a light direction have changed to be the position of the light. This is simple enough that there’s no point in showing them here, so let’s move on up to webGLStart. Once again, the changes are simple — this lesson has no mouse-based controls, so we have no mouse-handling code, and the function formerly known as initTexture is now called initTextures because it’s going to load two of them. Not very exciting…

Moving a little further up, the tick function has gained a new call, to animate, so that our scene updates over time:

  function tick() {
    drawScene();
    animate();
  }

Above that is the animate function itself, which simply updates two global variables that describe how far around their orbits the moon and the cube are in such a manner that they orbit at 50°/second:

  var lastTime = 0;
  function animate() {
    var timeNow = new Date().getTime();
    if (lastTime != 0) {
      var elapsed = timeNow - lastTime;

      moonAngle += 0.05 * elapsed;
      cubeAngle += 0.05 * elapsed;
    }
    lastTime = timeNow;
  }

The next function up is drawScene, which has a few interesting changes. It starts off with the normal boilerplate code to clear the canvas and set up our perspective matrix, and then has code identical to lesson 11’s to check whether the lighting checkbox is checked and to send the ambient lighting colour to the graphics card:

  function drawScene() {
    gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT)

    perspective(45, 1.0, 0.1, 100.0);

    var lighting = document.getElementById("lighting").checked;
    gl.uniform1i(gl.getUniformLocation(shaderProgram, "uUseLighting"), lighting);
    if (lighting) {
      gl.uniform3f(
        gl.getUniformLocation(shaderProgram, "uAmbientColor"),
        parseFloat(document.getElementById("ambientR").value),
        parseFloat(document.getElementById("ambientG").value),
        parseFloat(document.getElementById("ambientB").value)
      );

Next, we push the position of our point light up to the graphics card in a uniform. This is equivalent to the code that pushed the lighting direction up in lesson 11; the difference is in something that was taken away rather than something added. When we sent the lighting direction to the graphics card, we needed to turn it into a unit vector (that is, scale it so that its length was one unit) and reverse its direction. No need for anything like that here: we just push the coordinates of the light directly up:

      gl.uniform3f(
        gl.getUniformLocation(shaderProgram, "uPointLightingLocation"),
        parseFloat(document.getElementById("lightPositionX").value),
        parseFloat(document.getElementById("lightPositionY").value),
        parseFloat(document.getElementById("lightPositionZ").value)
      );

Next, we do the same for the point light’s colour, and we’re done with the lighting code in drawScene.

      gl.uniform3f(
        gl.getUniformLocation(shaderProgram, "uPointLightingColor"),
        parseFloat(document.getElementById("pointR").value),
        parseFloat(document.getElementById("pointG").value),
        parseFloat(document.getElementById("pointB").value)
      );
    }

Next, we actually draw the sphere and the cube in the appropriate positions:

    mvTranslate([0, 0, -20]);

    mvPushMatrix();
    mvRotate(moonAngle, [0, 1, 0]);
    mvTranslate([5, 0, 0]);
    gl.activeTexture(gl.TEXTURE0);
    gl.bindTexture(gl.TEXTURE_2D, moonTexture);
    gl.uniform1i(gl.getUniformLocation(shaderProgram, "uSampler"), 0);

    gl.bindBuffer(gl.ARRAY_BUFFER, moonVertexPositionBuffer);
    gl.vertexAttribPointer(vertexPositionAttribute, moonVertexPositionBuffer.itemSize, gl.FLOAT, false, 0, 0);

    gl.bindBuffer(gl.ARRAY_BUFFER, moonVertexTextureCoordBuffer);
    gl.vertexAttribPointer(textureCoordAttribute, moonVertexTextureCoordBuffer.itemSize, gl.FLOAT, false, 0, 0);

    gl.bindBuffer(gl.ARRAY_BUFFER, moonVertexNormalBuffer);
    gl.vertexAttribPointer(vertexNormalAttribute, moonVertexNormalBuffer.itemSize, gl.FLOAT, false, 0, 0);

    gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, moonVertexIndexBuffer);
    setMatrixUniforms();
    gl.drawElements(gl.TRIANGLES, moonVertexIndexBuffer.numItems, gl.UNSIGNED_SHORT, 0);
    mvPopMatrix();

    mvPushMatrix();
    mvRotate(cubeAngle, [0, 1, 0]);
    mvTranslate([5, 0, 0]);
    gl.bindBuffer(gl.ARRAY_BUFFER, cubeVertexPositionBuffer);
    gl.vertexAttribPointer(vertexPositionAttribute, cubeVertexPositionBuffer.itemSize, gl.FLOAT, false, 0, 0);

    gl.bindBuffer(gl.ARRAY_BUFFER, cubeVertexNormalBuffer);
    gl.vertexAttribPointer(vertexNormalAttribute, cubeVertexNormalBuffer.itemSize, gl.FLOAT, false, 0, 0);

    gl.bindBuffer(gl.ARRAY_BUFFER, cubeVertexTextureCoordBuffer);
    gl.vertexAttribPointer(textureCoordAttribute, cubeVertexTextureCoordBuffer.itemSize, gl.FLOAT, false, 0, 0);

    gl.activeTexture(gl.TEXTURE0);
    gl.bindTexture(gl.TEXTURE_2D, crateTexture);
    gl.uniform1i(gl.getUniformLocation(shaderProgram, "uSampler"), 0);

    gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, cubeVertexIndexBuffer);
    setMatrixUniforms();
    gl.drawElements(gl.TRIANGLES, cubeVertexIndexBuffer.numItems, gl.UNSIGNED_SHORT, 0);
    mvPopMatrix();
  }

So, that’s drawScene. Moving further up the code, you will see that initBuffers has gained our standard code for generating buffers for a cube as well as the code for a sphere, and even further up that initTextures is now loading two textures instead of just one.

The next, and in fact the final, change in the file is the most important one. If you scroll up to the top, where the vertex shader is, you’ll see that it has a few small changes, and its these that make the difference for this lesson. Working through from the top, with changes in red:

  attribute vec3 aVertexPosition;
  attribute vec3 aVertexNormal;
  attribute vec2 aTextureCoord;

  uniform mat4 uMVMatrix;
  uniform mat4 uPMatrix;
  uniform mat4 uNMatrix;

  uniform vec3 uAmbientColor;

  uniform vec3 uPointLightingLocation;
  uniform vec3 uPointLightingColor;

So, we have uniforms for the lighting location and colour to replace the old lighting direction and colour. Next:

  uniform bool uUseLighting;

  varying vec2 vTextureCoord;
  varying vec3 vLightWeighting;

  void main(void) {
    vec4 mvPosition = uMVMatrix * vec4(aVertexPosition, 1.0);
    gl_Position = uPMatrix * mvPosition;

What we’ve done here is split our old code in two. In all of our vertex shaders so far, we’ve applied the model-view matrix and the projection matrix to the vertex position in one go, like this:

    // Code from lesson 11
    gl_Position = uPMatrix * uMVMatrix * vec4(aVertexPosition, 1.0);

Now, we’re storing the intermediate value, the position of the vertex with the current model-view matrix applied but before it has been adjusted to allow for perspective. This is used in the next bit:

    vTextureCoord = aTextureCoord;

    if (!uUseLighting) {
      vLightWeighting = vec3(1.0, 1.0, 1.0);
    } else {
      vec3 lightDirection = normalize(uPointLightingLocation - mvPosition.xyz);

The light’s position is in terms of the world coordinates, and the vertex position, once it’s been multiplied by the model-view matrix, is also in terms of world coordinates. We need to work out the direction of the point light from our current vertex in terms of these coordinates, and to work out the direction from one point to another, we just need to subtract them; once that’s done, we need to normalise the direction vector so that, just like our old lighting direction vector, it has a length of one. Once that’s done, all of the pieces are in place to do a calculation that’s identical to the one we were doing for directional lighting, with just a few variable names changed:

      vec4 transformedNormal = uNMatrix * vec4(aVertexNormal, 1.0);
      float directionalLightWeighting = max(dot(transformedNormal.xyz, lightDirection), 0.0);
      vLightWeighting = uAmbientColor + uPointLightingColor * directionalLightWeighting;

And that’s it! You now know how to write shaders to provide point lighting.

That’s it for now; next time we’ll look at lighting again, improving the realism of our scene by making the lighting work per-fragment instead of per-vertex.

<< Lesson 11

Acknowledgments: As before, the texture-map for the moon comes from NASA’s JPL website, and the code to generate a sphere is based on this demo, which was originally by the WebKit team. Many thanks to both!

by giles at January 05, 2010 03:18 PM