Skip to content

Commit 2080d75

Browse files
colin3dmaxgreggman
authored andcommitted
Translate webgl-2d-vs-3d-library.md to Chinese
1 parent f251b86 commit 2080d75

File tree

1 file changed

+177
-0
lines changed

1 file changed

+177
-0
lines changed
Lines changed: 177 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,177 @@
1+
Title: WebGL2 - 光栅化 vs 3D 库
2+
Description: 为什么 WebGL 不是 3D 库以及这点为什么重要。
3+
TOC: 2D vs 3D 库
4+
5+
这篇文章是关于 WebGL 系列文章的一个旁支话题。
6+
第一篇是 [基础知识介绍](webgl-fundamentals.html)
7+
8+
我写这篇文章是因为我说 WebGL 是一个光栅化 API,而不是一个 3D API,这句话触到了某些人的神经。
9+
我不太清楚为什么他们会觉得被威胁,或者是什么让他们对我称 WebGL 为光栅化 API 这件事如此反感。
10+
11+
可以说,一切都是视角问题。我可能会说刀是一种餐具,别人可能会说刀是工具,还有人可能会说刀是武器。
12+
13+
但在 WebGL 的情况下,我认为将 WebGL 称为光栅化 API 是重要的,这是有原因的——那就是你需要掌握大量的 3D 数学知识,才能用 WebGL 绘制出任何 3D 内容。
14+
15+
我认为,任何自称为 3D 库的东西,都应该替你处理好 3D 的部分。你只需提供一些 3D 数据、材质参数、灯光信息,它就应该能够帮你完成 3D 渲染。
16+
WebGL(以及 OpenGL ES 2.0+)虽然都可以用来绘制 3D 图形,但它们都不符合这个定义。
17+
18+
个比方,C++ 并不能“原生处理文字”。尽管可以用 C++ 编写文字处理器,但我们不会把 C++ 称作“文字处理器”。
19+
同样,WebGL 并不能直接绘制 3D 图形。你可以基于 WebGL 编写一个绘制 3D 图形的库,但 WebGL 本身并不具备 3D 绘图功能。
20+
21+
进一步举个例子,假设我们想要绘制一个带有灯光效果的 3D 立方体。
22+
23+
以下是使用 three.js 来显示这个代码。
24+
25+
<pre class="prettyprint showlinemods">{{#escapehtml}}
26+
// Setup.
27+
renderer = new THREE.WebGLRenderer({canvas: document.querySelector("#canvas")});
28+
c.appendChild(renderer.domElement);
29+
30+
// Make and setup a camera.
31+
camera = new THREE.PerspectiveCamera(70, 1, 1, 1000);
32+
camera.position.z = 400;
33+
34+
// Make a scene
35+
scene = new THREE.Scene();
36+
37+
// Make a cube.
38+
var geometry = new THREE.BoxGeometry(200, 200, 200);
39+
40+
// Make a material
41+
var material = new THREE.MeshPhongMaterial({
42+
ambient: 0x555555,
43+
color: 0x555555,
44+
specular: 0xffffff,
45+
shininess: 50,
46+
shading: THREE.SmoothShading
47+
});
48+
49+
// Create a mesh based on the geometry and material
50+
mesh = new THREE.Mesh(geometry, material);
51+
scene.add(mesh);
52+
53+
// Add 2 lights.
54+
light1 = new THREE.PointLight(0xff0040, 2, 0);
55+
light1.position.set(200, 100, 300);
56+
scene.add(light1);
57+
58+
light2 = new THREE.PointLight(0x0040ff, 2, 0);
59+
light2.position.set(-200, 100, 300);
60+
scene.add(light2);
61+
{{/escapehtml}}</pre>
62+
63+
它显示如下。
64+
65+
{{{example url="resources/three-js-cube-with-lights.html" }}}
66+
67+
以下是在 OpenGL(非 ES 版本)中显示一个带有两个光源的立方体的类似代码。
68+
69+
<pre class="prettyprint showlinemods">{{#escapehtml}}
70+
// Setup
71+
glViewport(0, 0, width, height);
72+
glMatrixMode(GL_PROJECTION);
73+
glLoadIdentity();
74+
gluPerspective(70.0, width / height, 1, 1000);
75+
glMatrixMode(GL_MODELVIEW);
76+
glLoadIdentity();
77+
78+
glClearColor(0.0, 0.0, 0.0, 0.0);
79+
glEnable(GL_DEPTH_TEST);
80+
glShadeModel(GL_SMOOTH);
81+
glEnable(GL_LIGHTING);
82+
83+
// Setup 2 lights
84+
glEnable(GL_LIGHT0);
85+
glEnable(GL_LIGHT1);
86+
float light0_position[] = { 200, 100, 300, };
87+
float light1_position[] = { -200, 100, 300, };
88+
float light0_color[] = { 1, 0, 0.25, 1, };
89+
float light1_color[] = { 0, 0.25, 1, 1, };
90+
glLightfv(GL_LIGHT0, GL_DIFFUSE, light0_color);
91+
glLightfv(GL_LIGHT1, GL_DIFFUSE, light1_color);
92+
glLightfv(GL_LIGHT0, GL_POSITION, light0_position);
93+
glLightfv(GL_LIGHT1, GL_POSITION, light1_position);
94+
...
95+
96+
// Draw a cube.
97+
static int count = 0;
98+
++count;
99+
100+
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
101+
glLoadIdentity();
102+
double angle = count * 0.1;
103+
glTranslatef(0, 0, -400);
104+
glRotatef(angle, 0, 1, 0);
105+
106+
glBegin(GL_TRIANGLES);
107+
glNormal3f(0, 0, 1);
108+
glVertex3f(-100, -100, 100);
109+
glVertex3f( 100, -100, 100);
110+
glVertex3f(-100, 100, 100);
111+
glVertex3f(-100, 100, 100);
112+
glVertex3f( 100, -100, 100);
113+
glVertex3f( 100, 100, 100);
114+
115+
/*
116+
...
117+
... repeat for 5 more faces of cube
118+
...
119+
*/
120+
121+
glEnd();
122+
{{/escapehtml}}</pre>
123+
124+
请注意,在这两个示例中我们几乎不需要任何 3D 数学知识。对比来看,WebGL 就不是这样。我不会去写 WebGL 所需的完整代码——代码本身其实不会多很多。关键不在于代码行数的多少,而在于所需的知识量。
125+
126+
在这两个 3D 库中,它们帮你处理了所有 3D 相关的事情。你只需要提供一个摄像机的位置和视野、几个光源以及一个立方体,其余的它们都会帮你搞定。换句话说:它们是真正的 3D 库。
127+
128+
请注意,在这两个示例中我们几乎不需要任何 3D 数学知识。对比来看,WebGL 就不是这样。我不会去写 WebGL 所需的完整代码——代码本身其实不会多很多。关键不在于代码行数的多少,而在于所需的知识量。
129+
130+
在这两个 3D 库中,它们帮你处理了所有 3D 相关的事情。你只需要提供一个摄像机的位置和视野、几个光源以及一个立方体,其余的它们都会帮你搞定。换句话说:它们是真正的 3D 库。
131+
132+
请注意,在这两个示例中我们几乎不需要任何 3D 数学知识。对比来看,WebGL 就不是这样。我不会去写 WebGL 所需的完整代码——代码本身其实不会多很多。关键不在于代码行数的多少,而在于所需的知识量。
133+
134+
在这两个 3D 库中,它们帮你处理了所有 3D 相关的事情。 你只需要提供一个摄像机的位置和视野、几个光源以及一个立方体,其余的它们都会帮你搞定。
135+
换句话说:它们是真正的 3D 库。
136+
137+
而在 WebGL 中,你则需要掌握矩阵运算、归一化坐标、视锥体、叉积、点积、varying 插值、光照、高光计算等等一系列内容,而这些通常需要几个月甚至几年的时间才能真正理解和掌握。
138+
139+
一个 3D 库的核心意义就在于它内部已经封装好了这些知识,因此你不需要自己去掌握它们,你只需要依赖这个库来帮你完成处理。正如上文所示,这一点在最初的 OpenGL 中就成立,对像 three.js 这样的其他 3D 库同样适用。但对于 OpenGL ES 2.0+ 或 WebGL 来说,这种封装是不存在的。
140+
141+
称 WebGL 为一个 3D 库似乎是具有误导性的。一个初学者接触 WebGL 时可能会想:“哦,这是个 3D 库,太棒了,它会帮我处理 3D。”然而他们最终会痛苦地发现,事实根本不是这样。
142+
143+
我们甚至可以更进一步。下面是使用 Canvas 绘制 3D 线框立方体的示例。
144+
145+
{{{example url="resources/3d-in-canvas.html" }}}
146+
147+
下面是使用 WebGL 绘制线框立方体的示例。
148+
149+
{{{example url="resources/3d-in-webgl.html" }}}
150+
151+
如果你检查这两段代码,就会发现它们在所需知识量或代码量方面并没有太大差异。归根结底,Canvas 版本是遍历顶点,使用我们提供的数学计算,然后在 2D 中绘制一些线条;而 WebGL 版本做的也是同样的事情,只不过这些数学计算是我们写在 GLSL 中,由 GPU 执行的。
152+
153+
这最后一个演示的重点是说明 WebGL 本质上只是一个光栅化引擎,就像 Canvas 2D 一样。确实,WebGL 提供了一些有助于实现 3D 的功能,比如深度缓冲区,它让深度排序变得比没有深度的系统简单得多。
154+
WebGL 还内置了各种数学函数,非常适合用于 3D 数学计算,尽管严格来说,这些函数本身并不属于“3D”的范畴——它们只是数学库,无论你是用于一维、二维还是三维计算都可以使用。
155+
但归根结底,WebGL 只负责光栅化。你必须自己提供裁剪空间(clip space)坐标来表示你想绘制的内容。确实,你可以提供 x, y, z, w,WebGL 会在渲染前将其除以 w,但这远远不足以让 WebGL 被称为一个“3D 库”。
156+
在一个真正的 3D 库中,你只需提供 3D 数据,库会帮你完成从 3D 到裁剪空间坐标的全部计算。
157+
158+
为了提供更多参考信息, [emscripten](https://emscripten.org/) 在 WebGL 之上实现了旧版 OpenGL 的仿真。相关代码在
159+
[这里](https://github.com/emscripten-core/emscripten/blob/main/src/lib/libglemu.js)
160+
161+
如果你查看这段代码,你会发现其中很大一部分是在生成着色器,用来模拟 OpenGL ES 2.0 中被移除的旧版 OpenGL 的 3D 部分。
162+
163+
你也可以在 [Regal](https://chromium.googlesource.com/external/p3/regal/+/refs/heads/master/src/regal/RegalIff.cpp) 中看到类似的做法。
164+
Regal 是 NVIDIA 发起的一个项目,旨在在现代 OpenGL 中仿真包含 3D 功能的旧版 OpenGL,而现代 OpenGL 已经不再内建这些 3D 功能。
165+
166+
再举一个例子,[three.js 所使用的着色器](https://gist.github.com/greggman/41d93c00649cba78abdbfc1231c9158c)
167+
就展示了如何在库内部提供 3D 功能。你可以看到这些例子中都做了大量工作。
168+
169+
所有这些 3D 功能以及背后的支持代码,都是由这些库提供的,而不是由 WebGL 自身提供的。
170+
171+
我希望你至少能理解我所说的“WebGL 不是一个 3D 库”是什么意思。我也希望你能意识到,一个真正的 3D 库应该为你处理好所有 3D 的相关部分。
172+
OpenGL 做到了这一点。Three.js 也做到了。而 OpenGL ES 2.0 和 WebGL 则没有。
173+
因此,可以说它们并不属于“3D 库”这个广义分类下。
174+
175+
这一切的重点,是为了让刚接触 WebGL 的开发者理解 WebGL 的本质。
176+
了解 WebGL 并不是一个 3D 库,而是一个栅格化 API,意味着你需要自己掌握所有与 3D 相关的知识。这能帮助你明确接下来的学习方向——是深入学习 3D 数学知识,还是选择一个能为你处理好这些细节的 3D 库来简化开发。
177+
同时,这也能帮助你揭开 WebGL 工作原理背后的许多神秘面纱。

0 commit comments

Comments
 (0)