Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does this state that WebGL is a 2D API, not a 3D API?

According to HTML5 Rocks, WebGL is actually a 2D API, not a 3D API. Why do they say that, and what does it mean?

We can specify X, Y, Z coordinates in WebGL vertex shaders and fragment shaders. I can't understand the difference between a 2D and 3D graphics API. Could you explain why they say this is a 2D API?

like image 996
Saravana Kumar Avatar asked Oct 30 '12 08:10

Saravana Kumar


People also ask

Is WebGL a 3D?

WebGL (Web Graphics Library) is a JavaScript API for rendering high-performance interactive 3D and 2D graphics within any compatible web browser without the use of plug-ins. WebGL does so by introducing an API that closely conforms to OpenGL ES 2.0 that can be used in HTML <canvas> elements.

Is WebGL the same as three Js?

Three. js is a JavaScript library under MIT license that works right above WebGL. The library's goal is to drastically simplify the process of handling all of what we just stated. You'll have an animated 3D scene in just a few code lines, and you won't have to provide shaders and matrices.

Is WebGL an API?

WebGL is a graphics application programming interface (API) created for use in web applications. It is based off the open graphics language (OpenGL) embedded standard (ES).

What is WebGL format?

What is WebGL? WebGL is an open web standard for rendering graphics on any compatible browser, without the need for additional plug-ins. WebGL is integrated completely into all the web standards of the browser allowing GPU accelerated usage of image processing and effects as part of the web page canvas.


2 Answers

WebGL is a rasteration API not a 3D api. You have to provide it with projected coordinates. This many ways it is no different than Canvas. It's just faster. Let's compare.

Here's 3D in Canvas

const cubeVertices = [
  -1, -1, -1,
   1, -1, -1,
   1,  1, -1,
  -1,  1, -1,
  -1, -1,  1,
   1, -1,  1,
   1,  1,  1,
  -1,  1,  1,
];
const indices = [
  0, 1,
  1, 2,
  2, 3,
  3, 0,
  4, 5,
  5, 6,
  6, 7,
  7, 4,
  0, 4,
  1, 5,
  2, 6,
  3, 7,
];

const canvas = document.querySelector("#c");
const ctx = canvas.getContext("2d");

function render(time) {
  time *= 0.001;

  const scale = 2;

  ctx.clearRect(0, 0, canvas.width, canvas.height);
  ctx.save();
  ctx.translate(canvas.width / 2, canvas.height / 2);
  ctx.scale(canvas.width / scale, -canvas.height / scale);
  ctx.lineWidth = scale / canvas.width;
  ctx.strokeStyle = "black";

  const fieldOfView = Math.PI * 0.25;
  const aspect = canvas.width / canvas.height;
  const projection = m4.perspective(fieldOfView, aspect, 1, 500);
  const radius = 5;
  const eye = [
      Math.sin(time) * radius,
      2,
      Math.cos(time) * radius];
  const target = [0, 0, 0];
  const up = [0, 1, 0];
  const camera = m4.lookAt(eye, target, up);
  const view = m4.inverse(camera);

  const worldViewProjection = m4.multiply(projection, view);

  drawLines(cubeVertices, indices, worldViewProjection);
  ctx.restore();
  requestAnimationFrame(render);
}
requestAnimationFrame(render);

function drawLines(cubeVertices, indices, worldViewProjection) {
  ctx.beginPath();
  //
  // transform points from 3D to 2D.
  //
  const points = [];
  for (let ii = 0; ii < cubeVertices.length; ii += 3) {
    points.push(m4.transformPoint(
      worldViewProjection,
      cubeVertices.slice(ii, ii + 3)));
  }
  for (let ii = 0; ii < indices.length; ii += 2) {
    var p0 = points[indices[ii + 0]];
    var p1 = points[indices[ii + 1]];
    ctx.moveTo(p0[0], p0[1]);
    ctx.lineTo(p1[0], p1[1]);
  }
  ctx.stroke();
}
canvas { border: 1px solid red; }
<!-- just a math lib -->
<script src="https://webglfundamentals.org/webgl/resources/m4.js"></script>
<canvas id="c"></canvas>

and here's the same 3D in WebGL

const cubeVertices = [
  -1, -1, -1,
   1, -1, -1,
   1,  1, -1,
  -1,  1, -1,
  -1, -1,  1,
   1, -1,  1,
   1,  1,  1,
  -1,  1,  1,
];
const indices = [
  0, 1,
  1, 2,
  2, 3,
  3, 0,
  4, 5,
  5, 6,
  6, 7,
  7, 4,
  0, 4,
  1, 5,
  2, 6,
  3, 7,
];

const canvas = document.querySelector('#c');
const gl = canvas.getContext('webgl');

const vs = `
attribute vec4 a_position;
uniform mat4 u_worldViewProjection;

void main() {
    //
    // transform points from 3D to 2D.
    //
    gl_Position = u_worldViewProjection * a_position;
}
`;

const fs = `
void main() {
   gl_FragColor = vec4(0,0,0,1);
}
`;

const program = webglUtils.createProgramFromSources(
    gl, [vs, fs]);
gl.useProgram(program);

const positionLoc = gl.getAttribLocation(program, "a_position");
const worldViewProjectionLoc =
    gl.getUniformLocation(program, "u_worldViewProjection");

const buffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
gl.bufferData(
    gl.ARRAY_BUFFER,
    new Float32Array(cubeVertices),
    gl.STATIC_DRAW);
gl.enableVertexAttribArray(positionLoc);
gl.vertexAttribPointer(positionLoc, 3, gl.FLOAT, false, 0, 0);

const buffer2 = gl.createBuffer();
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, buffer2);
gl.bufferData(
    gl.ELEMENT_ARRAY_BUFFER,
    new Uint16Array(indices),
    gl.STATIC_DRAW);

function render(time) {
  time *= 0.001;

  const scale = 4;

  const fieldOfView = Math.PI * 0.25;
  const aspect = canvas.width / canvas.height;
  const projection = m4.perspective(fieldOfView, aspect, 0.0001, 500);
  const radius = 5;
  const eye = [
      Math.sin(time) * radius,
      2,
      Math.cos(time) * radius];
  const target = [0, 0, 0];
  const up = [0, 1, 0];
  const camera = m4.lookAt(eye, target, up);
  const view = m4.inverse(camera);

  const worldViewProjection = m4.multiply(projection, view);
  gl.uniformMatrix4fv(
      worldViewProjectionLoc, false, worldViewProjection);

  gl.drawElements(gl.LINES, indices.length, gl.UNSIGNED_SHORT, 0);
  requestAnimationFrame(render);
}
requestAnimationFrame(render);
canvas { border: 1px solid red; }
<!-- just a math lib -->
<script src="https://webglfundamentals.org/webgl/resources/m4.js"></script>
<script src="https://webglfundamentals.org/webgl/resources/webgl-utils.js"></script>
<canvas id="c"></canvas>

The only difference between the Canvas one and the WebGL one is in Canvas I did the projection in JavaScript and in WebGL I did the projection in the shader. In both cases the code I wrote did the projection.

In the Canvas version that code is:

m4.transformPoint(
    worldViewProjection,
    cubeVertices.slice(ii, ii + 3));

In the WebGL version that code is:

gl_Position = u_worldViewProjection * a_position

The API itself only rasterizes. I have to supply the projection code in either case. There is nothing in WebGL that does 3D. There is just a rasterization api and 2 functions, the vertex shader, and fragment shader, both written in GLSL, that I have to supply that run very fast and include a math library. I still have to provide the code to do the 3D to the API in both cases.

WebGL is **NOT* a 3D API

I believe it's important to point this out. There are various versions of OpenGL. The original OpenGL from 1993 was a 3D api. You gave it 3D data, you told it what colors to make things, you told it about various lights. You gave it a model matrix and a projection matrix and it drew 3D for you.

OpenGL ES 2.0 and WebGL got rid of all of that. They provide a rasterization API and shaders and let you program the hardware. But it's up to you to write all the projections. You have to compute projected coordinates from 3D. You have compute lighting equations, and colors and all the rest.

This makes WebGL and OpenGL ES 2.0 arguably much harder than the old fixed function OpenGL but at the same time it makes them massively more flexible. If you're comfortable doing all those conversions and math or if you don't mind learning it then jump in and do it. If you aren't comfortable doing all then then there are plenty of WebGL 3D libraries that will do it for you.

To you who claim WebGL is a 3D library let's try a thought game.

Here's a physics library, box2d.js. You give it shapes, masses, and forces and it computes physics for you. If all it really was was a math library and you had to supply all the physics equations yourself would you still call it a physics library? Something called a physics library has to supply the physics knowledge or else it's not a physics library. Similarly, something called a 3D library has to supply the 3D knowledge or else it's not a 3D library.

OpenGL 1.0 was a 3D library. You gave it 3D positions, vertex colors, lights and it drew 3D for you. You needed no 3D knowledge. WebGL on the other hand does not provide any 3D knowledge. You have to know how to do 3D projections, you have to know how to sample textures, you have to know how to do lighting calculations. It's no longer a 3D library, it's just a rasterization API. Calling it a 3D library is a lie and a disservice to those actually looking for a 3D library, ie, a library the provides 3D.

Calling it a 2D library might be hyperbole but calling it a 3D library is wrong.

Here's another article about it.

like image 132
gman Avatar answered Sep 25 '22 23:09

gman


WebGL is actually a 2D API, not a 3D API. What does it mean?

It means you should stop listening to whatever website or person told you that. When people say idiotic things like that, it's best to ignore them and move on to more reasonable tutorials/information/discussions.

You can certainly work with WebGL in purely 2D terms. You can pass 2D positions to vertex shaders. You can turn off depth testing entirely. And so forth. But the output from your vertex shader is a 4D homogeneous coordinate, even if your W is 1 and your Z is 0. So the rendering system is going to do all of the 3D math that it would normally do for a 3D scene.

Yes, rasterization is basically a 2D process, with depth testing as a "hack" to allow for hidden surface removal. But this has been true of all rasterization-based renders. D3D, OpenGL, GLIDE, and every software rasterizer would also be "2D API"s by this logic.

And if all of them are 2D APIs, then the statement is pointless. It puts OpenGL/D3D on the same level as actual "2D API"s like SDL and Direct2D. Yet those "2D API"s can't do 3D rendering at all (or not without substantial pain).

So the statement is both factually incorrect and incredibly misleading. Whoever said it is not worth your time or attention.

from comments:

The person who originally wrote this "WebGL is 2D" stuff has deigned to explain his reasoning, so I'll address those points here.

Let's use his definition of API dimensionality. His exact quote is:

You gave them 3D data and nothing else and they gave you a 3D display. OpenGL ES 2.0 is a 2D api. You have to supply all of the 3D to 2D math conversion yourself.

From this, we can deduce that a "3D API" means "an API which 'you' feed 3D values into to cause 3D rendering to happen." Similarly, a "2D API" means "an API which 'you' feed 2D values into to cause 2D rendering to happen."

Let's assume that 'you' doesn't simply mean the dimensionality of the values fetched from a buffer object. 'You' means every piece of code you have direct control over, which includes your shader. OK, fine. So 'you', for WebGL, stops at the end of the vertex shader. Therefore, WebGL starts doing its stuff with the vertex shader outputs.

The output from a vertex shader is a 4D homogeneous coordinate. I'm guessing that the argument is that a 4D homogeneous coordinate is somehow identical to a 2D coordinate. Even though it's obviously not, since it has 2 more components, and the various math operations you do with them are very different.

I'll let you decide whether you want to consider a 4D homogeneous coordinate to be identical to a 2D coordinate.

Instead, I'll look at how WebGL treats the 4D output. Does it convert it into a 2D coordinate? The OpenGL specification says no.

From the OpenGL ES 2.0, section 2.12, folio page 44:

Vertex shader execution yields a vertex coordinate gl_Position which is assumed to be in clip coordinates. Perspective division is carried out on clip coordinates to yield normalized device coordinates, followed by a viewport transformation to convert these coordinates into window coordinates (see figure 2.4).

Clip coordinates are four-dimensional homogeneous vectors consisting of x, y, z, and w coordinates (in that order). If a vertex’s clip coordinates are:

(xc, yc, zc, wc)

then the vertex’s normalized device coordinates are

(xd, yd, zd) = (xc/wc, yc/wc, zc/wc)

Normalized device coordinate space has 3 components. It is therefore not a 2D space. But what about later transformations?

Well, from section 2.12.1 of the same specification (folio pages 44-45):

The viewport transformation is determined by the viewport’s width and height in pixels, px and py, respectively, and its center (ox, oy) (also in pixels). The vertex’s window coordinates, (xw, yw, zw), are given by

xw = (px/2)xd + ox

yw = (py/2)yd + oy

zw = ((f - n)/2)zd + (n + f)/2

So yes, even window space is a 3D coordinate system. Window space is the final space that OpenGL uses in its computation; window space goes straight to the rasterizer. That's what gets rendered.

Therefore, by the OpenGL ES 2.0 specification, there is no point in the entire rendering pipeline where anything get converted into a purely 2D space.

WebGL is an API that you feed 4D homogeneous coordinates into. At no point does WebGL perform any "3D to 2D math conversion", nor does the user. Nobody converts anything into 2D coordinates at any point in WebGL. 2D values are not fed through a 2D pipeline; 4D values are fed through a 3D pipeline.

Therefore, by his own definition, WebGL is not a 2D API.

QED.

like image 45
Nicol Bolas Avatar answered Sep 22 '22 23:09

Nicol Bolas