WebGL Lesson 4 – some real 3D objects

<< Lesson 3Lesson 5 >>

Welcome to my number four in my series of WebGL tutorials. This time we’re going to display some 3D objects. The lesson is based on number 5 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, second, or third tutorials already, you should probably do so before reading this one — here I will only explain the differences between the code for lesson 3 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.

The differences between the code for this lesson and the previous one are entirely concentrated in the animate, the initBuffers, and the drawScene functions. If you scroll down to animate now, you’ll see one first, very minor change: the variables that remember the current rotation state of the two objects in the scene have been renamed; they used to be rTri and rSquare. We’ve also reversed the direction of spin of the cube (just because it looks prettier), so now we have:

      rPyramid += (90 * elapsed) / 1000.0;
      rCube -= (75 * elapsed) / 1000.0;
 

That’s all for that function; let’s move up to drawScene. Just above the function declaration, we have definitions for the new variables:

  var rPyramid = 0;
  var rCube = 0;

Next comes the function header, followed by our setup code and the code to move into position for drawing the pyramid. Once that’s all done, we rotate it about the Y axis just as we did for the triangle in the previous lesson:

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

…and then we draw it. The only difference between the code in the last lesson that drew the colourful triangle and the new code to draw our equally-pretty pyramid is that there are more vertices, and equally more colours, so that will all be handled in initBuffers (which we’ll look at in a moment). This means that apart from a change in the names of the buffers we use, the code is identical:

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

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

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

Right, that was easy. Let’s look at the code for the cube. The first step is to rotate it; this time, instead of just rotating on the X axis, we’ll rotate around an axis that is (from the perspective of the viewer) upwards, to the right, and towards you:

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

Next, we draw the cube. This is a little more involved. There are three ways we can draw a cube:

  1. Use a single triangle strip. If the whole cube was one colour, this would be reasonably easy — we could use the vertex positions we’ve been using until now to draw a front face, then add another two points to add another face, and another two for another, and so on. This would be very efficient. Unfortunately, we want every face to have a different colour. Because each vertex specifies a corner of the cube, and each corner is shared between three faces, we’d need to specify each vertex three times, and doing this would be so tricky that I won’t even try to explain it…
  2. We could cheat, and draw our cube by drawing six separate squares, one for each face, with separate sets of vertex positions and colours for each. The first version of this lesson (prior to 30 October 2009) actually did this, and it worked just fine. However, it wouldn’t be good practice; because it costs a certain amount in terms of time every time you tell WebGL to draw another object in your scene, it’s much better to have a minimum number of calls to drawArrays.
  3. The final option is to specify the cube as six squares, each made up of of two triangles, but to send that all over to WebGL to be drawn in one go. This is similar to the way we would have done it with a triangle strip, but because we’re specifying the triangles in their entirety each time rather than simply defining each triangle by adding a single point on to the previous one, it’s easier to specify the per-side colours. It also has the advantage that the neatest way to code it lets me introduce a new function, drawElements — so it’s the way we’re going to do it :-)

The first step is to associate the buffers containing the cube’s vertex positions and colours that we’ll be creating in initBuffers with the appropriate attributes, just as we did with the pyramid:

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

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

The next step is to draw the triangles. There’s a bit of a problem here. Let’s consider the front face; we have four vertex positions for it, and each of them has an associated colour. However, it needs to be drawn using two triangles, and because we’re using simple triangles, which need their own vertices specified individually, rather than triangle strips, which can share vertices, we have to specify six vertices in total for it. But we only have four for it in our array buffer.

What we want to do is specify something like “draw a triangle made up of the first three vertices in the array buffer, then draw another made out of the first one, the third, and the fourth”. This would draw our front face; drawing the rest of the cube would be similar. And this is exactly what we do.

We use something called an element array buffer and a new call, drawElements, for this. Just like the array buffers we’ve been using until now, the element array buffer will be populated with appropriate values in initBuffers, and it will hold a list of vertices using a zero-based index into the arrays we used for the positions and the colours; we’ll take a look at that in a moment.

In order to use it, we make our cube’s element array buffer the current one (WebGL keeps different current array buffers and element array buffers, so we must specify which one we’re binding in the call to gl.bindBuffer), then we do the normal code to push our model-view and projection matrices up to the graphics card, then and call drawElements to draw the triangles:

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

That’s it for drawScene. The remainder of the code is in initBuffers, and is pretty obvious. We define buffers with new names to reflect the new kinds of objects we’re dealing with, and we add a new one in for the cube’s vertex index buffer:

  var pyramidVertexPositionBuffer;
  var pyramidVertexColorBuffer;
  var cubeVertexPositionBuffer;
  var cubeVertexColorBuffer;
  var cubeVertexIndexBuffer;

We put values in the pyramid’s vertex position buffer for all of the faces, with an appropriate change to the numItems:

    pyramidVertexPositionBuffer = gl.createBuffer();
    gl.bindBuffer(gl.ARRAY_BUFFER, pyramidVertexPositionBuffer);
    var vertices = [
        // Front face
         0.0,  1.0,  0.0,
        -1.0, -1.0,  1.0,
         1.0, -1.0,  1.0,
        // Right face
         0.0,  1.0,  0.0,
         1.0, -1.0,  1.0,
         1.0, -1.0, -1.0,
        // Back face
         0.0,  1.0,  0.0,
         1.0, -1.0, -1.0,
        -1.0, -1.0, -1.0,
        // Left face
         0.0,  1.0,  0.0,
        -1.0, -1.0, -1.0,
        -1.0, -1.0,  1.0
    ];
    gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);
    pyramidVertexPositionBuffer.itemSize = 3;
    pyramidVertexPositionBuffer.numItems = 12;

…likewise for the pyramid’s vertex colour buffer:

    pyramidVertexColorBuffer = gl.createBuffer();
    gl.bindBuffer(gl.ARRAY_BUFFER, pyramidVertexColorBuffer);
    var colors = [
        // Front face
        1.0, 0.0, 0.0, 1.0,
        0.0, 1.0, 0.0, 1.0,
        0.0, 0.0, 1.0, 1.0,
        // Right face
        1.0, 0.0, 0.0, 1.0,
        0.0, 0.0, 1.0, 1.0,
        0.0, 1.0, 0.0, 1.0,
        // Back face
        1.0, 0.0, 0.0, 1.0,
        0.0, 1.0, 0.0, 1.0,
        0.0, 0.0, 1.0, 1.0,
        // Left face
        1.0, 0.0, 0.0, 1.0,
        0.0, 0.0, 1.0, 1.0,
        0.0, 1.0, 0.0, 1.0
    ];
    gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(colors), gl.STATIC_DRAW);
    pyramidVertexColorBuffer.itemSize = 4;
    pyramidVertexColorBuffer.numItems = 12;

…and for the cube’s vertex position buffer:

    cubeVertexPositionBuffer = gl.createBuffer();
    gl.bindBuffer(gl.ARRAY_BUFFER, cubeVertexPositionBuffer);
    vertices = [
      // Front face
      -1.0, -1.0,  1.0,
       1.0, -1.0,  1.0,
       1.0,  1.0,  1.0,
      -1.0,  1.0,  1.0,

      // Back face
      -1.0, -1.0, -1.0,
      -1.0,  1.0, -1.0,
       1.0,  1.0, -1.0,
       1.0, -1.0, -1.0,

      // Top face
      -1.0,  1.0, -1.0,
      -1.0,  1.0,  1.0,
       1.0,  1.0,  1.0,
       1.0,  1.0, -1.0,

      // Bottom face
      -1.0, -1.0, -1.0,
       1.0, -1.0, -1.0,
       1.0, -1.0,  1.0,
      -1.0, -1.0,  1.0,

      // Right face
       1.0, -1.0, -1.0,
       1.0,  1.0, -1.0,
       1.0,  1.0,  1.0,
       1.0, -1.0,  1.0,

      // Left face
      -1.0, -1.0, -1.0,
      -1.0, -1.0,  1.0,
      -1.0,  1.0,  1.0,
      -1.0,  1.0, -1.0,
    ];
    gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);
    cubeVertexPositionBuffer.itemSize = 3;
    cubeVertexPositionBuffer.numItems = 24;

The colour buffer is marginally more complex, because we use a loop to create a list of vertex colours so that we don’t have to specify each colour four times, one for each vertex:

    cubeVertexColorBuffer = gl.createBuffer();
    gl.bindBuffer(gl.ARRAY_BUFFER, cubeVertexColorBuffer);
    colors = [
      [1.0, 0.0, 0.0, 1.0],     // Front face
      [1.0, 1.0, 0.0, 1.0],     // Back face
      [0.0, 1.0, 0.0, 1.0],     // Top face
      [1.0, 0.5, 0.5, 1.0],     // Bottom face
      [1.0, 0.0, 1.0, 1.0],     // Right face
      [0.0, 0.0, 1.0, 1.0],     // Left face
    ];
    var unpackedColors = [];
    for (var i in colors) {
      var color = colors[i];
      for (var j=0; j < 4; j++) {
        unpackedColors = unpackedColors.concat(color);
      }
    }
    gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(unpackedColors), gl.STATIC_DRAW);
    cubeVertexColorBuffer.itemSize = 4;
    cubeVertexColorBuffer.numItems = 24;

Finally, we define the element array buffer (note again the difference in the first parameter to gl.bindBuffer and gl.bufferData):

    cubeVertexIndexBuffer = gl.createBuffer();
    gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, cubeVertexIndexBuffer);
    var cubeVertexIndices = [
      0, 1, 2,      0, 2, 3,    // Front face
      4, 5, 6,      4, 6, 7,    // Back face
      8, 9, 10,     8, 10, 11,  // Top face
      12, 13, 14,   12, 14, 15, // Bottom face
      16, 17, 18,   16, 18, 19, // Right face
      20, 21, 22,   20, 22, 23  // Left face
    ]
    gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(cubeVertexIndices), gl.STATIC_DRAW);
    cubeVertexIndexBuffer.itemSize = 1;
    cubeVertexIndexBuffer.numItems = 36;

Remember, each number in this buffer is an index into the vertex position and colour buffers. So, the first line, combines with the instruction to draw triangles in drawScene, means that we get a triangle using vertices 0, 1, and 2, and then another using 0, 2 and 3. Because both triangles are the same colour and they are adjacent, the result is a square using vertices 0, 1, 2 and 3. Repeat for all faces of the cube, and you're done!

Now you know how to make WebGL scenes using 3D objects, and you know how to re-use the vertices you've specified in array buffers by using element array buffers and drawElements. If you have any questions, comments, or corrections, please do leave a comment below.

Next time, we'll go over texture mapping.

<< Lesson 3Lesson 5 >>

Acknowledgments: As always, I'm deeply in debt to NeHe for his OpenGL tutorial for the script for this lesson. Chris Marrin's WebKit spinning box was the inspiration for adapting this lesson to introduce element array buffers.

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

70 Responses to “WebGL Lesson 4 – some real 3D objects”

  1. Jeff says:

    First off, just found this site recently and love the tutorials/demos. My question might be a bit off topic, but I thought this would be the best lesson to comment the question under.

    How does this relate to mesh normals, and the difference values that can be exported out of a blender model, specifically blenders DirectX(.x) format? And what about animation of only certain vertexes, like the vertexes of an arm or leg?

    Seems to me that does something like that should follow this same ideas you present here, but I feel like that makes it incredibly more complex. Maybe there is just a lot more to it than I am thinking.

    Any input on that would be great! Thanks!

  2. giles says:

    Hi Jeff, glad you like the tutorials!

    You specify vertex normals just like you do their positions, as vertex attributes; similarly with the texture coordinates. You can see both of those in action in lesson 7. There’s work underway to provide a Blender “export to JSON” module, in which you’ll (eventually) be able to select which attributes you export.

    Animating certain vertices individually isn’t something I’ve covered in the tutorials yet, and it might be a while before I do. It does indeed make things quite a lot more complex…

  3. Oliver Vornberger says:

    Hi Giles,

    first of all: these are excellent tutorials. Thumb up !

    Question: Instead of storing 24 vertices in the CubeVertexPositionBuffer, would it be possible to store just 8 vertices and define the resulting 6 faces by listing the appropriate indices in cubeVertexIndexBuffer ?

  4. giles says:

    Hi Oliver — glad you like the tutorials! You could do that, but then they’d all have to be the same colour. Each vertex is a bundle of attributes — in this case a location and a colour, but in more complex models it might be location, colour, texture coordinates, and many other things.

  5. moo says:

    Don’t forget to change the animate() function!!
    rTri becomes rPyramid etc.

  6. giles says:

    Thanks, well spotted! I’ve updated the text appropriately.

  7. Phinehas says:

    I have one idea on generate the pyramid but I encounter some problem when implement it. Can you help me to solve the problem?

    I can draw a triangle to the screen first. Then, rotate the screen 90 degree and draw the triangle again. Using this method to generate four identical triangle can form a pyramid. When I implement it, I find that only one triangle can be drawn in the screen. How can I rotate the screen and place the same triangle correctly?

    for (var j = 0; j < n; j++) {
    mvPushMatrix();
    mvRotate(1, [0, 1, 0]);
    setMatrixUniforms();
    gl.drawElements(gl.TRIANGLES, pyramidVertexIndexBuffer.numItems, gl.UNSIGNED_SHORT, 0);
    mvPopMatrix();

  8. giles says:

    @Phinehas — I’m not sure if it will help, but there are two oddities in the code you pasted:

    1. You’re rotating by 1 degree rather than 90
    2. In your loop, you’re pushing the matrix in each iteration and popping it at the end, which means that you’re rotating 1 degree, drawing a triangle, going back to where you were, rotating 1 degree, drawing the triangle, going back to where you were again, and so on.

    So, if you set n to 4, pass “90″ in as the first parameter in your call to mvRotate, and remove the mvPushMatrix and mvPopMatrix calls, you may get better results.

  9. abresas says:

    Two notices:
    * You redeclare var colors and
    * the declaration of var unpackedcolors has a missing semicolon.
    Thank you for the otherwise decent javascript code.

  10. WarrenFaith says:

    I enjoyed the first 3 lessons, but here you start to jump to often between pyramid and cube changes. Maybe its because I’m not a native speaker.

    Anyway I am doing my bachelor thesis about WebGL (making a simple Benchmark and testing all possible browser and their performance).

    Your lessons help me on that way!

  11. giles says:

    @abresas — thanks! I’ll fix that. [UPDATE: fixed]

    @Warren — glad you’re finding the lessons useful. Which bit in particular did you find confusing here?

  12. Vik says:

    @Giles- these tutorials are using points or vertices within the code. now say if I want to use the vertices from an external source say an .obj file, is it possible in JavaScript and also what if .obj file has 3D model’s mesh information (like vertex normals, faces, etc.), can we use that information or just the vertices position? Any ideas???

    Thanks
    Vik

  13. giles says:

    Hi Vik,

    You’d have to convert the .obj file to a different format yourself. The laptop I use in lesson 16 is one I downloaded as an obj file, then converted to a JSON format using a hacked-up copy of some code written by the WebKit team for one of their demos — if you check out the GitHub repository for the lessons then you can see the hacked-up copy, though I don’t recommend you use exactly that :-)

    There are some instructions on exporting to JSON from Blender (and Blender can import obj files) here in Polish and English; they look good, but I’ve not had a chance to try them out.

    Cheers,

    Giles

  14. Owen Densmore says:

    Great, as usual. One minor improvement using the glMatrix package:

    function mvPushMatrix() {
    mvMatrixStack.push(mat4.create(mvMatrix));
    }

    .. i.e. the create can also to the copy.

  15. Owen Densmore says:

    Oops, I meant the above to be on lesson 3.

    I LOVE the highlighting of the new/changed code!

    You could make the 1,1,1 vec red, btw:
    mat4.rotate(mvMatrix, degToRad(rCube), [1, 1, 1]);

    Thanks again,

    — Owen

  16. Marc says:

    Thanks a lot for putting in the time on these tutorials. I’m a programmer with zero opengl (or any gl for that matter) experience, and until I found this site, it was very difficult to learn. The other resources, and the spec, give explanations that require you to already know a lot of terminology, and you do a great job of explaining all that along the way. When webgl hits the big time, I’ll be ready.

  17. Rich Conlan says:

    Great tutorials! But the pyramid has no bottom!

    This upset me so I added,

    // Base-1
    1.0, -1.0, 1.0,
    -1.0, -1.0, 1.0,
    -1.0, -1.0, -1.0,

    // Base-2
    1.0, -1.0, 1.0,
    1.0, -1.0, -1.0,
    -1.0, -1.0, -1.0,

    pyramidVertexPositionBuffer.itemSize = 3;
    pyramidVertexPositionBuffer.numItems = 18;

    // Base-1
    0.0, 0.0, 1.0, 1.0,
    0.0, 1.0, 0.0, 1.0,
    0.0, 0.0, 1.0, 1.0,

    // Base-2
    0.0, 0.0, 1.0, 1.0,
    0.0, 1.0, 0.0, 1.0,
    0.0, 0.0, 1.0, 1.0,

    pyramidVertexColorBuffer.itemSize = 4;
    pyramidVertexColorBuffer.numItems = 18;

    and changed the rotation to,

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

    so that the shiny new bottom is visible.

    Was there a nicer way to achieve this?

  18. David says:

    These are great tutorials, and really give a good grounding in WebGL. Thank you so much.

    The way I’m working through is to copy and paste the lines from the tutorial into a local file adding comments based on my understanding of your explanations.
    That’s working great for me, and makes sure I understand things (or at least think that I do :)

    Couple of comments for this lesson (and one that applies to lessons 3 & 4)

    1. in drawScene()
    mat4.rotate(mvMatrix, degToRad(rCube), [1, 1, 1]);

    Highlight the changed code “rCube” and “[1, 1, 1]”

    2. in drawScene()
    Wasn’t initially sure if the
    gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, cubeVertexIndexBuffer);
    replaced
    gl.bindBuffer(gl.ARRAY_BUFFER, cubeVertexPositionBuffer);

    Of course after thinking about it, I realise the GPU has to get hold of the actual vertices, but wasn’t initially clear from the text.

    3. in initBuffers()
    Defining the cubes vertices:
    The last vertex has the trailing comma …

    -1.0, 1.0, -1.0,
    ];

    4. I also had a few issues around which support code to use for webgl-utils.js and glMatrix.js since I had a “version” of these files.
    I got from google,
    http://code.google.com/p/glmatrix/source/browse/glMatrix.js

    For webgl-utils.js, in my version I had to mess around with the
    requestAnimFrame()

    using instead
    WebGLUtils.requestAnimationFrame(canvas, tick);

    and had a slightly different initialisation function, and had to make canvas a global (well didn’t have to but it was simplest)

    Thanks once again for the great work.

  19. Vik says:

    @Giles – I am trying to read an .obj file and converting it to JSON format and then trying to use its information in my code after parsing JSON file. Now, I am not able to see any errors but it shows nothing in the canvas, just the black box. I was wondering if there is any way I could send my Javascript code to you so that you can simply see if I ma doing anything wrong here or not. I would really appreciate your help in this matter as I am really stuck with this and I need to get done with it real soon.

    So, please suggest me what should I do. I would really appreciate that.

    Thanks
    Vik

  20. Jarav says:

    cubeVertexColorBuffer.numItems and cubeVertexPositionBuffer.numItems are not used anywhere.

  21. giles says:

    @Owen — thanks! I’ll make that change to the matrix code, and I’ve made the rotation vector red as you suggested.

    @Marc — glad you’re finding this stuff useful :-)

    @David — that looks fine to me.

    @Vik — sure, just email me and I’ll take a look — the address is on the “about” page.

    @Jarav — yes, they’re just there for completeness.

  22. Vik says:

    @Giles- thanks. I have already sent it to [email protected] Please have a look and let me know what is wrong in my attempt. I really appreciate your help.

    Thanks
    Vik

  23. Vik says:

    @Giles- hey Giles, I just want to follow up if you were able to find some time to look at the code that I sent you?

    Hoping for your valuable help and suggestions.

    Thanks
    Vik

  24. giles says:

    Hi Vik — I’ve received it but have been completely swamped with both work and home life, sorry. I’ll take a look as soon as I can.

  25. Vik says:

    @Giles- hey Giles, did you get a chance to look at it? Actually, I am really in a kind of hurry. I am sorry I don’t want to push you or anything but please do have a look at it as soon as possible. I would really appreciate your help.

    Thanks
    Vik

  26. Julio says:

    hi Giles and everyone xD,

    have you try to do a cylinder and/or a cone? I’m trying to do the cone like the sphere here but I can’t get the right slope for the cone, just the first part. It looks like the flame of a candle xD
    And have not try the cylinder yet.

    Any suggestions? Thx :)

    Regards,
    Julio

  27. Amit Agarwal says:

    Hi Giles,

    I created multiple cubes kept side by side and they are all in different solid colors. I strongly feel the need of having a black border differentiating between each surface to make it look more precise. How to add a black border for a surface?

  28. giles says:

    @Julio — sure, that should be easy enough — if you post the code here I’ll try to see if there’s anything obviously wrong ;-)

    @Amit — that’s actually surprisingly tricky to do. The best suggestion I’ve heard is to use a texture with a border around the edge, and that seems kind of unsatisfactory to me. Perhaps you could do it with shaders — say, you have texture coordinates going from 0, 0 to 1, 1 across the faces of teh cube (check out lesson 5 if that doesn’t make sense) and then in the fragment shader make the pixel black if either texture coordinate is in the range (0, 0.01) or (0.99, 1)?

  29. Web GL is new feature to me and i am really interested to learn it.. i just google to and found learningwebgl.com.. i think its really treasure for me. Great work guy…. I hope next tutorial must be with better graphic….

    Thanks Web GL
    regard
    Alizain

  30. Will says:

    Thanks for all the great lessons man, really helping me to get my head around WebGL.

    Is it me or does the sidebar cut off the end of some lines of code in parts? I’m trying to copy and paste as little as possible so that I have to make my lazy lazy brain work. :)

  31. giles says:

    @Alizain — thanks!

    @Will — thanks too! The thing with the sidebar cutting off some of the code happens for me too, perhaps I need to find a new template for this blog…

  32. Amit Agarwal says:

    Hi Giles,

    I have a confusion in the above lesson. Which line of code makes a connection between cubeVertexIndexBuffer and cubeVertexPositionBuffer? Or in other words, how does the code knows that 0,1,2 in cubeVertexIndexBuffer points to first, second and third point in cubeVertexPositionBuffer ?

  33. giles says:

    Hi Amit,

    The itemSize attribute that we attach to the cubeVertexPositionBuffer is set to three, and when we bind it to the shaderProgram.vertexPositionAttribute using gl.vertexAttribPointer, we pass the itemSize in. That means that the shaderProgram.vertexPositionAttribute knows that its contents are made up of groups of three numbers, one-per-vertex. Then the cubeVertexIndexBuffer indexes these vertices.

    Is that any clearer?

    Cheers,

    Giles

  34. Autumn says:

    Hi Giles,
    I am going through the tutorial and trying to implement my own geometry. It is more complex geometry, and some of the faces are len 4 some are len 3. Do I need to convert the geometry to all triangles or quads?? From what I researched the answer would be ‘yes’ but I thought you may have better insight. THX!

  35. giles says:

    Hi Autumn,

    Your research is right :-)

    Cheers,

    Giles

  36. merve says:

    hi,
    what does the cubeVertexindexbuffer do?
    i did not understand
    thanks

  37. jlabanca says:

    Awesome, 3D objects demystified.

    In the animate() function, you wrote that you only changed the variable name, but you also changed the sign of rCube. Still works fine, except you might scratch your head as to why the cube rotates the opposite direction.
    rCube -= (75 * elapsed) / 1000.0;
    versus
    rSquare += (75 * elapsed) / 1000.0;

  38. giles says:

    @merve — it tells WebGL which vertices to draw, in what order.

    @jlabanca — good point, thanks! I’ll correct that.

  39. andreas says:

    Great tutorials.

    I also did an implementation of the Pyramid with draw elements. Could you comment a little bit on when it is better to use the one or the other ? DrawArrays vs DrawElements. Performance, Convenience etc.

  40. George says:

    Congratulations!! You have made a great job. I am new to WegGL (and to OpenGL generally) but you have made the things quite clear. Thank you very much!

  41. Jason Slemons says:

    “That’s if for drawScene” should be “thats it for drawScene” i think ;-) great tutorials btw.

  42. giles says:

    @andreas, @George — sorry for the slow response, looks like I missed the comments for a bit! Glad you like the tutorials.

    @andreas — drawArrays is useful if you have a bunch of vertex attributes already set up in the right order — it just draws every vertex in the array. drawElements is more useful if you want to draw a subset of the vertices, or if you want to draw them in a different order. Hopefully that answers your questions!

    @Jason Slemans — thanks for the heads up, I’ll fix that.

  43. Stef says:

    Hi! first of all great lessons!

    second, if I want a wireframe model I could use LINE_STRIP in drawElements. But is there an easy way to hide the back side of the model? I mean I could calculate every polygon and see if its in the back or not, but I’m wondering if there’s an easy way.

    Thanks

  44. giles says:

    @Stef — glad you like the lessons! Re: your question — doing wireframe with hidden-line-removal (ironically, given that it’s normally regarded as a more primitive form of 3D than shaded stuff) is actually quite hard in WebGL — I’ve never seen a good way of doing it. (I have seen a lot of bad ways — using a black texture with green lines around the outside, for example — but I don’t suggest trying that :-)

  45. [...] a mostrar auténticos objetos en 3D.  Es una traducción no literal de su respectivo tutorial en Learning webGL, que a su vez está basada en el capítulo 5 del tutorial sobre openGL de [...]

  46. reg says:

    I am new to webGL. Why is there no index buffer for the pyramid?

  47. Sergejack says:

    Any though about the performance difference when using TRIANGLE_STRIP instead of TRIANGLES (+ ELEMENTS) to draw a cube?

    @reg because the cube is made of 6 square (which are 2 triangles each) thanks to the ELEMENT_ARRAY_BUFFER while the (bottomless) pyramid is directly made of 4 triangles using ARRAY_BUFFER. And elements are defined with indexes.

  48. [...] 第四课的原文:http://learningwebgl.com/blog/?p=370 [...]

  49. Thomas says:

    Hey, awesome tutorials, there’s a little error though, for the cube’s color data, you use this line:
    gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(colors), gl.STATIC_DRAW);

    Which should be (and is correct in the live demo)
    gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(unpackedColors), gl.STATIC_DRAW);

  50. Scott says:

    Thanks – great stuff.

    One question: You have quite a few repeated points in the cube vertex array. Couldn’t you just use 8 points mapped appropriately in the index buffer?

Leave a Reply

Subscribe to RSS Feed Follow Learning WebGL on Twitter