为什么这表明WebGL是2D API而不是3D API?

时间:2012-10-30 08:29:50

标签: javascript graphics opengl-es html5-canvas webgl

According to HTML5 Rocks,WebGL实际上是2D API,而不是3D API。他们为什么这么说,这是什么意思?

我们可以在WebGL顶点着色器和片段着色器中指定X,Y,Z坐标。我无法理解2D和3D图形API之间的区别。你能解释为什么他们说这是一个2D API吗?

6 个答案:

答案 0 :(得分:24)

  

WebGL实际上是2D API,而不是3D API。这是什么意思?

这意味着您应该停止收听任何网站或人员告诉您的内容。当人们说这种愚蠢的事情时,最好忽略它们,然后继续进行更合理的教程/信息/讨论。

您当然可以使用纯粹的2D术语来处理WebGL。您可以将2D位置传递到顶点着色器。您可以完全关闭深度测试。等等。但是顶点着色器的输出是4D homogeneous coordinate,即使你的W是1而你的Z是0.所以渲染系统将完成它通常对3D场景所做的所有3D数学运算。

是的,光栅化基本上是一个2D过程,深度测试是一个&#34; hack&#34;允许隐藏表面去除。但是所有基于光栅化的渲染都是如此。 D3D,OpenGL,GLIDE和每个软件光栅化器通过这种逻辑<#34; 2D API&#34;

如果所有这些都是2D API,那么这句话毫无意义。它将OpenGL / D3D置于与实际&#34; 2D API相同的级别,如SDL和Direct2D。然而,那些&#34; 2D API&#34;根本无法进行3D渲染(或者没有实质性的痛苦)。

因此,该陈述事实上是不正确的,令人难以置信的误导。无论谁说不值得你的时间或注意力。

  

来自评论:

最初写这个&#34; WebGL的人是2D&#34;他已经设法解释他的推理,所以我在这里解决这些问题。

让我们使用他对API维度的定义。他的确切引用是:

  

你给了他们3D数据,没有别的,他们给你一个3D显示。 OpenGL ES 2.0是一个2D api。您必须自己提供所有3D到2D数学转换。

由此,我们可以推断出一个&#34; 3D API&#34;意味着&#34;一个API,你&#39;将3D值输入以使3D渲染发生。&#34;同样,&#34; 2D API&#34;意味着&#34;一个API,你&#39;将2D值输入以使2D渲染发生。&#34;

让我们假设你&#39;你&#39;并不仅仅意味着从缓冲区对象获取的值的维度。 &#39;你&#39;表示您可以直接控制的每段代码,包括着色器。好的。因此,对于WebGL,您在顶点着色器的末尾停止。因此,WebGL开始使用顶点着色器输出。

顶点着色器的输出是4D齐次坐标。我猜这个论点是4D齐次坐标在某种程度上与2D坐标相同。虽然它显然没有,因为它有2个以上的组件,你用它们做的各种数学运算是非常不同的。

我将让您决定是否要将4D齐次坐标视为与2D坐标相同。

相反,我将看看 WebGL 如何处理4D输出。它是否将其转换为2D坐标? OpenGL规范说没有。

来自the OpenGL ES 2.0,第2.12节,第44页作品集:

  

顶点着色器执行产生顶点坐标gl_Position,假设它位于剪辑坐标中。在剪辑坐标上执行透视分割以生成标准化设备坐标,然后进行视口转换以将这些坐标转换为窗口坐标(参见图2.4)。

     

剪辑坐标是由x,y组成的四维齐次向量,   z和w坐标(按此顺序)。如果顶点的剪辑坐标为:

     

(x c ,y c ,z c ,w c

     

然后顶点的标准化设备坐标为

     

(x d ,y d ,z d )=(x c / w c ,y c /,z c / w c

规范化设备坐标空间有3个组件。因此它不是2D空间。但是后来的转变呢?

那么,从同一规范的第2.12.1节(对开页44-45):

  

视口变换由视口的宽度和高度(以像素为单位)确定,分别为p x 和p y 及其中心(o x ,o y )(也以像素为单位)。顶点的窗口坐标(x w ,y w ,z w )由

给出      

x w =(p x / 2)x d + o x

     

y w =(p y / 2)y d + o y

     

z w =((f - n)/ 2)z d +(n + f)/ 2

是的,即使窗口空间也是一个3D坐标系。窗口空间是OpenGL在计算中使用的最终空间;窗口空间直接进入光栅化器。这就是呈现的内容。

因此,通过OpenGL ES 2.0规范,整个渲染管道中没有任何内容可以将任何内容转换为 2D空间。

WebGL是一种将4D齐次坐标输入的API。 WebGL在任何时候都不会执行任何&#34; 3D到2D数学转换&#34;,用户也不会。在WebGL中的任何位置都没有人将任何东西转换为2D坐标。 2D值不通过2D管道馈送; 4D值通过3D管道输入。

因此,按照他自己的定义,WebGL不是2D API。

QED。

答案 1 :(得分:16)

在我看来(作为拥有超过15年3D图形经验的游戏开发人员),gman将WebGL描述为2D API的行为极具误导性,而且我倾向于认为只是出错了。 Nicol Bolas在他的回答中指出了大多数原因,但对我而言,关键点在于,一旦你从画布/ WebGL示例中使用的线框图形gman移动到纹理,就不可能获得正确渲染的3D场景三角形,如果WebGL未在顶点着色器的输出中提供z和w信息,并且在光栅化期间未使用它们以获得透视正确插值并使用z缓冲区执行隐藏曲面移除。

点gman似乎真的试图让WebGL不是一个固定功能的3D图形API,就像古老的3D图形API一样,但有一个可编程的管道。然而,所有现代3D图形API都是如此(Direct3D 8,9,10,11; OpenGL 2.0及以上;您可以在PS3,PS4,Wii U等控制台上找到的专有API)。它们的工作方式基本相同:顶点着色器输出齐次坐标,光栅化器使用z和w信息正确插值投影到2D图像的3D三角形,并使用z缓冲区执行隐藏曲面移除。这与没有z和w坐标的2D API,没有透视正确插值的概念以及用于隐藏表面移除的z缓冲区非常不同。要在类似于画布的2D API中正确渲染纹理三角形的3D场景,您需要自己在软件中实现所有这些。

[已更新]他的一个articles gman或多或少可互换地使用'API'和'Library'。我不认为对这两个术语有明确而明确的定义,但我认为对这些术语的不同理解可能会导致这里的一些分歧。

Khronos描述了WebGL:

  

WebGL™是为网络设计的即时模式3D渲染API。

我认为这是一个准确的描述。 “API”的一个常用含义是用于访问底层硬件或OS服务的已定义软件接口,并且是指面向公众的软件接口而不是任何特定实现。从这个意义上讲,所有用于访问3D图形硬件的主流现代API都可以被视为低级“即时模式3D渲染API”。我将包括OpenGL,Op​​enGL ES,WebGL,Direct3D以及此类别控制台上的专有API。

业界通常将所有这些称为“3D API”,因为它们旨在提供对GPU的访问,这些GPU的主要功能是渲染3D图形,并且它们暴露了支持该功能的低级功能(透视正确)在光栅化过程中基于插值和基于z缓冲的隐藏表面去除,各向异性纹理过滤,在某些情况下,细分硬件等)以及对3D流水线的可编程部分(顶点,像素,几何着色器,船体和域)进行编程的方法着色器等。)。

我倾向于认为'库'与'API'的含义略有不同。像three.js这样的东西将自己描述为“库”而不是“API”:

  

Three.js是一个在浏览器中制作WebGL - 3D的库   简单。虽然原始WebGL中的一个简单的多维数据集会变成数百个   Javascript和着色器代码行,一个Three.js等价物只有一个   这一部分。

虽然这两个术语没有硬性和快速的定义,但我倾向于认为库更多地涉及功能的特定实现,并且暗示可能比直接API更高级别的辅助功能。

其他更高级别的3D'库'可能将自己描述为“引擎”或“框架”,例如

  

OGRE(面向对象的图形渲染引擎)是一个面向场景的,   灵活的3D引擎,用C ++编写,旨在使其更容易和更多   直观的开发人员利用   硬件加速的3D图形。

有一大堆功能不属于旧式固定功能'API',如2.0之前的OpenGL或DX8之前的DirectX,但如果您只想渲染一些3D对象而不需要详细了解3D图形,则非常有用 - 像场景图,加载和渲染模型的功能,对光和阴影的高级支持等等,但这不是Direct3D或WebGL等低级3D'API所针对的。这不是他们试图解决的问题。我可以看到如何尝试将其传达给那些只想在浏览器中渲染一些3D对象的新手,但我不认为声称WebGL是一个'2D API'是一种有用或准确的方法来实现它

答案 2 :(得分:6)

好吧,我不知道其他人 - 我会倾向于选择Kronos在他们网站上说的话。对我来说似乎很清楚。 :耸肩:

  

WebGL™是一种为网络设计的即时模式3D渲染API。它   源自OpenGL®ES2.0,并提供类似的渲染   功能,但在HTML上下文中。 WebGL被设计为   渲染HTML Canvas元素的上下文。 HTML Canvas   为网页中的程序化渲染提供了一个目的地   允许使用不同的渲染API执行该渲染。   唯一这样的界面被描述为Canvas规范的一部分   是2D画布渲染上下文CanvasRenderingContext2D。这个   文档描述了另一个这样的接口,WebGLRenderingContext,   它提供了WebGL API。

https://www.khronos.org/registry/webgl/specs/1.0/

答案 3 :(得分:5)

这是一个观点。

WebGL中没有任何要求在3d中对世界进行建模,创建相机,设置灯光等。最后,渲染器只关注点,线和三角形,其4个坐标与|w*x|<w, |w*y|<w, |w*z|<w.相关默认值只能传递给着色器两个坐标x和y,而框架设置z = 0和w = 1.

也可以使用opengl来绘制2d精灵而不用担心设置一些投影矩阵。可以省略z缓冲区和z坐标的处理,直到该点为止,需要保持:| z * w | &lt; = w表示要呈现的任何内容。

但是很明显,API非常适合渲染3D模型并非巧合。

答案 4 :(得分:0)

WebGL是一种rasteration API而不是3D api。你必须提供投影坐标。这种方式与Canvas没有什么不同。它只是更快。我们来比较吧。

这是画布中的3D

window.onload = main;

function main() {
  var 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,
  ];
  var 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,
  ];

  var canvas = document.getElementById("c");
  var ctx = canvas.getContext("2d");
  var clock = 0;
  var then = Date.now() * 0.001;

  function render() {
    var now = Date.now() * 0.001;
    clock += now - then;
    then = now;

    var 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";

    var fieldOfView = Math.PI * 0.25;
    var aspect = canvas.width / canvas.height;
    var projection = m.perspective(fieldOfView, aspect, 1, 500);
    var radius = 5;
    var eye = [
        Math.sin(clock) * radius,
        1,
        Math.cos(clock) * radius];
    var target = [0, 0, 0];
    var up = [0, 1, 0];
    var view = m.lookAt(eye, target, up);

    var worldViewProjection = m.multiplyMatrix(view, projection);

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

  function drawLines(cubeVertices, indices, worldViewProjection) {
    ctx.beginPath();
    //
    // transform points from 3D to 2D.
    //
    var points = [];
    for (var ii = 0; ii < cubeVertices.length; ii += 3) {
      points.push(m.transformPoint(
        worldViewProjection,
        [cubeVertices[ii + 0], cubeVertices[ii + 1], cubeVertices[ii + 2]]));
    }
    for (var 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();
  }
}

这里是WebGL中相同的3D

<script>
window.onload = main;

function main() {
  var 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,
  ];
  var 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,
  ];

  var canvas = document.getElementById("c");
  var gl = getWebGLContext(c);
  var clock = 0;
  var then = Date.now() * 0.001;

  var program = createProgramFromScripts(
      gl, ["2d-vertex-shader", "2d-fragment-shader"]);
  gl.useProgram(program);

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

  var 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);

  var buffer = gl.createBuffer();
  gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, buffer);
  gl.bufferData(
      gl.ELEMENT_ARRAY_BUFFER,
      new Uint16Array(indices),
      gl.STATIC_DRAW);

  function render() {
    var now = Date.now() * 0.001;
    clock += now - then;
    then = now;

    var scale = 4;

    gl.clear(gl.COLOR_BUFFER_BIT);

    var fieldOfView = Math.PI * 0.25;
    var aspect = canvas.width / canvas.height;
    var projection = m.perspective(fieldOfView, aspect, 0.0001, 500);
    var radius = 5;
    var eye = [
        Math.sin(clock) * radius,
        1,
        Math.cos(clock) * radius];
    var target = [0, 0, 0];
    var up = [0, 1, 0];
    var view = m.lookAt(eye, target, up);

    var worldViewProjection = m.multiplyMatrix(view, projection);
    gl.uniformMatrix4fv(
        worldViewProjectionLoc, false, worldViewProjection);

    gl.drawElements(gl.LINES, indices.length, gl.UNSIGNED_SHORT, 0);
    requestAnimFrame(render);
  }
  render();
}
</script>
<!-- vertex shader -->
<script id="2d-vertex-shader" type="x-shader/x-vertex">
attribute vec4 a_position;
uniform mat4 u_worldViewProjection;

void main() {
    //
    // transform points from 3D to 2D.
    //
    gl_Position = u_worldViewProjection * a_position;
}
</script>
<!-- fragment shader -->
<script id="2d-fragment-shader" type="x-shader/x-fragment">
void main() {
   gl_FragColor = vec4(0,0,0,1);
}
</script>

如果您希望看到它们here's the canvas versionhere's the webgl version

Canvas one和WebGL之间的唯一区别在于Canvas我在JavaScript中进行了转换投影,在WebGL中我在着色器中进行了投影。在这两种情况下,我编写的代码都进行了投影。

在Canvas版本中代码为:

m.transformPoint(
    worldViewProjection,
    [cubeVertices[ii + 0], cubeVertices[ii + 1], cubeVertices[ii + 2]);

在WebGL版本中代码为:

gl_Position = u_worldViewProjection * a_position

API本身只会栅格化。在任何一种情况下我都必须提供投影。 WebGL中没有任何3D功能。只有一个光栅化api和2个函数,顶点着色器和片段着色器,用GLSL编写,我必须提供非常快的运行并包含一个数学库。在这两种情况下,我仍然需要向3D提供代码。

WebGL是* NOT 3D API

我认为指出这一点非常重要。有各种版本的OpenGL。 1993年的原始OpenGL是一个3D api。你给它3D数据,你告诉它什么颜色的东西,你告诉它有关各种灯。你给它一个模型矩阵和一个投影矩阵,它为你绘制了3D。

OpenGL ES 2.0和WebGL摆脱了所有这些。它们提供光栅化API和着色器,并允许您对硬件进行编程。但是由你来编写所有预测。您必须从3D计算投影坐标。你有计算照明方程,颜色和其他所有。

这使得WebGL和OpenGL ES 2.0可能比旧的固定功能OpenGL更难,但同时它使它们更加灵活。如果你很自在地做所有这些转换和数学,或者你不介意学习它,那么就跳进去做。如果您不习惯所有这些,那么有很多WebGL 3D库可以为您完成。

对于声称WebGL是3D库的人来说,让我们尝试思考游戏。

这是一个物理库,box2d.js。你给它形状,质量和力量,它为你计算物理。如果真的只是一个数学库而你必须自己提供所有的物理方程式,你还会把它称为物理库吗?称为物理库的东西必须提供物理知识,否则它不是物理库。同样,称为3D库的东西必须提供3D知识,否则它不是3D库。

OpenGL 1.0是一个3D库。你给它3D位置,顶点颜色,灯光,它为你绘制3D。你不需要3D知识。另一方面,WebGL不提供任何3D知识。您必须知道如何进行3D投影,您必须知道如何对纹理进行采样,您必须知道如何进行光照计算。它不再是3D库,它只是一个光栅化API。将它称为3D库对于那些真正寻找3D库的人来说是一种谎言和伤害,即提供3D的库。

将其称为2D库可能会夸张,但将其称为3D库是错误的。

Here's another article about it

答案 5 :(得分:-2)

哇,你的问题很棒!你让所有这些大师在最后的战斗O.o

中战斗

好吧,也许你也想读一下拥有1年WebGL经验并且没有OpenGL编程技巧的家伙的答案,所以让我们这样做吧! :)

首先,我必须说一些我尚未在那里阅读的内容,我觉得这很重要。 @gman sais说:

  

API提供解决方案,因此您没有亲自拥有该解决方案   知识。

嗯,我真的不同意这个说法。 API提供了一些解决方案,但这并不意味着您不需要这些知识。

现在回到你的问题,正如你所说:

  

我们可以在webGL顶点着色器和fragement中指定x,y,z坐标   着色器。我无法理解2D和3D图形之间的区别   API。

正如其他人所说,你可以在4D向量的着色器中指定(我认为你甚至必须指定)gl_Position。之前,只有javascript和一些分离的gpu。然后WebGL出现新选项。 WebGL本身不提供 3D行为的任何解决方案,但它带来了在着色器中指定gl_Position的选项。

我计算新选项而不仅仅是整个解决方案。所以我认为这个问题也是关于什么API真的适合你。为了我?在这种情况下,它可以通过着色器制作3D应用程序。所以它是3D API。

我希望它有所帮助...