WebGL Lesson 3 – a bit of movement

<< Lesson 2Lesson 4 >>

Welcome to my number three in my series of WebGL tutorials. This time we’re going to start making things move around. It’s based on number 4 in the NeHe OpenGL tutorials.

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

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.

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 first and second tutorials already, you should probably do so before reading this one — here I will only explain the differences between the code for lesson 2 and the new code.

As before, 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. Either way, once you have the code, load it up in your favourite text editor and take a look.

Before I get into describing the code, I’ll clarify one thing. The way you animate a 3D scene in WebGL is very simple — you just draw repeatedly, drawing it differently each time. This may well be totally obvious to a lot of readers, but it was a bit of a surprise to me when I was learning OpenGL, and might surprise others who are coming to 3D graphics for the first time with WebGL. The reason I was confused originally was that I was imagining that it would use a higher-level abstraction, which would work in terms of “tell the 3D system that there’s (say) a square at point X the first time I draw it, and then to move the square, tell the 3D system that the square I told it about earlier has moved to point Y.” Instead, what happens is more that you “tell the 3D system that there’s a square at point X, then next time you draw it, tell the 3D system that there’s a square at point Y, and then next time that there’s a square at point Z” and so on.

I hope that last paragraph has made things clearer for at least some people (let me know in the comments if it’s just confusing matters and I’ll delete it :-)

Anyway, what this means is that because our code so far has been using a function called drawScene to draw everything, to animate things we need to arrange matters such that this function is called repeatedly, and draws something slightly different each time. Let’s start at the bottom of the index.html file and see how that’s done. Firstly, let’s take a look at the function that kicks everything off when the page is loaded, webGLStart:

  function webGLStart() {
    var canvas = document.getElementById("lesson03-canvas");
    initGL(canvas);
    initShaders()
    initBuffers();

    gl.clearColor(0.0, 0.0, 0.0, 1.0);
    gl.enable(gl.DEPTH_TEST);

    tick();
  }

The only change here is that instead of calling drawScene at the end to draw the scene, we call a new function, tick. This is the function that needs to be called regularly; it updates the scene’s animation state (eg. the triangle has moved from being 81 degrees rotated to 82 degrees) draws the scene, and also arranges for itself to be called again in an appropriate time. It’s the next function up in the file, so let’s look at it next.

  function tick() {
    requestAnimFrame(tick);

The first line is where tick arranges to be called again when a repaint is needed next. requestAnimFrame is a function in some Google-provided code that we’re including into this web page with a <script> tag at the top, webgl-utils.js. It gives us a browser-independent way of asking the browser to call us back next time it wants to repaint the WebGL scene — for example, next time the computer’s display is refreshing itself. Right now, functions to do this exist in all WebGL-supporting browsers, but they have different names for it (for example, Firefox has a function called mozRequestAnimationFrame, while Chrome and Safari have webkitRequestAnimationFrame). In the future they are expected to all use just requestAnimationFrame. Until then, we can use the Google WebGL utils to have just one call that works everywhere.

It’s worth noting that you could get a similar effect to using requestAnimFrame by asking JavaScript to call the drawScene function regularly, for example by using the built-in JavaScript setInterval function. A lot of early WebGL code (including earlier versions of these tutorials) did just that, and it worked fine — right up until people had more than one WebGL page open, in different browser tabs. Because functions scheduled with setInterval are called regardless of whether the browser tab they belong to is showing, using it meant that computers were doing all the work of displaying every open WebGL tab all the time, hidden or not. This was obviously a Bad Thing, and was the reason why requestAnimationFrame was introduced; functions scheduled using it are only called when the tab is visible.

On to the remainder of tick:

    drawScene();
    animate();
  }

So, once we’ve scheduled tick to be called again next time the browser wants a frame to be painted, we simply draw this one, and update our state for the next. Let’s look at the drawScene and animate functions in turn.

drawScene is about two thirds of the way down index.html. The first thing to note is that just before the function declaration, we’re now defining two new global variables.

  var rTri = 0;
  var rSquare = 0;

These are used to track the rotation of the triangle and the square respectively. They both start off rotated by zero degrees, and then over time these numbers will increase — you’ll see how later — making them rotate more and more. (A side note — using global variables for things like this in a 3D program that is not a simple demo like this would be really bad practice. I show how to structure things in a more elegant manner in lesson 9.)

The next change in drawScene comes at the point where we draw the triangle. I’ll show all of the code that draws it by way of context, the new lines are the ones in red:

    mat4.perspective(45, gl.viewportWidth / gl.viewportHeight, 0.1, 100.0, pMatrix);

    mat4.identity(mvMatrix);

    mat4.translate(mvMatrix, [-1.5, 0.0, -7.0]);

    mvPushMatrix();
    mat4.rotate(mvMatrix, degToRad(rTri), [0, 1, 0]);

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

    gl.bindBuffer(gl.ARRAY_BUFFER, triangleVertexColorBuffer);
    gl.vertexAttribPointer(shaderProgram.vertexColorAttribute, triangleVertexColorBuffer.itemSize, gl.FLOAT, false, 0, 0);

    setMatrixUniforms();
    gl.drawArrays(gl.TRIANGLES, 0, triangleVertexPositionBuffer.numItems);

    mvPopMatrix();

In order to explain what’s going on here, let’s go back to lesson 1. There, I said:

In OpenGL, when you’re drawing a scene, you tell it to draw each thing you draw at a “current” position with a “current” rotation — so, for example, you say “move 20 units forward, rotate 32 degrees, then draw the robot”, the last bit being some complex set of “move this much, rotate a bit, draw that” instructions in itself. This is useful because you can encapsulate the “draw the robot” code in one function, and then easily move said robot around just by changing the move/rotate stuff you do before calling that function.

You’ll remember that this current state is stored in a model-view matrix. Given all that, the purpose of the call to:

    mat4.rotate(mvMatrix, degToRad(rTri), [0, 1, 0]);

Is probably pretty obvious; we’re changing our current rotation state as stored in the model-view matrix, rotating by rTri degrees around the vertical axis (which is specified by the vector in the third parameter). This means that when the triangle is drawn, it will be rotated by rTri degrees. Note that mat4.rotate takes angles in radians; personally I find degrees easier to deal with, so I’ve written a simple conversion function degToRad to use here.

Now, what about the calls to mvPushMatrix and mvPopMatrix? As you would expect from the function names, they’re also related to the model-view matrix. Going back to my example of drawing a robot, let’s say your code at the highest level needs to move to point A, draw the robot, then move to some offset from point A and draw a teapot. The code that draws the robot might make all kinds of changes to the model-view matrix; it might start with a body, then move down for the legs, then up for the head, and finish off with the arms. The problem is that if after this you tried to move to your offset, you’d move not relative to point A but instead relative to whatever you last drew, which would mean that if your robot lifted its arms, the teapot would start levitating. Not a good thing.

Obviously what is required is some way of storing the state of the model-view matrix before you start drawing the robot, and restoring it afterwards. This is, of course, what mvPushMatrix and mvPopMatrix do. mvPushMatrix puts the matrix onto a stack, and mvPopMatrix gets rid of the current matrix, takes one from the top of the stack, and restores it. Using a stack means that we can have any number of bits of nested drawing code, each of which manipulates the model-view matrix and then restores it afterwards. So once we’ve finished drawing our rotated triangle, we restore the model-view matrix with mvPopMatrix so that this code:

    mat4.translate(mvMatrix, [3.0, 0.0, 0.0]);

…moves across the scene in an unrotated frame of reference. (If it’s still not clear what this all means, I recommend copying the code and seeing what happens if you remove the push/pop code, then running it again; it will almost certainly “click” pretty quickly.)

So, these three changes make the triangle rotate around the vertical axis through its centre without affecting the square. There are also three similar lines to make the square rotate around the horizontal axis through its centre:

    mvPushMatrix();
    mat4.rotate(mvMatrix, degToRad(rSquare), [1, 0, 0]);

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

    gl.bindBuffer(gl.ARRAY_BUFFER, squareVertexColorBuffer);
    gl.vertexAttribPointer(shaderProgram.vertexColorAttribute, squareVertexColorBuffer.itemSize, gl.FLOAT, false, 0, 0);

    setMatrixUniforms();
    gl.drawArrays(gl.TRIANGLE_STRIP, 0, squareVertexPositionBuffer.numItems);

    mvPopMatrix();
  }

…and that’s all of the changes to the drawing code in drawScene.

Obviously, the other thing we need to do to animate our scene is to change the values of rTri and rSquare over time, so that each time the scene is drawn, it’s slightly different. This, of course, happens in our new animate function, which looks like this:

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

      rTri += (90 * elapsed) / 1000.0;
      rSquare += (75 * elapsed) / 1000.0;
    }
    lastTime = timeNow;
  }

A simple way of animating a scene would be to just rotate our triangle and our square by a fixed amount each time animate was called (which is what the original OpenGL lesson on which this one was based does), but here I’ve chosen to do something I think is slightly better practice; the amount by which we rotate the objects is determined by how long it has been since the function was last called. Specifically, the triangle is rotating by 90 degrees per second, and the square by 75 degrees per second. The nice thing about doing it this way is that everyone sees the same rate of motion in the scene regardless of how fast their machine is; people with slower machines (for whom functions scheduled with requestAnimFrame will be called less frequently) just see jerkier images. This doesn’t matter so much for a simple demo like this, but obviously can be a bigger deal with games and the like.

So, that’s all of the code that actually animates and draws the scene. Let’s look at the supporting code that we had to add, mvPushMatrix and mvPopMatrix:

  var mvMatrix = mat4.create();
  var mvMatrixStack = [];
  var pMatrix = mat4.create();

  function mvPushMatrix() {
    var copy = mat4.create();
    mat4.set(mvMatrix, copy);
    mvMatrixStack.push(copy);
  }

  function mvPopMatrix() {
    if (mvMatrixStack.length == 0) {
      throw "Invalid popMatrix!";
    }
    mvMatrix = mvMatrixStack.pop();
  }

There shouldn’t be anything surprising there. We have a list to hold our stack of matrices, and define push and pop appropriately.

There’s only one new thing left to explain — the degToRad function I mentioned earlier. If you remember anything from your maths at school, it won’t hold any surprises…

    function degToRad(degrees) {
        return degrees * Math.PI / 180;
    }

And… that’s it! There are no more changes to go through. Now you know how to animate simple WebGL scenes. If you have any questions, comments, or corrections, please do leave a comment below.

Next time, (to quote NeHe’s preface to his lesson 5) we’ll “make the object into TRUE 3D object, rather than 2D objects in a 3D world”. Click here to find out how.

<< Lesson 2Lesson 4 >>

Acknowledgments: The code for mvPushMatrix and mvPopMatrix is adapted from Vladimir Vukićević’s spore creature viewer. Thanks also to Google for publishing their very useful webgl-utils.js helper file, and, of course, I’m deeply in debt to NeHe for his OpenGL tutorial for the script for this lesson.

You can leave a response, or trackback from your own site.

83 Responses to “WebGL Lesson 3 – a bit of movement”

  1. mike says:

    Hello,
    Thanks for the great tutorials. I got up and running in no time. I ran your javascripts through JSLint and got some errors. I thought this was important:

    var timeNow = (new Date).getTime();

    should be:
    var timeNow = new Date().getTime();

    All for now. Thanks again!

  2. giles says:

    @mike — glad you like the tutorials! Many thanks for the correction, I don’t know how it slipped through: it’s obviously wrong now that you point it out. I’ll fix it.

  3. Ray says:

    There is only one Colorful Triangle moving, no quad.

  4. giles says:

    @Ray — do you see both the triangle and the square on this page http://learningwebgl.com/bug-repro/singlevertexattr-minrepro.html on the same machine?

    If you can’t, then I think it must be an ATI driver bug I’m trying to work out a fix for.

    If you can see both the triangle and the square on the repro page, but don’t see the moving square for this tutorial, then I’m totally confused!

  5. giles says:

    @Ray — OK, I’ve changed the code in this lesson so that I no longer use the WebGL feature (vertexAttrib4fv) that I think was causing the problem you’re seeing.

  6. ed says:

    I’m a little confused when you write “we used setInterval to make sure that we’d be called every 40 milliseconds; “. Did you mean 15ms ?

  7. giles says:

    @ed — I’m beginning to think I need a proofreader… yes, it should say 15ms and I’ve corrected it now. That was left over from an earlier version of the lesson where I actually did use 40ms.

  8. Jos Hirth says:

    You should stick to one indent style. Since only “hugging brackets” aka 1tbs works in all cases, you should use that one. Always.

    Also note that you shouldn’t put “–” into html comments.

    Excellent work though. I really appreciate your effort.

  9. giles says:

    Jos, thanks for the advice and the kind words! I personally prefer the Allman indentation/brace style (perhaps because I spent a lot of time writing Java before I discovered Python); the reason the variation is that some of the matrix code came from elsewhere and I forgot to reformat all of it — a terrible excuse, I know. I’ll search for “) {” and make it all Allman-style, so that at least it’s consistent :-)

    Oh, and re: the problem with “–” in HTML comments — I didn’t know that, thanks for pointing it out. I’ll fix that too.

  10. Jos Hirth says:

    Heh. That “–” thing should have been “minus minus”. I always forget that Wordpress likes to convert this stuff automatically.

  11. giles says:

    Yup, don’t worry — I figured that was what had happened.

  12. Jos Hirth says:

    http://en.wikipedia.org/wiki/Indent_style#Variant:_1TBS

    That’s the only style which works for JavaScript. You see, the problem with JS is that the semicolons are optional. If something doesn’t make sense, the parser jumps back, adds a semicolon, and tries again.

    So, if you write something like:

    return
    {
    foo: ‘bar’
    };

    It will return undefined!

    Not putting the opening brace in a new line is the only indent style you can *always* use.

    [JavaScript: The Good Parts, Appendix A: Awful Parts, Semicolon Insertion]

  13. giles says:

    That’s a very convincing argument.

    I’d noticed the semicolon-handling — having been coding a lot of Python recently I’d got out of the habit of putting them in, and was rather confused (and a little annoyed) that JavaScript didn’t seem to mind and didn’t warn me. I’ve not been bitten by the problem you show, but it’s probably only a matter of time…

    So, OK: I’ll switch over to 1TBS. It may take a while, though, because I’d like to update the old lessons too.

  14. Jos Hirth says:

    >[JavaScript] didn’t warn me.

    Get Komodo Edit (open source, available for Windows, Mac, and Linux) and the kjslint addon.

    Head over to http://jslint.com/ and get a settings comment like this one:

    /*jslint white: true, onevar: true, undef: true, nomen: true, eqeqeq: true, bitwise: true, regexp: true, newcap: true, immed: true */

    (“The Good Parts” without requiring “use strict” and allowing ++ and –.)

    If you have a comment like that in your files, jslint (or kjslint) will use those options for validation. You can also use kjslint’s options, but with a comment like this it’s a tad nicer for full automation.

  15. giles says:

    Thanks!

  16. giles says:

    Right, lessons 1-5 all now use 1TBS; the remainder should be done by the end of today.

  17. Skofo says:

    First let me say that the way you explain things is -awesome-. I’ve been struggling with O3D for a long time because there aren’t any good tutorials out there for it, but with this one everything just ‘clicks’ in my mind even though I haven’t ever touched low-level graphics programming before.

    I’m dubious about your style of coding, however. You seem to make many functions that you use only once or could be considerably shortened, combined with other functions or taken out altogether. For example, wouldn’t your code be more efficient if you combined the ‘tick’, ‘animate’ and ‘drawScene’ functions together, and simply defining a variable as the current state of the model-view matrix instead of creating a couple functions to store it in a perpetually one-item list?

  18. giles says:

    Skofo — glad you like the explanations!

    Re: the structure of the code: I agree it’s not perfect — I’ve used global variables much more than I would in normal code, for example — but in general this is because I’ve tried to aim for readability and explainability over elegance, and to avoid forcing people into a particular style, be it functional or object-oriented.

    Regarding the functions: I prefer using short functions with well-defined tasks — so, while you’re right that animate() and drawScene() could be merged into one, they actually do quite different things and so IMO they’re better separated out. This also allows you to separate out repainting and animation at a later stage, so that (for example) in a MORPG, animation can be pushed out from a server while repainting is handled locally.

    Regarding the matrices — obviously my explanation there isn’t up to scratch! It’s not a perpetually one-item list, it’s a stack. mvPushMatrix puts a matrix onto the stack, mvPopMatrix restores it. This allows you to call nested functions, each of which can maintain its own model-view matrix state. Is that any clearer?

  19. Skofo says:

    Ahh, that makes perfect sense. Thanks!

  20. oskude says:

    Thanks for these nice tutorials, but why is the CPU usage at 100% in this simple animation example ? (1.8Ghz AMD Sempron / NVIDIA GeForce 6100)

    Or is this only like that on MS-Windows and Firefox 3.7alpha ?
    (couldn’t test Chrome as i only found an installer that doesn’t work with a proxy that needs authentication…)

    Cause when i check the “original” example, http://nehe.gamedev.net/data/lessons/lesson.asp?lesson=04 the objects rotate smooth and CPU usage is at ~5%.

    I’m just a “web monkey” and a hobby coder, but looking at the NeHe (c++) example code, i don’t see anything that says how fast the screen is being updated, i assume as fast as possible ?

    With this WebGL thing (in MS-Windows/Firefox) i needed to go low as setInterval(100) (10FPS) to get CPU usage down to 5%, but then the animation is jerky…

    So where does this massive CPU usage come from ?
    Will the CPU usage ever be as low as in the NeHe (c++) example and still stay smooth ?

    I really hope this is just a temporary “show stopper” ;)

  21. giles says:

    Hi oskude, glad you like the tutorials. I don’t think WebGL is ever going to be quite as efficient as C++, as it’s use JavaScript, but it should get better. The current implementation is in need of optimisation, and I would expect it to be faster by the time it’s finally released.

    On the other hand, there is one worry; because WebGL needs to be “composited” with the other elements in a web page, so that (for example) other HTML stuff can overlay it like it can overlay the 2D canvas, it may take a while to get it up to speed. As I understand it (and I may have the wrong end of the stick here), in order to composite the 3D graphics with the other HTML stuff, after the 3D buffer is generated by the graphics card it is copied back down into main memory, then the composition happens using the CPU rather than the GPU, and then it’s pushed back up to the graphics card. If this really is what is happening, it is obviously a non-optimal process… though it’s hard to see how it could be addressed. Rendering the HTML with Direct2D or an equivalent API could help — you might be able to keep everything on the GPU — though I’m not sure how much work that would involve. Something between “a lot” and “an impossible amount”, I suspect…

  22. Aetas says:

    These are great tutorials, been working through them over the past day and everything’s been explained very well. Thanks ^^.

    The only error I ran into I didn’t even know was an error (Never used JavaScript before) and it was the

    var timeNow = (new Date).getTime();

    line. I didn’t notice that until I saw the comments. You fixed it in the code, just not the lesson. =)

    Thank you again ^^

    On to lesson 4.

  23. giles says:

    “You fixed it in the code, just not the lesson. =)”

    Crikey, so I did! Thanks for pointing that out, I’ve fixed it in the lesson too now :-)

    Glad you like the tutorials, hope the later ones are as useful for you!

  24. Thomas says:

    Great write-up. You’ve structured it really well and I can’t even begin to tell you how incredible useful it is that the code changes only very slightly from lesson to lesson while still introducing and illustrating new concepts gradually.

    It would be nice if you could add an explanation about what the purpose is of the parameter m in mvPushMatrix, and what specifically the method call m.dup() does.

    I’ve found it in Sylvester’s documentation ( http://sylvester.jcoglan.com/api/matrix#dup ) eventually. In hindsight, the name is pretty obvious, I suppose. :p

  25. giles says:

    Thanks, Thomas — glad you found the tutorial useful! I’m considering moving to the MJS or EWGL matrix library instead of Sylvester (they’re both stripped down for WebGL use) but if I don’t, I’ll add an explanation of the unexplained bits in the matrix code.

  26. [...] and see what we can do with just canvas.  So, I took the source code for one of the Learning WebGL examples and used it for my test case.  I was having more problems with it and was almost ready to give [...]

  27. msirin says:

    hi, thank you for these awesome tutorials. seems to be a lot of work..
    tell me if i understood correctly: we define our vertices in the model coordinate system, then translating the coordinate system of our vertex buffer object so that we can see e.g. the triangle. now when we say rotate around the y-axis, it is the y-axis of the above mentioned coordinate system.
    but what to do if i don’t want to turn around the y-axis, instead use an arbitrary one? let’s say the chosen new rotate axis is 2 units right to the y-axis. how would you realize that with webgl?

    regards

  28. giles says:

    Hi msirin,

    Glad you like the tutorials! It sounds like you understand the concepts, and answering your question is just a simple step on from there. Right now, before we draw the triangle, we move 1.5 units to the left, and seven units into the screen, so that we’re positioned at the centre of the place where the triangle will be — that is, our translated Y axis is seven units deeper and 1.5 to left of where it was:

    mvTranslate([-1.5, 0.0, -7.0]);
    

    Next, we rotate around our translated Y axis:

    mvRotate(rTri, [0, 1, 0]);
    

    Now, if what we wanted to do is rotate around a a vertical axis 2 units to the right of the centre of the triangle, what we’d need to do is translate such that the point 2 units to the right was the Y axis, then do the rotate. So a first approximation of what we’d do would be:

    # Move to the centre of the triangle
    mvTranslate([-1.5, 0.0, -7.0]);
    
    # Move two units to the right
    mvTranslate([2.0, 0.0, 0.0]);
    
    # Rotate around the translated Y axis
    mvRotate(rTri, [0, 1, 0]);
    
    # Move back to where the triangle now is
    mvTranslate([2.0, 0.0, 0.0]);
    

    Now, obviously, the first two translates can be combined, like this:

    # Move two units to the right of the centre of the triangle
    mvTranslate([0.5, 0.0, -7.0]);
    
    # Rotate around the translated Y axis
    mvRotate(rTri, [0, 1, 0]);
    
    # Move back to where the triangle now is
    mvTranslate([2.0, 0.0, 0.0]);
    

    …and you’re done!

  29. Ted says:

    Instead, what happens is more that you “tell the 3D system that there’s a square at point X, then next time you draw it, tell the 3D system that it’s at point Y, and then next time that it’s at point Z” and so on.

    You mean WebGL is more stateless than you expected?

  30. giles says:

    @Ted — well, it was OpenGL when I learned about it, but yes — that’s right.

  31. jed says:

    I’m a confused.
    elapsed represent how long time it has been since the function was last called,
    but a fixed during is 15s by setInterval(tick, 15);
    Are they equal elapsed and 15s ?

  32. giles says:

    @jed — they might be! setInterval is a request to be called every 15ms, but there’s no guarantee that tick will be called precisely that frequently. All we can be sure of is that it won’t be called *more* frequently than that.

  33. 80063r says:

    I don’t think you can combine the first two mvTranslate’s into one because of the mvPushMatrix and the second value of 2.0 I think should be negative.

    I think it should be this:

    mvTranslate([-1.5, 0.0, -7.0]);

    mvPushMatrix();

    mvTranslate([2.0, 0.0, 0.0]);

    mvRotate(rTri, [0, 1, 0]);

    mvTranslate([-2.0, 0.0, 0.0]);

    Am I on the right track?

  34. 80063r says:

    Sorry for not specifying what I was referring to. I’m referring to the comments about rotating the triangle on a Y-axis 2 units to the right of the triangle.

  35. deathy/Brainstorm says:

    I’ve been doing 3D code for long enough to be able to tell what was going on there, but it may be worth a line or two about the conversion from angle to radians for rotation, since that’s something that will frequently bite newbs.

    Thanks for the tutes!

  36. giles says:

    @80063er — hmm, I think you may be right there. I guess I was just thinking in terms of doing the triangle alone, not the square too.

    @deathy/Brainstorm — good point, I’ll make a note.

  37. neo says:

    Just as you wanted to let you know about one of the first paragraphs: it could be worth mentioning that there is also a system which let you draw things once and then move them around – it’s svg.

  38. giles says:

    @deathy/Brainstorm — OK, as part of the update to glMatrix I’ve put in something about that. Hope it improves things.

    @neo — interesting idea, I’ll need to read up on SVG animation a bit first though.

  39. Jason says:

    I’m not sure if this is the correct place to ask this, but I’m hoping that someone reading this can help me. I’m trying to take the info graciously provided by Giles and produce a simple 3D game out of it, but I have one major issue. I have not been able to come up with a good boundary checking algorithm. Unlike in a 2D space where is is relatively easy to calculate where all of the relevant points are, in the 3D space I have to worry about perspective altering the relative location of all of the points of my object. So the question that I am asking is, is there either a good way of accessing the precalculated values of the points of lets say a cube or is there a good way of calculating these points myself?

  40. giles says:

    @Jason — I’ve no idea, but perhaps other people reading here might. You could also try asking on the WebGL forums.

  41. Jason says:

    Thanks Giles, I’m posting there now.

  42. Ramon says:

    Check Nehe’s bouncing ball demo

    or if you are lucky like me.
    you can browse through DigiBen’s 3d collision tutorials before Gametutorials gone $$$$$

  43. Nobody says:

    …personally I find degrees easier to deal with, so I’ve written a simple conversion function degToRag to use here….

    should read degToRad not degToRag.

  44. I’ve been following these greats tutorials, I love the do-and-learn approach.
    On this tutorial though I noticed that the degToRad function isn’t added in the text. While it is available in the example.

    Next to that, when opening my FireBug I also get an error:
    attempt to run compile-and-go script on a cleared scope requestAnimFrame(tick);

    Is there any way to work around this in a proper way?

  45. giles says:

    @Nobody, @Martin — thanks for the heads-up about degToRad, I’ve fixed the typo and added something showing it to the text.

    Re: the compile-and-go error — that’s strange, I suspect a Firefox (or FireBug?) error. Does the example work despite it?

  46. Munawwar says:

    var timeNow = new Date().getTime();

    I have seen that in any program (not restricted to JavaScript), constantly polling the OS for system time is a bad idea, since it consumes lot of CPU. In fact it can take up to 99% of the core it runs on.
    That means on a dual core, up to 50% of the total CPU can be taken up by the process, and on an old Pentium 4 that would be 99%. Here’s a demo – http://www.youtube.com/watch?v=0iTArn6es5k.

    Try writing, say a console application in C++ (or any language of your choice), that insanely polls for time, and you would ’see’ what I am talking about.

    There must be a workaround for this.

  47. Munawwar says:

    Tried using setTimeout(tick,1000/60). It gives similar results. CPU stays within a range 25 to 50. Reduce the FPS to 40 and its slightly better, CPU 15-30.
    But it shouldn’t be this way. A normal OpenGL triangle rotate at 60 fps would take 0% on my PC. Something weird going on.

  48. Munawwar says:

    Ah. I take back everything I said about Date(). The problem seems to be something else http://stackoverflow.com/questions/2980959/high-cpu-usage-with-webgl.

  49. giles says:

    @Munawwar — yes, it’s unlikely to be the calls to Date() that’s causing the problem — it might be slow, but 60 times a second is nothing.

    Still, you shouldn’t see lots of CPU usage with this demo; when I run it, it doesn’t go above 1%. Which OS and browser are you using, and what kind of graphics card do you have?

  50. Munawwar says:

    Windows 7, FireFox 4.0.1, NVIDIA Geforce 8400 GS and Intel Core 2 Duo.
    With Chrome 11.0,this example runs at ~30% CPU.

    I just noted that with FF, going to ‘about:config’ and
    setting webgl.prefer-native-gl = true, increases the CPU usage, making this example run at about 30%-45%. I enabled it when I began WebGL (I don’t quite remember why). After setting it back to ‘false’, this example runs at 15-20%.

    I updated my graphic driver today and re-ran your example. Here’s the results: http://www.youtube.com/watch?v=bV26x_PvO_k (And sorry I removed the video in my first post).

Leave a Reply

Subscribe to RSS Feed Follow Learning WebGL on Twitter