为什么说WebGL是二维API,而不是三维API



根据HTML5 Rocks,WebGL实际上是一个2D API,而不是3D API。他们为什么这么说,这意味着什么?

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

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

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

您当然可以使用纯二维的WebGL。可以将2D位置传递给顶点着色器。您可以完全关闭深度测试。等等但是顶点着色器的输出是4D齐次坐标,即使W为1,Z为0。因此,渲染系统将进行所有的3D数学运算,就像它通常对3D场景所做的那样。

是的,光栅化基本上是一个2D过程,深度测试是一个"过程";破解";以允许去除隐藏的表面。但所有基于光栅化的渲染都是如此。D3D、OpenGL、GLIDE和每一个软件光栅化器都将为";2D API";根据这个逻辑。

如果所有这些都是2D API,那么该语句就毫无意义了。它将OpenGL/D3D置于与实际"相同的水平上;2D API";像SDL和Direct2D。然而,那些";2D API";s根本无法进行3D渲染(或者并非没有实质性的痛苦)。

因此,这种说法既有事实上的错误,也有令人难以置信的误导。不管是谁说的,都不值得你花时间或注意。

来自评论:

最初写这篇文章的人"WebGL是2D";东西已经屈尊解释了他的推理,所以我将在这里解决这些问题。

让我们使用他对API维度的定义。他的原话是:

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

由此,我们可以推断出;3D API";是指";API,"您"将3D值输入到该API中以实现3D渲染"类似地;2D API";是指";API,"您"将2D值输入到该API中以使2D渲染发生">

让我们假设"您"不仅仅是指从缓冲区对象中提取的值的维度"你"是指你可以直接控制的每一段代码,包括你的着色器。好的,好的。因此,对于WebGL,"你"会停在顶点着色器的末尾。因此,WebGL开始使用顶点着色器输出。

顶点着色器的输出是4D齐次坐标。我猜,论点是4D齐次坐标在某种程度上与2D坐标相同。尽管它显然不是,因为它还有两个组件,而且你对它们进行的各种数学运算非常不同。

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

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

来自OpenGL ES 2.0第2.12节对开本第44页:

顶点着色器执行会生成顶点坐标gl_Position,该坐标假定为剪辑坐标。对剪辑坐标进行透视分割,生成标准化的设备坐标,然后进行视口变换,将这些坐标转换为窗口坐标(见图2.4)

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

则顶点的标准化设备坐标为

标准化设备坐标空间有3个组件。因此,它不是2D空间。但是以后的转换呢?

好吧,来自同一规范第2.12.1节(对开页44-45):

视口变换由视口的宽度和高度(以像素为单位)决定,分别为px和py,以及视口的中心(ox、oy)(以像素计)。顶点的窗口坐标(xw、yw和zw/sub>)由给出

xw=(px/2)xd+ox

yw=(py/2)yd+oy

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

所以是的,即使窗口空间也是一个三维坐标系。窗口空间是OpenGL在计算中使用的最终空间;窗口空间直接进入光栅化器。这就是渲染的内容。

因此,根据OpenGL ES 2.0规范,在整个渲染管道中没有任何东西被转换为2D空间。

WebGL是一个API,您可以将4D齐次坐标输入其中。WebGL在任何时候都不执行任何";"3D到2D数学转换";,用户也没有。在WebGL中,任何人都无法将任何东西转换为二维坐标。2D值不是通过2D管道馈送的;4D值通过3D管道馈送。

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

QED。

在我看来(作为一名拥有15年以上3D图形经验的游戏开发者),gman将WebGL描述为2D API充其量是高度误导,我倾向于认为这完全是错误的。Nicol Bolas在他的回答中指出了大部分原因,但对我来说,关键是,如果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缓冲器。这与2D API非常不同,2D API没有z和w坐标,没有透视正确插值的概念,也没有用于隐藏表面去除的z缓冲区。要在类似API的2D画布中正确渲染纹理三角形的3D场景,您需要自己在软件中实现所有这些。

[更新]在他的一篇文章中,gman或多或少地可互换地使用"API"one_answers"库"。我不认为这两个术语有一个明确而成熟的定义,但我认为对这些术语的不同理解可能会导致这里的一些分歧。

Khronos描述WebGL:

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

我认为这是一个准确的描述。API的一个常用含义是访问底层硬件或操作系统服务的定义软件接口,它指的是面向公众的软件接口,而不是任何特定的实现。从这个意义上说,所有旨在访问3D图形硬件的主流现代API都可以被视为低级的"即时模式3D渲染API"。我将包括OpenGL、OpenGL ES、WebGL、Direct3D和控制台上的专有API。

业内通常将所有这些称为"3D API",因为它们旨在提供对GPU的访问,GPU的主要功能是渲染3D图形,并且它们暴露了支持该功能的低级别功能

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

Three.js是一个使浏览器中的WebGL-3D非常容易的而原始WebGL中的一个简单立方体会产生数百个行Javascript和着色器代码,Three.js等效代码只是其中的一小部分。

虽然这两个术语没有硬性定义,但我倾向于认为库更多地指的是功能的特定实现,并意味着可能比直接的API更高级别的助手功能。

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

OGRE(Object Oriented Graphics Rendering Engine)是一个面向场景的图形渲染引擎,用C++编写的灵活的3D引擎,旨在使其变得更简单开发人员直观地利用硬件加速的3D图形。

有一大堆功能不是老式固定功能"API"的一部分,比如2.0之前的OpenGL或DX8之前的DirectX,但如果你只想渲染一些3D对象而不需要详细了解3D图形,这些功能非常有用,比如场景图、加载和渲染带有附加材料的模型的功能、对灯光和阴影的高级支持,但这不是Direct3D或WebGL等低级别3D"API"的目标。这不是他们试图解决的问题。我可以看出,尝试将其传达给那些只想在浏览器中渲染一些3D对象的新手可能会很有用,但我不认为声称WebGL是"2D API"是一种有用或准确的方式。

嗯,我不知道其他人的情况——无论Khronos在他们的网站上说什么,我都倾向于接受。我觉得很清楚。:耸耸肩:

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

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

这是一个观点。

WebGL中没有任何命令要求对世界进行三维建模、创建相机、设置灯光等。最后,渲染器只关心点、线和三角形,它们的4个坐标与|w*x|<w, |w*y|<w, |w*z|<w.有关。默认情况下,一个着色器只能传递两个坐标,x和y,而框架设置z=0和w=1。

人们也可以使用opengl绘制二维精灵,而不必担心设置一些投影矩阵。可以省略z缓冲区和z坐标的处理,直到有必要保持:|z*w|<=w表示要渲染的任何内容。

但也很清楚,API非常适合渲染三维模型,这并非巧合。

WebGL是一个光栅化API,而不是3D API。您必须为其提供投影坐标。在很多方面,它与Canvas没有什么不同。它只是更快。让我们进行比较。

这是画布中的3D

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>

这是WebGL 中的相同3D

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>

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

在Canvas版本中,代码为:

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

在WebGL版本中,代码为:

gl_Position = u_worldViewProjection * a_position

API本身只是光栅化。无论哪种情况,我都必须提供投影代码。WebGL中没有做3D的东西。只有一个光栅化api和两个函数,顶点着色器和片段着色器,都是用GLSL编写的,我必须提供它们,它们运行得非常快,并且包括一个数学库。在这两种情况下,我仍然需要为API提供3D代码。

WebGL是**非*三维API

我认为指出这一点很重要。OpenGL有各种版本。1993年的OpenGL最初是一个3D api。你给了它3D数据,告诉它制作东西的颜色,告诉它各种各样的光。你给了它一个模型矩阵和一个投影矩阵,它为你画了3D。

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

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

对于那些声称WebGL是一个3D库的人,让我们来试试思维游戏。

这是一个物理库,box2d.js。你给它形状、质量和力,它就会为你计算物理。如果它真的只是一个数学库,而你必须自己提供所有的物理方程,你还会称之为物理库吗?一个叫做物理图书馆的东西必须提供物理知识,否则它就不是物理图书馆。类似地,被称为3D库的东西必须提供3D知识,否则它就不是3D库。

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

称之为2D库可能有些夸张,但称之为3D库是错误的。

这是关于它的另一篇文章。

哇,你的问题太棒了!你让所有这些大师像上次战斗一样战斗

好吧,也许你也想读一个有1年WebGL经验的人的回答,他几乎没有OpenGL编程技能,所以让我们这样做吧!:)

首先,我要说一些我还没有在那里读到的东西,我觉得它很重要@格曼说:

API提供了一个解决方案,因此您个人不必拥有该解决方案知识

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

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

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

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

我计算的是新的选择,而不仅仅是整个解决方案。所以我认为这个问题也是关于API对你来说到底是什么。为了我?在这种情况下,它带来了通过着色器制作3D应用程序的可能性。所以它是三维API。

我希望它能帮助。。。

最新更新