According to HTML5 Rocks,WebGL实际上是2D API,而不是3D API。他们为什么这么说,这是什么意思?
我们可以在WebGL顶点着色器和片段着色器中指定X,Y,Z坐标。我无法理解2D和3D图形API之间的区别。你能解释为什么他们说这是一个2D API吗?
答案 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,OpenGL 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。
答案 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 version和here'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库是错误的。
答案 5 :(得分:-2)
好吧,也许你也想读一下拥有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。
我希望它有所帮助...