Sar*_*mar 10 javascript graphics opengl-es webgl html5-canvas
根据HTML5 Rocks,WebGL实际上是一个2D API,而不是3D API.他们为什么这么说,这是什么意思?
我们可以在WebGL顶点着色器和片段着色器中指定X,Y,Z坐标.我无法理解2D和3D图形API之间的区别.你能解释为什么他们说这是一个2D API吗?
Nic*_*las 24
WebGL实际上是2D API,而不是3D API.这是什么意思?
这意味着您应该停止收听任何网站或人员告诉您的内容.当人们说这种愚蠢的事情时,最好忽略它们,然后继续进行更合理的教程/信息/讨论.
您当然可以使用纯粹的2D术语来处理WebGL.您可以将2D位置传递到顶点着色器.您可以完全关闭深度测试.等等.但是顶点着色器的输出是一个4D齐次坐标,即使你的W是1而你的Z是0.所以渲染系统将完成它通常对3D场景所做的所有3D数学运算.
是的,光栅化基本上是一个2D过程,深度测试是一个"黑客",允许隐藏表面去除.但是所有基于光栅化的渲染都是如此.D3D,OpenGL,GLIDE和每个软件光栅化器也都是这个逻辑的"2D API".
如果所有这些都是2D API,那么这个陈述毫无意义.它将OpenGL/D3D放在与SDL和Direct2D等实际 "2D API" 相同的水平上.然而,那些"2D API"根本无法进行3D渲染(或者没有实质性的痛苦).
因此,该陈述事实上是不正确的,令人难以置信的误导.谁说不值得你的时间或注意力.
来自评论:
最初写这篇"WebGL是2D"的人已经设计了解释他的推理,所以我将在这里解决这些问题.
让我们使用他对API维度的定义.他的确切引用是:
你给了他们3D数据,没有别的,他们给你一个3D显示.OpenGL ES 2.0是一个2D api.您必须自己提供所有3D到2D数学转换.
由此,我们可以推断出"3D API"意味着"您将3D值提供给API以使3D渲染发生." 类似地,"2D API"意味着"您将2D值输入的API,以使2D渲染发生."
让我们假设"你"并不仅仅意味着从缓冲区对象获取的值的维度."你"是指你可以直接控制的每一段代码,包括你的着色器.好的.因此,对于WebGL,"你"停在顶点着色器的末尾.因此,WebGL开始使用顶点着色器输出.
顶点着色器的输出是4D齐次坐标.我猜这个论点是4D齐次坐标在某种程度上与2D坐标相同.虽然它显然不是,因为它有2个以上的组件,并且你用它们做的各种数学运算是非常不同的.
我将让您决定是否要将4D齐次坐标视为与2D坐标相同.
相反,我将看看WebGL如何处理4D输出.它是否将其转换为2D坐标?OpenGL规范说没有.
从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是一种API,可以将4D齐次坐标输入.WebGL在任何时候都不会执行任何"3D到2D数学转换",用户也不会.WebGL中的任何位置都没有人将任何内容转换为2D坐标.2D值不通过2D管道馈送; 4D值通过3D管道馈送.
因此,根据他自己的定义,WebGL不是2D API.
QED.
mat*_*ort 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场景,您需要自己在软件中实现所有这些.
[ 更新 ]在他的一篇文章中, gman或多或少地交替使用"API"和"图书馆".我不认为对这两个术语有明确而明确的定义,但我认为对这些术语的不同理解可能会导致这里的一些分歧.
Khronos描述了WebGL:
WebGL™是一种为网络设计的即时模式3D渲染API.
我认为这是一个准确的描述."API"的一个常用含义是用于访问底层硬件或OS服务的已定义软件接口,并且指的是面向公众的软件接口而不是任何特定实现.从这个意义上讲,所有用于访问3D图形硬件的主流现代API都可以被视为低级"即时模式3D渲染API".我将包括OpenGL,OpenGL ES,WebGL,Direct3D以及此类别控制台上的专有API.
业界通常将所有这些称为"3D API",因为它们旨在提供对主要功能正在渲染3D图形的GPU的访问,并且它们暴露了支持该功能的低级功能(透视正确插值和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'是一种有用或准确的方法来实现它.
好吧,我不知道其他人 - 我会倾向于选择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/
这是一个观点.
WebGL中没有任何要求在3d中对世界进行建模,创建相机,设置灯光等.最后,渲染器只关注点,线和三角形,其4个坐标与|w*x|<w, |w*y|<w, |w*z|<w.默认情况下相关也只能传递到着色器两个坐标x和y,而框架设置z = 0和w = 1.
也可以使用opengl来绘制2d精灵而不用担心设置一些投影矩阵.可以省略z缓冲区和z坐标的处理,直到该点为止,需要保持:| z*w | <= w表示要渲染的任何内容.
但是很明显,API非常适合渲染3D模型并非巧合.
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();
}Run Code Online (Sandbox Code Playgroud)
canvas { border: 1px solid red; }Run Code Online (Sandbox Code Playgroud)
<!-- just a math lib -->
<script src="https://webglfundamentals.org/webgl/resources/m4.js"></script>
<canvas id="c"></canvas>Run Code Online (Sandbox Code Playgroud)
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);Run Code Online (Sandbox Code Playgroud)
canvas { border: 1px solid red; }Run Code Online (Sandbox Code Playgroud)
<!-- 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>Run Code Online (Sandbox Code Playgroud)
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));
Run Code Online (Sandbox Code Playgroud)
In the WebGL version that code is:
gl_Position = u_worldViewProjection * a_position
Run Code Online (Sandbox Code Playgroud)
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.
对于那些声称 WebGL 是 3D 库的人来说,让我们尝试一下思维游戏。
这是一个物理库,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 是一个 3D 库。您给它 3D 位置、顶点颜色、灯光,它就会为您绘制 3D 图像。您不需要 3D 知识。另一方面,WebGL 不提供任何 3D 知识。你必须知道如何进行 3D 投影,你必须知道如何对纹理进行采样,你必须知道如何进行光照计算。它不再是一个 3D 库,而只是一个光栅化 API。称其为 3D 库是一个谎言,对那些真正寻找 3D 库(即提供 3D 的库)的人来说是一种伤害。
称其为 2D 库可能有些夸张,但称其为 3D 库则是错误的。
| 归档时间: |
|
| 查看次数: |
3551 次 |
| 最近记录: |