WebGL: 2D vs 3D Libraries

This post is a kind of side topic on a series of posts about WebGL. The first started with fundamentals

I’m writing this because my claim that WebGL is a 2D API and not a 3D API touches a nerve with some people. I’m not sure why they feel threatened or whatever it is that makes them so upset I called WebGL a 2D API.

Arguably everything is a matter of perspective. I might say a knife is an eating utensil, someone else might say a knife is a tool and yet another person might say a knife is a weapon.

In the case of WebGL though there’s a reason I think it’s important to not call WebGL a 3D API and that is specifically because of the amount of 3D math knowledge you need to know to use WebGL to draw anything in 3D.

I would argue that anything that calls itself a 3D library should do the 3D parts for you. You should be able to give the library some 3D data, some material parameters, some lights and it should draw 3D for you. WebGL (and OpenGL ES 2.0+) are both used to draw 3D but neither fits this description.

To give an analogy, C++ does not “process words” out of the box. We don’t call C++ a “word processor” even though word processors can be written in C++. Similarly WebGL does not draw 3D graphics out of the box. You can write library that will draw 3D graphics with WebGL but by itself it doesn’t not do 3D graphics.

To give a further example, assume we want to draw a cube in 3d with lights.

Here’s the code in three.js to display this

  // Setup WebGL.
  var c = document.getElementById("c");
  renderer = new THREE.WebGLRenderer();
  renderer.setSize(c.clientWidth, c.clientHeight);

  // Make and setup a camera.
  camera = new THREE.PerspectiveCamera(
      70, c.clientWidth / c.clientHeight, 1, 1000);
  camera.position.z = 400;

  // Make a scene
  scene = new THREE.Scene();

  // Make a cube.
  var geometry = new THREE.CubeGeometry(200, 200, 200);

  // Make a material
  var material = new THREE.MeshPhongMaterial({
    ambient: 0x555555,
    color: 0x555555,
    specular: 0xffffff,
    shininess: 50,
    shading: THREE.SmoothShading

  // Create a mesh based on the geometry and material
  mesh = new THREE.Mesh(geometry, material);

  // Add 2 lights.
  light1 = new THREE.PointLight(0xff0040, 2, 0);
  light1.position.set(200, 100, 300);

  light2 = new THREE.PointLight(0x0040ff, 2, 0);
  light2.position.set(-200, 100, 300);

and here it is displayed.

click here to open in a separate window

Here’s similar code in OpenGL (not ES) to display a cube with 2 lights.

  // Setup
  glViewport(0, 0, width, height);
  gluPerspective(70.0, width / height, 1, 1000);

  glClearColor(0.0, 0.0, 0.0, 0.0);

  // Setup 2 lights
  float light0_position[] = {  200, 100, 300, };
  float light1_position[] = { -200, 100, 300, };
  float light0_color[] = { 1, 0, 0.25, 1, };
  float light1_color[] = { 0, 0.25, 1, 1, };
  glLightfv(GL_LIGHT0, GL_DIFFUSE, light0_color);
  glLightfv(GL_LIGHT1, GL_DIFFUSE, light1_color);
  glLightfv(GL_LIGHT0, GL_POSITION, light0_position);
  glLightfv(GL_LIGHT1, GL_POSITION, light1_position);

  // Draw a cube.
  static int count = 0;

  double angle = count * 0.1;
  glTranslatef(0, 0, -400);
  glRotatef(angle, 0, 1, 0);

  glNormal3f(0, 0, 1);
  glVertex3f(-100, -100, 100);
  glVertex3f( 100, -100, 100);
  glVertex3f(-100,  100, 100);
  glVertex3f(-100,  100, 100);
  glVertex3f( 100, -100, 100);
  glVertex3f( 100,  100, 100);

  ... repeat for 5 more faces of cube


Notice how we need almost no knowledge of 3D math for either of those examples. Compare that to WebGL. I’m not going to write the code required for WebGL. The code is not that much larger. It’s not about the amount of lines required. It’s about the amount of knowledge required. In the two 3D libraries they take care of the 3D. You give them a camera position and field of view, a couple of lights and, a cube. They deal with all the rest. In other words. They are 3D libraries.

In WebGL on the other and you need to know matrix math, normalized coordinates, frustums, cross products, dot products, varying interpolation, lighting specular calculations and all kinds of other stuff that often take months or years to fully understand.

A 3D library’s entire point is to have that knowledge built in so you don’t need that knowledge yourself, you can just rely on the library to handle it for you. This was true the original OpenGL as shown above. It’s true of other 3D libraries like three.js. It is NOT true of OpenGL ES 2.0+ or WebGL.

It seems misleading to call WebGL a 3D library. A user coming to WebGL will think “oh, 3D library. Cool. This will do 3D for me” and then find out the hard way that no, that’s not the case at all.

We can even take it one step further. Here’s drawing 3D wireframe cube in Canvas.

click here to open in a separate window

And here is drawing a wireframe cube in WebGL.

click here to open in a separate window

If you inspect the code you’ll see there’s not a whole lot of difference in terms of the amount of knowledge or for that matter even the code. Ultimately the Canvas version loops over the vertices, does the math WE SUPPLIED and draws some lines in 2D. The WebGL version does the same thing except the math WE SUPPLIED is in GLSL and executed by the GPU.

The point of this last demonstration is to show that effectively WebGL is just a 2D rasterization engine, similar to Canvas 2d. Sure WebGL does have features that help you implement 3D. WebGL has a depth buffer which makes depth sorting far easier than a system without. WebGL also has various math functions built in that are very useful for doing 3D math although there is arguably nothing that makes them 3D. They’re a math library. You use them for math whether or not that math is 1d, 2d, 3d, whatever. But ultimately, WebGL only draws in 2D. You have to provide it with clipspace 2D coordinates that represent what you want drawn. Sure you provide a x,y,z,w and it divides by W before rendering but that’s hardly enough to qualify WebGL as a 3D library. In the 3D libraries you supply 3D data, the libraries take care of calculating 2D points from 3D.

In WebGL/OpenGL ES 2.0 all of the following have been removed.

  • Projection from 3D to clipspace
  • Directional Lights
  • Point Lights
  • Spot Lights
  • Textured Polygons
  • Vertex Colors

Some of you will be thinking you can implement all of those features in WebGL. Yes, you can. And you can implement them in any language with or without WebGL. The point is they don’t happen ‘out of the box’. Unlike real 3D libraries you have to implement them. In old OpenGL you give it lighting info, position, texture coords, vertex colors, and a texture and it would draw a lit, textured, colored object. In WebGL you give it all that data and it will do nothing. You have to the supply the code to calculate the lighting, you have to supply code to project the positions, you have to supply code to look up pixels from textures, you have to supply code to modulate the color you looked up from the texture with the vertex colors and the lighting calculations you made. You’re the one supplying the 3D knowledge, not WebGL.

Some people argue that OpenGL is a 3D library because the docs say it is. I hope you see the fallacy in that logic. Just because I call my dog a ‘cat’ doesn’t mean it’s a cat. Arguably OpenGL is called a 3D library because that’s what it started as. The original fixed function OpenGL is a 3D library as it supplies all the 3D knowledge I mentioned above. All of that knowledge was pulled out for OpenGL ES 2.0 though they just didn’t bother to change the docs. The same is true of DirectX. It started as a 3D library. It wasn’t until DirectX 8 or 9 that all that 3D knowledge was pulled out. Both libraries are now just rasterizing engines and are no longer 3D libraries. The other reason is marketing. It’s much more sexy to call OpenGL a 3D library than an accelerated rasterization library.

I hope you at least understand where I’m coming from when I say WebGL is not a 3D library. I hope you’ll also realize that a 3D library should handle the 3D for you. OpenGL did. Three.js does. OpenGL ES 2.0 and WebGL do not. Therefore they arguably don’t belong in the same broad category of “3D libraries”.

The point of all of this is to give a developer that is new to WebGL an understanding of WebGL at its core. Knowing that WebGL is not a 3D library and that they have to provide all the knowledge themselves lets them know what’s next for them and whether they want to pursue that 3D math knowledge or instead choose a 3D library to handle it for them. It also removes much of the mystery of how it works.

  • Everton Marques

    Good point.

  • Q-Guy

    Your argument that OpenGL is a “3D library” and OpenGL ES 2.0 is not, is flawed. Here’s why:

    Your OpenGL snippet uses GLUT, which hides the math of calculating the projection matrix. Furthermore your code snippet only uses the fixed function pipeline. As soon as you use the programmable piple, your OpenGL code is practically the same as ES 2.0.

    Whether you call it a “3D library” or not is up to you, but OpenGL and OpenGL ES 2.0 definitely belong in the same broad category.

  • greggman

    We’ll just have to disagree then. I know hundreds of programmers that can use the fixed function pipeline that can’t use shaders. Why, because shaders require 3D math knowledge that the fixed function stuff used provide for you. IMO that makes the fixed function GL a 3D library and the shader GL not. ES and WebGL have no fixed function pipeline. Therefore you need 3D knowledge. Therefore they aren’t 3D libraries. Libraries should provide the knowledge suggested by their name.

  • Q-Guy

    The fixed function pipleline has been removed in OpenGL 3.1. It is also no longer available in DirectX10 and 11.

    Again, your OpenGL snippet uses GLUT, without that extra lib, you would need to do the same math in OpenGL, no matter if it’s fixed function or shaders. So OpenGL does as much 3D math for you as OpenGL ES, except for fixed function transformation of vertices and per vertex lighting, which will make you application look like it’s from 2003. So I pity the hundreds of programmers you know that do not understand simple linear algebra. They can never program a sophisticated 3D application without that knowledge, and should therefore stick to real 3D middleware libraries, and leave OpenGL programming to dedicated professionals.

  • greggman

    Yep, Which is exactly the point. If you don’t know 3D use a 3D library. If you do then by all means do it yourself. If you don’t know 3D and someone tells you WebGL or OpenGL ES 2.0 is a 3D library you’ll have been sorely mislead.

  • Q-Guy

    Yes, I agree. But the same is true for OpenGL, not just ES 2.0

  • greggman

    I don’t agree. I can use OpenGL fixed function pipeline with zero 3d knowledge. That I need to call gluPerspective isn’t 3d knowledge since I don’t have to know what happens inside that function, only that I need to call it. Similarly I can draw objects by handing the API 3D data, giving it 3D positions, turning on flags for 3D lights. I need no knowledge of 3D math, perspective projection, lighting equations, etc to do this. Contrast to a shader only API where I need all that knowledge.

  • Q-Guy

    gluPerspective is a part of GLUT, not OpenGL! E.g. with ES 2.0 you could use GLM (OpenGL Mathematics lib) to avoid the math behind the projection matrix. Therefore you can use ES 2.0 with “zero 3d knowledge” in the same sense as OpenGL, thus proving my point that OpenGL and OpenGL ES 2.0 are in the same category.

  • greggman

    We’ll just have to agree to disagree. To me it doesn’t matter that gluPerspective is part of glut and not OpenGL. It’s included with every OpenGL. It’s used in nearly every OpenGL sample. It requires no knowledge of what it actually does to use it.

    That’s not true in shader land where you have no know what it does since you have to write the shaders to use it.

  • Q-Guy

    In that vein of arguing, 4,5,6,7 are irrelevant, because just like people use gluPerspective without understanding it, people use shaders without understanding them. Same thing.

    You are calling OpenGL ES 2.0 a 2D API, yet it does not draw 2D out of the box, so your “word processor” analogy does not add up.

    So I agree to disagree. The important thing is that fixed function OpenGL and programmable pipeline OpenGL DO belong in the “same broad category”. After several replies you still could not coherently explain why they don’t.

  • greggman

    I’ve fully explained it. Sorry it’s over your head

  • Q-Guy

    And I disproved all your arguments. Sorry that’s over your head.

    If you cannot comprehend what I’m talking about, here’s some final words: the Khronos Groups, which is the consortium that created WebGL calls it a “web standard for a low-level 3D graphics API based on OpenGL ES 2.0″. Go ahead and keep calling it a 2D-only API, while graphics programmers all over the world keep laughing at you.

    You’re obviously a beginner to OpenGL, your twisted view on fixed function vs programmable pipeline in regard to required math knowledge will straighten out, once you’ve written something that exceeds the level of a OpenGL tutorial sample. Good luck in learning 3D math, you’ll need it.

  • greggman

    Wow, You should probably do a little research before you stick your foot in your mouth. I implement WebGL in Chrome. I’m on the Khronos WebGL Working Group. I helped write the WebGL spec. I wrote 90% of the WebGL Conformance Tests. So no, I’m not a beginner to WebGL. I’m one of the principle architects of it.

    No graphics programmers are laughing at me. In fact these ideas were used in a talk by the implementors of WebGL for Firefox because they help make it clear how much extra knowledge is needed to use WebGL from scratch and why if you’re just getting started it’s often better to use a 3D library like Three.js rather than try to write your own 3D library on top of the non-3d WebGL.

    I’m sorry if you’re having trouble seeing the difference. Compare an OpenGL 1.1 book to a OpenGL ES 2.0 book. The 1.1 book will have lit cube on the screen in a couple of chapters. The ES 2.0 book will take 7 or 8 more chapters to get to that part because so much 3D knowledge that was encapsulated in the fixed function features is not in ES 2.0+

  • Spongman

    *sound of crickets*

    Nice article.