aboutsummaryrefslogtreecommitdiff
path: root/files/zh-cn/web/api/webglrenderingcontext/vertexattribpointer/index.html
blob: 9398bab8234c981e7cdff61f103f9a401340c89d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
---
title: WebGLRenderingContext.vertexAttribPointer()
slug: Web/API/WebGLRenderingContext/vertexAttribPointer
translation_of: Web/API/WebGLRenderingContext/vertexAttribPointer
---
<div>{{APIRef("WebGL")}}</div>

<p class="summary">The <strong><code>WebGLRenderingContext.vertexAttribPointer()</code></strong> method of the <a href="/en-US/docs/Web/API/WebGL_API">WebGL API</a> binds the buffer currently bound to <code>gl.ARRAY_BUFFER</code> to a generic vertex attribute of the current vertex buffer object and specifies its layout.<br>
 告诉显卡从当前绑定的缓冲区(bindBuffer()指定的缓冲区)中读取顶点数据。</p>

<p class="summary"> WebGL API 的<code><strong>WebGLRenderingContext.vertexAttribPointer()</strong></code>方法绑定当前缓冲区范围到<code>gl.ARRAY_BUFFER</code>,成为当前顶点缓冲区对象的通用顶点属性并指定它的布局(缓冲区对象中的偏移量)。</p>

<h2 id="Syntax">Syntax</h2>

<pre class="syntaxbox">void <em>gl</em>.vertexAttribPointer(<em>index</em>, <em>size</em>, <em>type</em>, <em>normalized</em>, <em>stride</em>, <em>offset</em>);
</pre>

<h3 id="Parameters">Parameters</h3>

<dl>
 <dt><code>index</code></dt>
 <dd>A {{domxref("GLuint")}} specifying the index of the vertex attribute that is to be modified.<br>
 指定要修改的顶点属性的索引。</dd>
 <dt><code>size</code></dt>
 <dd>A {{domxref("GLint")}} specifying the number of components per vertex attribute. Must be 1, 2, 3, or 4.<br>
 指定每个顶点属性的组成数量,必须是1,2,3或4。</dd>
 <dt><code>type</code></dt>
 <dd>A {{domxref("GLenum")}} specifying the data type of each component in the array. Possible values:<br>
 指定数组中每个元素的数据类型可能是:
 <ul>
  <li><code>gl.BYTE</code>: signed 8-bit integer, with values in [-128, 127]<br>
   有符号的8位整数,范围[-128, 127]</li>
  <li><code>gl.SHORT</code>: signed 16-bit integer, with values in [-32768, 32767]<br>
   有符号的16位整数,范围[-32768, 32767]</li>
  <li><code>gl.UNSIGNED_BYTE</code>: unsigned 8-bit integer, with values in [0, 255]<br>
   无符号的8位整数,范围[0, 255]</li>
  <li><code>gl.UNSIGNED_SHORT</code>: unsigned 16-bit integer, with values in [0, 65535]<br>
   无符号的16位整数,范围[0, 65535]</li>
  <li><code>gl.FLOAT</code>: 32-bit IEEE floating point number<br>
   32位IEEE标准的浮点数</li>
  <li>When using a {{domxref("WebGL2RenderingContext", "WebGL 2 context", "", 1)}}, the following values are available additionally:<br>
   使用WebGL2版本的还可以使用以下值:
   <ul>
    <li><code>gl.HALF_FLOAT</code>: 16-bit IEEE floating point number<br>
     16位IEEE标准的浮点数</li>
   </ul>
  </li>
 </ul>
 </dd>
 <dt><code>normalized</code></dt>
 <dd>A {{domxref("GLboolean")}} specifying whether integer data values should be normalized into a certain range when being casted to a float.<br>
 当转换为浮点数时是否应该将整数数值归一化到特定的范围。
 <ul>
  <li>For types <code>gl.BYTE</code> and <code>gl.SHORT</code>, normalizes the values to [-1, 1] if true.<br>
   对于类型<code>gl.BYTE</code><code>gl.SHORT</code>,如果是true则将值归一化为[-1, 1]</li>
  <li>For types <code>gl.UNSIGNED_BYTE</code> and <code>gl.UNSIGNED_SHORT</code>, normalizes the values to [0, 1] if true.<br>
   对于类型<code>gl.UNSIGNED_BYTE</code><code>gl.UNSIGNED_SHORT</code>,如果是true则将值归一化为[0, 1]</li>
  <li>For types <code>gl.FLOAT</code> and <code>gl.HALF_FLOAT</code>, this parameter has no effect.<br>
   对于类型<code>gl.FLOAT</code><code>gl.HALF_FLOAT</code>,此参数无效</li>
 </ul>
 </dd>
 <dt><code>stride</code></dt>
 <dd>A {{domxref("GLsizei")}} specifying the offset in bytes between the beginning of consecutive vertex attributes. Cannot be larger than 255. If stride is 0, the attribute is assumed to be tightly packed, that is, the attributes are not interleaved but each attribute is in a separate block, and the next vertex' attribute follows immediately after the current vertex.</dd>
 <dd>一个GLsizei,以字节为单位指定连续顶点属性开始之间的偏移量(即数组中一行长度)。不能大于255。如果stride为0,则假定该属性是紧密打包的,即不交错属性,每个属性在一个单独的块中,下一个顶点的属性紧跟当前顶点之后。</dd>
 <dt></dt>
 <dt><code>offset</code></dt>
 <dd>A {{domxref("GLintptr")}} specifying an offset in bytes of the first component in the vertex attribute array. Must be a multiple of the byte length of <code>type</code>.</dd>
 <dd>{{domxref("GLintptr")}}指定顶点属性数组中第一部分的字节偏移量。必须是类型的字节长度的倍数。</dd>
</dl>

<h3 id="Return_value">Return value</h3>

<p>None.</p>

<h3 id="Exceptions">Exceptions</h3>

<ul>
 <li>A <code>gl.INVALID_VALUE</code> error is thrown if <code>offset</code> is negative.</li>
 <li>如果偏移量为负,则抛出<code>gl.INVALID_VALUE</code>错误。</li>
 <li>A <code>gl.INVALID_OPERATION</code> error is thrown if <code>stride</code> and <code>offset</code> are not multiples of the size of the data type.</li>
 <li>如果<code>stride</code>和offset不是数据类型大小的倍数,则抛出<code>gl.INVALID_OPERATION</code>错误。</li>
 <li>A <code>gl.INVALID_OPERATION</code> error is thrown if no WebGLBuffer is bound to the ARRAY_BUFFER target.</li>
 <li>如果没有将WebGLBuffer绑定到<code>ARRAY_BUFFER</code>目标,则抛出<code>gl.INVALID_OPERATION</code>错误。</li>
 <li>When using a {{domxref("WebGL2RenderingContext", "WebGL 2 context", "", 1)}}, a <code>gl.INVALID_OPERATION</code> error is thrown if this vertex attribute is defined as a integer in the vertex shader (e.g. <code>uvec4</code> or <code>ivec4</code>, instead of <code>vec4</code>).</li>
</ul>

<h2 id="Description">Description</h2>

<p>Let's assume we want to render some 3D geometry, and for that we will need to supply our vertices to the Vertex Shader. Each vertex has a few attributes, like position, normal vector, or texture coordinate, that are defined in an {{jsxref("ArrayBuffer")}} and will be supplied to the Vertex Buffer Object (VBO). First, we need to bind the {{domxref("WebGLBuffer")}} we want to use to <code>gl.ARRAY_BUFFER</code>, then, with this method, <code>gl.vertexAttribPointer()</code>, we specify in what order the attributes are stored, and what data type they are in. In addition, we need to include the stride, which is the total byte length of all attributes for one vertex. Also, we have to call {{domxref("WebGLRenderingContext/enableVertexAttribArray", "gl.enableVertexAttribArray()")}} to tell WebGL that this attribute should be filled with data from our array buffer.</p>

<p>Usually, your 3D geometry is already in a certain binary format, so you need to read the specification of that specific format to figure out the memory layout. However, if you are designing the format yourself, or your geometry is in text files (like <a href="https://en.wikipedia.org/wiki/Wavefront_.obj_file">Wavefront .obj files</a>) and must be converted into an <code>ArrayBuffer</code> at runtime, you have free choice on how to structure the memory. For highest performance, <a href="https://en.wikipedia.org/wiki/Interleaved_memory">interleave</a> the attributes and use the smallest data type that still accurately represents your geometry.</p>

<p>The maximum number of vertex attributes depends on the graphics card, and you can call <code>gl.getParameter(gl.MAX_VERTEX_ATTRIBS)</code> to get this value. On high-end graphics cards, the maximum is 16, on lower-end graphics cards, the value will be lower.</p>

<h3 id="Attribute_index">Attribute index</h3>

<p>For each attribute, you must specify its index. This is independent from the location inside the array buffer, so your attributes can be sent in a different order than how they are stored in the array buffer. You have two options:</p>

<ul>
 <li>Either you specify the index yourself. In this case, you call {{domxref("WebGLRenderingContext.bindAttribLocation()", "gl.bindAttribLocation()")}} to connect a named attribute from the vertex shader to the index you want to use. This must be done before calling {{domxref("WebGLRenderingContext.linkProgram()", "gl.linkProgram()")}}. You can then provide this same index to <code>gl.vertexAttribPointer()</code>.</li>
 <li>Alternatively, you use the index that is assigned by the graphics card when compiling the vertex shader. Depending on the graphics card, the index will vary, so you must call {{domxref("WebGLRenderingContext.getAttribLocation()", "gl.getAttribLocation()")}} to find out the index, and then provide this index to <code>gl.vertexAttribPointer()</code>.<br>
  If you are using WebGL 2, you can specify the index yourself in the vertex shader code and override the default used by the graphics card, e.g. <code>layout(location = 3) in vec4 position;</code> would set the <code>"position"</code> attribute to index 3.</li>
</ul>

<h3 id="Integer_attributes">Integer attributes</h3>

<p>While the <code>ArrayBuffer</code> can be filled with both integers and floats, the attributes will always be converted to a float when they are sent to the vertex shader. If you need to use integers in your vertex shader code, you can either cast the float back to an integer in the vertex shader (e.g. <code>(int) floatNumber</code>), or use {{domxref("WebGL2RenderingContext.vertexAttribIPointer()", "gl.vertexAttribIPointer()")}} from WebGL2.</p>

<h3 id="Default_attribute_values">Default attribute values</h3>

<p>The vertex shader code may include a number of attributes, but we don't need to specify the values for each attribute. Instead, we can supply a default value that will be identical for all vertices. We can call <code>{{domxref("WebGLRenderingContext.disableVertexAttribArray()", "gl.disableVertexAttribArray()")}}</code> to tell WebGL to use the default value, while calling {{domxref("WebGLRenderingContext.enableVertexAttribArray()", "gl.enableVertexAttribArray()")}} will read the values from the array buffer as specified with <code>gl.vertexAttribPointer()</code>.</p>

<p>Similarily, if our vertex shader expects e.g. a 4-component attribute with <code>vec4</code> but in our <code>gl.vertexAttribPointer()</code> call we set the <code>size</code> to <code>2</code>, then WebGL will set the first two components based on the array buffer, while the third and fourth components are taken from the default value.</p>

<p>The default value is <code>vec4(0.0, 0.0, 0.0, 1.0)</code> by default but we can specify a different default value with <code>{{domxref("WebGLRenderingContext.vertexAttrib()", "gl.vertexAttrib[1234]f[v]()")}}</code>.</p>

<p>For example, your vertex shader may be using a position and a color attribute. Most meshes have the color specified at a per-vertex level, but some meshes are of a uniform shade. For those meshes, it is not necessary to place the same color for each vertex into the array buffer, so you use <code>gl.vertexAttrib4fv()</code> to set a constant color.</p>

<h3 id="Querying_current_settings">Querying current settings</h3>

<p>You can call {{domxref("WebGLRenderingContext.getVertexAttrib()", "gl.getVertexAttrib()")}} and {{domxref("WebGLRenderingContext.getVertexAttribOffset()", "gl.getVertexAttribOffset()")}} to get the current parameters for an attribute, e.g. the data type or whether the attribute should be normalized. Keep in mind that these WebGL functions have a slow performance and it is better to store the state inside your JavaScript application. However, these functions are great for debugging a WebGL context without touching the application code.</p>

<h2 id="Examples">Examples</h2>

<p>This example shows how to send your vertex attributes to the shader program. We use an imaginary data structure where the attributes of each vertex are stored interleaved with a length of 20 bytes per vertex:</p>

<ol>
 <li><strong>position:</strong> We need to store the X, Y and Z coordinates. For highest precision, we use 32-bit floats; in total this uses 12 bytes.</li>
 <li><strong>normal vector:</strong> We need to store the X, Y and Z components of the normal vector, but since precision is not that important, we use 8-bit signed integers. For better performance, we align the data to 32 bits by also storing a fourth zero-valued component, bringing the total size to 4 bytes. Also, we tell WebGL to normalize the values because our normals are always in range [-1, 1].</li>
 <li><strong>texture coordinate:</strong> We need to store the U and V coordinates; for this 16-bit unsigned integers offer enough precision, the total size is 4 bytes. We also tell WebGL to normalize the values to [0, 1].</li>
</ol>

<p>For example, the following vertex:</p>

<pre class="brush: json">{
  "position": [1.0, 2.0, 1.5],
  "normal": [1.0, 0.0, 0.0],
  "texCoord": [0.5, 0.25]
}
</pre>

<p>Will be stored in the array buffer as follows:</p>

<table style="width: auto;">
 <tbody>
  <tr>
   <td style="background-color: #ccf;">00 00 80 3F</td>
   <td style="background-color: #ddf;">00 00 00 40</td>
   <td style="background-color: #ccf;">00 00 0C 3F</td>
   <td style="background-color: #cfc;">7F</td>
   <td style="background-color: #dfd;">00</td>
   <td style="background-color: #cfc;">00</td>
   <td style="background-color: #dfd;">00</td>
   <td style="background-color: #fcc;">7F FF</td>
   <td style="background-color: #fdd;">3F FF</td>
  </tr>
 </tbody>
</table>

<h3 id="Creating_the_array_buffer">Creating the array buffer</h3>

<p>First, we dynamically create the array buffer from JSON data using a {{domxref("DataView")}}. Note the use of <code>true</code> because WebGL expects our data to be in little-endian.</p>

<pre class="brush: js">//load geometry with fetch() and Response.json()
const response = await fetch('assets/geometry.json');
const vertices = await response.json();

//Create array buffer
const buffer = new ArrayBuffer(20 * vertices.length);
//Fill array buffer
const dv = new DataView(buffer);
for (let i = 0; i &lt; vertices.length; i++) {
  dv.setFloat32(20 * i, vertices[i].position[0], true);
  dv.setFloat32(20 * i + 4, vertices[i].position[1], true);
  dv.setFloat32(20 * i + 8, vertices[i].position[2], true);
  dv.setInt8(20 * i + 12, vertices[i].normal[0] * 0x7F);
  dv.setInt8(20 * i + 13, vertices[i].normal[1] * 0x7F);
  dv.setInt8(20 * i + 14, vertices[i].normal[2] * 0x7F);
  dv.setInt8(20 * i + 15, 0);
  dv.setUint16(20 * i + 16, vertices[i].texCoord[0] * 0xFFFF, true);
  dv.setUint16(20 * i + 18, vertices[i].texCoord[1] * 0xFFFF, true);
}</pre>

<p>For higher performance, we could also do the previous JSON to ArrayBuffer conversion on the server-side, e.g. with Node.js. Then we could load the binary file and interpret it as an array buffer:</p>

<pre class="brush: js">const response = await fetch('assets/geometry.bin');
const buffer = await response.arrayBuffer();
</pre>

<h3 id="Consume_array_buffer_with_WebGL">Consume array buffer with WebGL</h3>

<p>First, we create a new Vertex Buffer Object (VBO) and supply it with our array buffer:</p>

<pre class="brush: js">//Bind array buffer to a Vertex Buffer Object
const vbo = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, vbo);
gl.bufferData(gl.ARRAY_BUFFER, buffer, gl.STATIC_DRAW);
</pre>

<p>Then, we specify the memory layout of the array buffer, either by setting the index ourselves:</p>

<pre class="brush: js, highlight:[3, 6, 9]">//Describe the layout of the buffer:
//1. position, not normalized
gl.vertexAttribPointer(0, 3, gl.FLOAT, false, 20, 0);
gl.enableVertexAttribArray(0);
//2. normal vector, normalized to [-1, 1]
gl.vertexAttribPointer(1, 4, gl.BYTE, true, 20, 12);
gl.enableVertexAttribArray(1);
//3. texture coordinates, normalized to [0, 1]
gl.vertexAttribPointer(2, 2, gl.UNSIGNED_SHORT, true, 20, 16);
gl.enableVertexAttribArray(2);

//Set the attributes in the vertex shader to the same indices
gl.bindAttribLocation(shaderProgram, 0, 'position');
gl.bindAttribLocation(shaderProgram, 1, 'normal');
gl.bindAttribLocation(shaderProgram, 2, 'texUV');
//Since the attribute indices have changed, we must re-link the shader
//Note that this will reset all uniforms that were previously set.
gl.linkProgram(shaderProgram);
</pre>

<p>Or we can use the index provided by the graphics card instead of setting the index ourselves; this avoids the re-linking of the shader program.</p>

<pre class="brush: js, highlight:[2, 6, 10]">const locPosition = gl.getAttribLocation(shaderProgram, 'position');
gl.vertexAttribPointer(locPosition, 3, gl.FLOAT, false, 20, 0);
gl.enableVertexAttribArray(locPosition);

const locNormal = gl.getAttribLocation(shaderProgram, 'normal');
gl.vertexAttribPointer(locNormal, 4, gl.BYTE, true, 20, 12);
gl.enableVertexAttribArray(locNormal);

const locTexUV = gl.getAttribLocation(shaderProgram, 'texUV');
gl.vertexAttribPointer(locTexUV, 2, gl.UNSIGNED_SHORT, true, 20, 16);
gl.enableVertexAttribArray(locTexUV);
</pre>

<h2 id="Specifications">Specifications</h2>

<table class="standard-table">
 <tbody>
  <tr>
   <th scope="col">Specification</th>
   <th scope="col">Status</th>
   <th scope="col">Comment</th>
  </tr>
  <tr>
   <td>{{SpecName('WebGL', "#5.14.10", "vertexAttribPointer")}}</td>
   <td>{{Spec2('WebGL')}}</td>
   <td>Initial definition.</td>
  </tr>
  <tr>
   <td>{{SpecName('OpenGL ES 2.0', "glVertexAttribPointer.xml", "glVertexAttribPointer")}}</td>
   <td>{{Spec2('OpenGL ES 2.0')}}</td>
   <td>Man page of the OpenGL API.</td>
  </tr>
 </tbody>
</table>

<h2 id="Browser_compatibility">Browser compatibility</h2>

<p>{{Compat("api.WebGLRenderingContext.vertexAttribPointer")}}</p>

<h2 id="See_also">See also</h2>

<ul>
 <li><a href="https://www.khronos.org/opengl/wiki/Vertex_Specification">Vertex Specification</a> on the OpenGL wiki</li>
 <li>{{domxref("WebGL2RenderingContext.vertexAttribIPointer()")}}</li>
</ul>