1
- Title: WebGL2 Pulling Vertices
2
- Description: Using independent indices
3
- TOC: Pulling Vertices
1
+ Title: WebGL2 顶点拉取
2
+ Description: 使用独立索引
3
+ TOC: 顶点拉取
4
4
5
- This article assumes you've read many of the other articles
6
- starting with [ the fundamentals] ( webgl-fundamentals.html ) .
7
- If you have not read them please start there first.
5
+ 本文假设你已经阅读了其他许多文章,从 [ 基础知识] ( webgl-fundamentals.html ) 开始。如果你还没有阅读它们,请先从那里开始。
8
6
9
- Traditionally, WebGL apps put geometry data in buffers.
10
- They then use attributes to automatically supply vertex data from those buffers
11
- to the vertex shader where the programmer provides code to convert them to clip space.
7
+ 传统上,WebGL应用会将几何数据放入缓冲区中,然后通过属性(attributes)自动将这些缓冲区中的顶点数据传递给顶点着色器,由程序员编写代码将其转换为裁剪空间(clip space)坐标。
12
8
13
- The word ** traditionally** is important. It's only a ** tradition**
14
- to do it this way. It is in no way a requirement. WebGL doesn't
15
- care how we do it, it only cares that our vertex shaders
16
- assign clip space coordinates to ` gl_Position ` .
9
+ 这里的 ** “传统上”** 非常重要。
10
+ 这只是一种** 传统做法** ,并不是必须如此。
11
+ WebGL 并不关心我们是如何处理的,它只关心顶点着色器是否为 ` gl_Position ` 赋予了裁剪空间坐标。
17
12
18
- Let's draw a texture mapped cube using code like the examples in [ the article on textures] ( webgl-3d-textures.html ) .
19
- We're told we need at least 24 unique vertices. This is because even though there are only 8 corner
20
- positions the same corner gets used on 3 different faces of the
21
- cube and each face needs different texture coordinates.
13
+ 让我们使用类似于 [ 纹理] ( webgl-3d-textures.html ) 中示例的方式,绘制一个带纹理映射的立方体。我们通常会说需要至少 24 个唯一顶点,这是因为虽然立方体只有 8 个角点位置,但每个角点会出现在立方体的 3 个不同面上,而每个面又需要不同的纹理坐标。
22
14
23
15
<div class =" webgl_center " ><img src =" resources/cube-vertices-uv.svg " style =" width : 400px ;" ></div >
24
16
25
- In the diagram above we can see that the left face's use of corner 3 needs
26
- texture coordinates 1,1 but the right face's use of corner 3 needs texture coordinates
27
- 0,1. The top face would need different texture coordinates as well.
17
+ 在上面的图示中,我们可以看到左侧面的角点 3 需要的纹理坐标是 (1,1),而右侧面对角点 3 的使用则需要纹理坐标 (0,1)。顶部面则会需要另一组不同的纹理坐标。
18
+
19
+ 通常,我们是通过将 8 个角点位置扩展为 24 个顶点来实现这一点的。
28
20
29
- This is usually accomplished by expanding from 8 corner positions
30
- to 24 vertices
31
21
32
22
``` js
33
23
// front
@@ -62,13 +52,10 @@ to 24 vertices
62
52
{ pos: [- 1 , - 1 , - 1 ], uv: [1 , 0 ], }, // 23
63
53
```
64
54
65
- Those positions and texture coordinates are
66
- put into buffers and provided to the vertex shader
67
- via attributes.
55
+ 这些位置和纹理坐标通常会被放入缓冲区中,并通过属性传递给顶点着色器。
68
56
69
- But do we really need to do it this way? What if
70
- we wanted to actually have just the 8 corners
71
- and 4 texture coordinates. Something like
57
+ 但我们真的需要以这种方式来做吗?如果我们实际上只想保留 8 个角点和 4 个纹理坐标,会怎样?
58
+ 类似于下面这样:
72
59
73
60
``` js
74
61
positions = [
@@ -89,8 +76,7 @@ uvs = [
89
76
];
90
77
```
91
78
92
- And then for each of the 24 vertices we'd specify which of those
93
- to use.
79
+ 然后,对于这 24 个顶点中的每一个,我们指定要使用哪一个位置和哪一个纹理坐标。
94
80
95
81
``` js
96
82
positionIndexUVIndex = [
@@ -127,11 +113,9 @@ positionIndexUVIndex = [
127
113
];
128
114
```
129
115
130
- Could we use this on the GPU? Why not!?
116
+ 我们能在 GPU 上使用这种方式吗?为什么不可以!
131
117
132
- We'll upload the positions and texture coordinates
133
- each to their own textures like
134
- we covered in [ the article on data textures] ( webgl-data-textures.html ) .
118
+ 我们会将位置和纹理坐标分别上传到各自的纹理中,就像我们在 [ 数据纹理] ( webgl-data-textures.html ) 中讲到的那样。
135
119
136
120
``` js
137
121
function makeDataTexture (gl , data , numComponents ) {
@@ -168,10 +152,10 @@ const positionTexture = makeDataTexture(gl, positions, 3);
168
152
const texcoordTexture = makeDataTexture (gl, uvs, 2 );
169
153
```
170
154
171
- Since textures have up to 4 values per pixel ` makeDataTexture `
172
- expands whatever data we give it to 4 values per pixel.
155
+ 由于纹理每个像素最多可以存储 4 个值,` makeDataTexture ` 会将我们提供的数据扩展为每像素 4 个值。
156
+
157
+ 接着,我们会创建一个顶点数组对象(vertex array)来保存我们的属性状态。
173
158
174
- Then we'll create a vertex array to hold our attribute state
175
159
176
160
``` js
177
161
// create a vertex array object to hold attribute state
@@ -191,7 +175,7 @@ gl.bindBuffer(gl.ARRAY_BUFFER, positionIndexUVIndexBuffer);
191
175
gl .bufferData (gl .ARRAY_BUFFER , new Uint32Array (positionIndexUVIndex), gl .STATIC_DRAW );
192
176
```
193
177
194
- and setup the attribute
178
+ 接下来,我们需要将位置索引和纹理坐标索引上传到一个缓冲区。
195
179
196
180
``` js
197
181
// Turn on the position index attribute
@@ -209,14 +193,13 @@ gl.enableVertexAttribArray(posTexIndexLoc);
209
193
}
210
194
```
211
195
212
- Notice we're calling ` gl.vertexAttribIPointer ` not ` gl.vertexAttribPointer ` .
213
- The ` I ` is for integer and is used for integer and unsigned integer attributes.
214
- Also note the size is 2, since there is 1 position index and 1 texcoord
215
- index per vertex.
196
+ 注意这里调用的是 ` gl.vertexAttribIPointer ` ,而不是 ` gl.vertexAttribPointer ` 。
197
+ 其中的 ` I ` 表示整数,用于整数和无符号整数类型的属性。
198
+ 另外,` size ` 设置为 2,因为每个顶点包含 1 个位置索引和 1 个纹理坐标索引。
199
+
200
+ 虽然我们只需要 24 个顶点,但绘制 6 个面,每个面 12 个三角形,每个三角形 3 个顶点,总共 36 个顶点。
201
+ 为了指定每个面使用哪 6 个顶点,我们将使用 [ 顶点索引] ( webgl-indexed-vertices.html ) 。
216
202
217
- Even though we only need 24 vertices we still need draw 6 faces, 12 triangles
218
- each, 3 vertices per triangle for 36 vertices. To tell it which 6 vertices
219
- to use for each face we'll use [ vertex indices] ( webgl-indexed-vertices.html ) .
220
203
221
204
``` js
222
205
const indices = [
@@ -234,9 +217,10 @@ gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
234
217
gl .bufferData (gl .ELEMENT_ARRAY_BUFFER , new Uint16Array (indices), gl .STATIC_DRAW );
235
218
```
236
219
237
- As we want to draw an image on the cube itself we need a 3rd texture
238
- with that image. Let's just make another 4x4 data texture with a checkerboard.
239
- We'll use ` gl.LUMINANCE ` as the format since then we only need one byte per pixel.
220
+ 由于我们想要在立方体上绘制一张图像,因此还需要第三个纹理存储这张图像。
221
+ 这里我们用一个 4x4 的数据纹理,内容是棋盘格图案。
222
+ 纹理格式使用 ` gl.LUMINANCE ` ,因为这样每个像素只需要一个字节。
223
+
240
224
241
225
``` js
242
226
// Create a checker texture.
@@ -263,17 +247,16 @@ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
263
247
gl .texParameteri (gl .TEXTURE_2D , gl .TEXTURE_MAG_FILTER , gl .NEAREST );
264
248
```
265
249
266
- On to the vertex shader... We can look up a pixel from the texture like
267
- this
250
+ 接下来是顶点着色器……
251
+ 我们可以像这样从纹理中查找一个像素:
268
252
269
253
``` glsl
270
254
vec4 color = texelFetch(sampler2D tex, ivec2 pixelCoord, int mipLevel);
271
255
```
272
256
273
- So given an integer pixel coordinate the code above will pull out a pixel value.
257
+ 因此,给定一个整数像素坐标,上述代码将提取出对应的像素值。
274
258
275
- Using the ` texelFetch ` function we can take a 1D array index
276
- and lookup a value out of a 2D texture like this
259
+ 使用 ` texelFetch ` 函数,我们可以将一维数组索引转换为二维纹理坐标,并从二维纹理中查找对应的值,方式如下:
277
260
278
261
``` glsl
279
262
vec4 getValueByIndexFromTexture(sampler2D tex, int index) {
@@ -284,7 +267,7 @@ vec4 getValueByIndexFromTexture(sampler2D tex, int index) {
284
267
}
285
268
```
286
269
287
- So given that function here is our shader
270
+ 有了这个函数,我们的着色器如下所示:
288
271
289
272
``` glsl
290
273
#version 300 es
@@ -321,16 +304,11 @@ void main() {
321
304
}
322
305
```
323
306
324
- At the bottom it's effectively the same shader we used
325
- in [ the article on textures] ( webgl-3d-textures.html ) .
326
- We multiply a ` position ` by ` u_matrix ` and we output
327
- a texcoord to ` v_texcoord ` to pass on the fragment shader.
307
+ 在底部,它实际上和我们在 [ 纹理] ( webgl-3d-textures.html ) 中使用的着色器是一样的。我们将 ` position ` 与 ` u_matrix ` 相乘,并将纹理坐标输出到 ` v_texcoord ` ,以传递给片元着色器。
328
308
329
- The difference is only in how we get the position and
330
- texcoord. We're using the indices passed in and getting
331
- those values from their respective textures.
309
+ 不同之处仅在于我们获取 ` position ` 和 ` texcoord ` 的方式。我们使用传入的索引,从各自的纹理中提取这些值。
332
310
333
- To use the shader we need to lookup all the locations
311
+ 要使用这个着色器,我们需要查找所有相关的变量位置。
334
312
335
313
``` js
336
314
// setup GLSL program
@@ -347,7 +325,7 @@ const program = webglUtils.createProgramFromSources(gl, [vs, fs]);
347
325
+ const u_textureLoc = gl .getUniformLocation (program, " u_texture" );
348
326
```
349
327
350
- At render time we setup the attributes
328
+ 在渲染阶段,我们设置属性( attributes)。
351
329
352
330
``` js
353
331
// Tell it to use our program (pair of shaders)
@@ -357,8 +335,7 @@ gl.useProgram(program);
357
335
gl .bindVertexArray (vao);
358
336
```
359
337
360
- Then we need to bind all 3 textures and setup all the
361
- uniforms
338
+ 然后,我们需要绑定全部 3 个纹理,并设置所有的 uniform。
362
339
363
340
``` js
364
341
// Set the matrix.
@@ -383,74 +360,69 @@ gl.bindTexture(gl.TEXTURE_2D, checkerTexture);
383
360
gl .uniform1i (u_textureLoc, 2 );
384
361
```
385
362
386
- And finally draw
363
+ 最后,执行绘制操作。
387
364
388
365
``` js
389
366
// Draw the geometry.
390
367
gl .drawElements (gl .TRIANGLES , 6 * 6 , gl .UNSIGNED_SHORT , 0 );
391
368
```
392
369
393
- And we get a textured cube using only 8 positions and
394
- 4 texture coordinates
370
+ 最终,我们只使用了 8 个位置和 4 个纹理坐标,就得到了一个带贴图的立方体。
395
371
396
372
{{{example url="../webgl-pulling-vertices.html"}}}
397
373
398
- Some things to note. The code is lazy and uses 1D
399
- textures for the positions and texture coordinates.
400
- Textures can only be so wide. [ How wide is machine
401
- specific] ( https://web3dsurvey.com/webgl/parameters/MAX_TEXTURE_SIZE ) which you can query with
374
+ 有几点需要注意:代码实现较为简化,使用了 1D 纹理来存储位置和纹理坐标。
375
+ 但纹理的宽度是有限的,[ 具体有多宽依赖于硬件] ( https://web3dsurvey.com/webgl/parameters/MAX_TEXTURE_SIZE ) ,
376
+ 你可以通过以下方式查询:
402
377
403
378
``` js
404
379
const maxSize = gl .getParameter (gl .MAX_TEXTURE_SIZE );
405
380
```
406
381
407
- If we wanted to handle more data than that we'd need
408
- to pick some texture size that fits our data, and spread
409
- the data across multiple rows possibly
410
- padding the last row to make a rectangle.
382
+ 如果我们想处理比最大纹理宽度还多的数据,就需要选择一个合适的纹理尺寸,并将数据分布到多行中,可能还需要对最后一行进行填充以保持矩形结构。
383
+
384
+ 我们在这里还做了另一件事:使用了两张纹理,一张存储位置,另一张存储纹理坐标。
385
+ 其实我们完全可以将这两类数据存储在同一张纹理中,例如交错(interleaved)存储。
411
386
412
- Another thing we're doing here is using 2 textures,
413
- one for positions, one for texture coordinates.
414
- There is no reason we couldn't put both data in the
415
- same texture either interleaved
416
387
417
388
pos,uv,pos,uv,pos,uv...
418
389
419
- or in different places in the texture
390
+ 或者将它们存储在纹理的不同区域。
420
391
421
392
pos,pos,pos,...
422
393
uv, uv, uv,...
423
394
424
- We'd just have to change the math in the vertex shader
425
- that computes how to pull them out of the texture.
395
+ 我们只需要修改顶点着色器中的数学逻辑,以正确地从纹理中提取对应的数据。
396
+
397
+ 那么问题来了:是否应该用这种方式?
398
+ 答案是“视情况而定”。具体效果可能因 GPU 而异,有些情况下这比传统方式还慢。
399
+
400
+ 本文的重点再次强调:
401
+ WebGL 并不在意你是如何为 ` gl_Position ` 设置裁剪空间坐标的,也不在意你是如何输出颜色的。它只关心你是否设置了这些值。纹理,本质上只是可以随机访问的二维数组。
402
+
403
+ 当你在 WebGL 中遇到问题时,请记住,WebGL 只是运行一些着色器程序,而这些着色器可以通过以下方式访问数据。
426
404
427
- The question comes up, should you do things like this?
428
- The answer is "it depends". Depending on the GPU this
429
- might be slower than the more traditional way.
405
+ - uniforms(全局变量)
406
+ - attributes(每个顶点着色器执行时接收的数据)
407
+ - textures(可以随机访问的二维数组)
430
408
431
- The point of this article was to point out yet again,
432
- WebGL doesn't care how you set ` gl_Position ` with
433
- clip space coordinates nor does it care how you
434
- output a color. All it cares is that you set them.
435
- Textures are really just 2D arrays of random access
436
- data.
409
+ 不要让传统的 WebGL 使用方式限制了你的思维。
410
+ WebGL 实际上具有极强的灵活性。
437
411
438
- When you have a problem you want to solve in WebGL
439
- remember that WebGL just runs shaders and those shaders
440
- have access to data via uniforms (global variables),
441
- attributes (data that comes per vertex shader iteration),
442
- and textures (random access 2D arrays). Don't let the
443
- traditional ways of using WebGL prevent you from
444
- seeing the real flexibility that's there.
412
+ 当你想在 WebGL 中解决问题时,请记住 WebGL 只是运行着色器,
413
+ 这些着色器可以通过 uniforms(全局变量)、attributes(每次顶点着色器执行时传入的数据)
414
+ 以及 textures(可随机访问的二维数组)来访问数据。
415
+ 不要让传统的 WebGL 使用方式阻碍你发现它真正的灵活性。
445
416
446
417
<div class =" webgl_bottombar " >
447
- <h3 >Why is it called Vertex Pulling?</h3 >
448
- <p >I'd actually only heard the term recently (July 2019)
449
- even though I'd used the technique before. It comes
450
- from <a href =' https://www.google.com/search?q=OpenGL+Insights+"Programmable+Vertex+Pulling"+article+by+Daniel+Rakos ' >OpenGL Insights "Programmable Vertex Pulling" article by Daniel Rakos</a >.
418
+ <h3 >为什么叫做顶点拉取(Vertex Pulling)?</h3 >
419
+ <p >实际上我最近(2019年7月)才听到这个术语,
420
+ 尽管我之前就用过这种技术。
421
+ 它来源于
422
+ <a href =' https://www.google.com/search?q=OpenGL+Insights+"Programmable+Vertex+Pulling"+article+by+Daniel+Rakos ' >
423
+ OpenGL Insights 中 Daniel Rakos 撰写的“可编程顶点拉取”文章</a >。
451
424
</p >
452
- <p >It's called vertex *pulling* since it's the vertex shader
453
- that decides which vertex data to read vs the traditional way where
454
- vertex data is supplied automatically via attributes. Effectively
455
- the vertex shader is * pulling* data out of memory.</p >
425
+ <p >之所以叫做顶点*拉取*,是因为顶点着色器决定读取哪个顶点数据,
426
+ 而传统方式是通过属性自动提供顶点数据。
427
+ 实际上,顶点着色器是在* 拉取* 内存中的数据。</p >
456
428
</div >
0 commit comments