aboutsummaryrefslogtreecommitdiff
path: root/files/ja/web/api/webglrenderingcontext/vertexattribpointer/index.html
blob: bf0e2f05f9150fc5e7e607341021e263bd3ac561 (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
---
title: WebGLRenderingContext.vertexAttribPointer()
slug: Web/API/WebGLRenderingContext/vertexAttribPointer
translation_of: Web/API/WebGLRenderingContext/vertexAttribPointer
---
<div>{{APIRef("WebGL")}}</div>

<p class="summary"><a href="/ja/docs/Web/API/WebGL_API">WebGL API</a><strong><code>WebGLRenderingContext.vertexAttribPointer()</code></strong> メソッドは、現在 <code>gl.ARRAY_BUFFER</code> に結合されたバッファーを、現在の頂点バッファーオブジェクトの一般的な頂点属性に結合して、そのレイアウトを指定します。</p>

<h2 id="構文">構文</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="引数">引数</h3>

<dl>
 <dt><code>index</code></dt>
 <dd>頂点属性の場所を指定する {{domxref("GLuint")}}。この場所が変更されます。</dd>
 <dt><code>size</code></dt>
 <dd>頂点属性あたりの要素数を指定する {{domxref("GLint")}}。1, 2, 3 か, 4 でなければいけません。</dd>
 <dt><code>type</code></dt>
 <dd>配列の各要素のデータ型を指定する {{domxref("GLenum")}}。以下の値を取ることができます。
 <ul>
  <li><code>gl.BYTE</code>: 符号付き 8 ビット整数、値は [-128, 127]</li>
  <li><code>gl.SHORT</code>: 符号付き 16 ビット整数、値は [-32768, 32767]</li>
  <li><code>gl.UNSIGNED_BYTE</code>: 符号無し 8 ビット整数、値は [0, 255]</li>
  <li><code>gl.UNSIGNED_SHORT</code>: 符号無し 16 ビット整数、値は [0, 65535]</li>
  <li><code>gl.FLOAT</code>: 32 ビット IEEE 浮動小数点数</li>
  <li>{{domxref("WebGL2RenderingContext", "WebGL 2 context", "", 1)}} を使用している場合、さらに以下の値を取ることができます。
   <ul>
    <li><code>gl.HALF_FLOAT</code>: 16 ビット IEEE 浮動小数点数</li>
   </ul>
  </li>
 </ul>
 </dd>
 <dt><code>normalized</code></dt>
 <dd>整数データを浮動小数点数へ型変換するとき、厳密な範囲へ正規化するかどうか指定する {{domxref("GLboolean")}}<ul>
  <li><code>gl.BYTE</code> と <code>gl.SHORT</code> の型では、true の場合に [-1, 1] の値へ正規化されます。</li>
  <li><code>gl.UNSIGNED_BYTE</code> と <code>gl.UNSIGNED_SHORT</code> の型では、true の場合に [0, 1] の値へ正規化されます。</li>
  <li><code>gl.FLOAT</code> と <code>gl.HALF_FLOAT</code> の型では、この引数に効果はありません。</li>
 </ul>
 </dd>
 <dt><code>stride</code></dt>
 <dd>連続する頂点属性の始端どうしの間にある、オフセット数をバイト単位で指定する {{domxref("GLsizei")}}。255 より大きくできません。stride が 0 の場合、属性はきっちり詰められていると想定されます。すなわち、アトリビュートはインターリーブされておらず、各属性は別れたブロックにあり、現在の頂点属性のすぐ隣に次の頂点属性が続きます。</dd>
 <dt><code>offset</code></dt>
 <dd>頂点属性配列の最初の要素のオフセットをバイト単位で指定する {{domxref("GLintptr")}}。 <code>type</code> のバイト長の倍数でなければいけません。</dd>
</dl>

<h3 id="返り値">返り値</h3>

<p>ありません。</p>

<h3 id="例外">例外</h3>

<ul>
 <li><code>offset</code> が負数の場合、<code>gl.INVALID_VALUE</code> エラーがスローされます。</li>
 <li><code>stride</code> と <code>offset</code> がデータ型のサイズの倍数でない場合、<code>gl.INVALID_OPERATION</code> エラーがスローされます。</li>
 <li>WebGLBuffer が ARRAY_BUFFER ターゲットに結合されていない場合、<code>gl.INVALID_OPERATION</code> エラーがスローされます。</li>
 <li>{{domxref("WebGL2RenderingContext", "WebGL 2 context", "", 1)}} を使用している場合、この頂点属性が頂点シェーダー内で整数として (例えば <code>vec4</code> の代わりに <code>uvec4</code> や <code>ivec4</code> で) 定義されていた場合、<code>gl.INVALID_OPERATION</code> エラーがスローされます。</li>
</ul>

<h2 id="説明">説明</h2>

<p>3D ジオメトリーをレンダリングしたいとしましょう。そのためには頂点シェーダーに頂点を供給する必要があります。各頂点には、位置、法線ベクトル、テクスチャー座標など、 {{jsxref("ArrayBuffer")}} で定義された頂点バッファーオブジェクト (VBO) に提供されるいくつかの属性があります。まず、使用したい {{domxref("WebGLBuffer")}} を <code>gl.ARRAY_BUFFER</code> に結合する必要があります。それから、この <code>gl.vertexAttribPointer()</code> メソッドで、属性が格納されている順序、それらの中のデータ型を指定します。加えて、ストライドを含める必要があります。これは、一つの頂点でのすべての属性の総バイト長です。さらに、 {{domxref("WebGLRenderingContext/enableVertexAttribArray", "gl.enableVertexAttribArray()")}} を呼んで、WebGL にこの属性を配列バッファーの値で埋めるように知らせます。</p>

<p>大抵は、あなたの 3D ジオメトリーはすでに厳格なバイナリ形式になっているので、メモリーレイアウトを把握するためにはその特定のフォーマットの仕様を読む必要があります。しかし、自分のフォーマットを設計していたり、ジオメトリーがテキストファイル (<a href="https://en.wikipedia.org/wiki/Wavefront_.obj_file">Wavefront .obj files</a> など) にあって実行時に <code>ArrayBuffer</code> へ変換する必要がある場合、どのようにメモリ構築するかは自由に選択できます。より高いパフォーマンスのためには、属性を <a href="https://en.wikipedia.org/wiki/Interleaved_memory">インターリーブ</a> してジオメトリを正確に表す最小のデータ型を使用してください。</p>

<p>頂点属性の最大数はグラフィックカードに依存しており、<code>gl.getParameter(gl.MAX_VERTEX_ATTRIBS)</code> を呼ぶとこの値を取得できます。ハイエンドグラフィックスカードでは、最大値は 16 です。ローエンドグラフィックスカードでは、値は低くなります。</p>

<h3 id="属性の位置">属性の位置</h3>

<p>各属性には、その位置を指定する必要があります。これは配列バッファー内の場所とは無関係なので、配列バッファーへの格納方法とは異なる順序で送信できます。以下の 2 つの選択肢があります。</p>

<ul>
 <li>自分でインデックスを指定します。 この場合、{{domxref("WebGLRenderingContext.bindAttribLocation()", "gl.bindAttribLocation()")}} を呼び出して、頂点シェーダーの名前付き属性を、使用するインデックスに接続します。 これは {{domxref("WebGLRenderingContext.linkProgram()", "gl.linkProgram()")}} を呼ぶ前に完了しなければなりません。それから、同じインデックスを <code>gl.vertexAttribPointer()</code> に与えます。</li>
 <li>他方では、頂点シェーダをコンパイルするときは、グラフィックスカードによって割り当てられたインデックスを使用します。グラフィックスカードに応じて、インデックスは異なります。なので、{{domxref("WebGLRenderingContext.getAttribLocation()", "gl.getAttribLocation()")}} を呼んでインデックスを探す必要があります。それから、このインデックスを <code>gl.vertexAttribPointer()</code> に与えます。<br>
  WebGL 2 を使用している場合、バーテックスシェーダー内で自身のインデックスを指定し、グラフィックカードに使用される既定値を上書きすることができます。例えば、<code>layout(location = 3) in vec4 position;</code> は <code>"position"</code> 属性をインデックス 3 に設定します。</li>
</ul>

<h3 id="整数属性">整数属性</h3>

<p><code>ArrayBuffer</code> は整数と浮動小数点数の両方で埋めることができますが、属性は頂点シェーダーに送信されると常に浮動小数点数に変換されます。頂点シェーダーコードで整数を使用する必要がある場合は、頂点シェーダーで (<code>(int) floatNumber</code> のように) 浮動小数点数を整数に型変換し戻すことができます。または、WebGL 2 では{{domxref("WebGL2RenderingContext.vertexAttribIPointer()", "gl.vertexAttribIPointer()")}} を使用できます。</p>

<h3 id="既定の属性値">既定の属性値</h3>

<p>バーテックスシェーダーコードはいくつかの属性を含みますが、各属性の値を指定する必要はありません。代わりに、全ての頂点に同一の既定の値を与えることができます。<code>{{domxref("WebGLRenderingContext.disableVertexAttribArray()", "gl.disableVertexAttribArray()")}}</code> を呼んで WebGL に既定の値を使うように知らせることができます。{{domxref("WebGLRenderingContext.enableVertexAttribArray()", "gl.enableVertexAttribArray()")}} を呼ぶと配列バッファーから値を読み出します。この配列バッファーは <code>gl.vertexAttribPointer()</code> で指定します。</p>

<p>同様に、例えばバーテックスシェーダーが 4 要素属性の <code>vec4</code> を予期しているのに <code>gl.vertexAttribPointer()</code> 呼び出しで <code>size</code> を <code>2</code> にセットした場合、WebGL は最初の 2 つの要素は配列バッファーを基にセットして、3 番目と 4 番目の値は既定値から取られます。</p>

<p>既定値は <code>vec4(0.0, 0.0, 0.0, 1.0)</code> ですが、<code>{{domxref("WebGLRenderingContext.vertexAttrib()", "gl.vertexAttrib[1234]f[v]()")}}</code> を呼ぶことで異なる既定値を指定することができます。</p>

<p>例えば、頂点シェーダーが位置と色の属性を使用しているとします。ほとんどのメッシュは頂点ごとのレベルで指定された色を持っていますが、いくつかのメッシュはユニフォームによるシェーディングです。そういったメッシュでは、各頂点に同じ色を配列バッファに入れる必要はありません。一定の色を設定するのに <code>gl.vertexAttrib4fv()</code> を使用できます。</p>

<h3 id="現在の設定の問い合わせ">現在の設定の問い合わせ</h3>

<p>{{domxref("WebGLRenderingContext.getVertexAttrib()", "gl.getVertexAttrib()")}}{{domxref("WebGLRenderingContext.getVertexAttribOffset()", "gl.getVertexAttribOffset()")}} で現在の属性の引数である、データ型や属性が正規化されるかどうかなどを取得できます。これらの WebGL 関数はパフォーマンスが遅いため、JavaScript アプリケーション内に状態を保存するほうがよいでしょう。しかし、これらの関数は、アプリケーションコードに触れることなく WebGL コンテキストをデバッグするのに最適です。</p>

<h2 id="例"></h2>

<p>この例は、頂点属性をシェーダープログラムに送信する方法を示しています。各頂点の属性が 1 頂点あたり 20 バイトの長さでインターリーブされて格納されている架空のデータ構造を使用します。</p>

<ol>
 <li><strong>position:</strong> X, Y と Z 座標を格納する必要があります。高い精度のために、32 ビット浮動小数点数を使用し、これは合計で 12 バイト使用します。</li>
 <li><strong>normal vector:</strong> 法線ベクトルの X, Y と Z 要素を格納する必要がありますが、精度は重要ではないので、8 ビット符号付き整数を使用します。さらなるパフォーマンスのために、データをゼロ値要素で 4 番目まで格納し、32 ビットにアライメントします。総サイズは 4 バイトになります。更に、WebGL に値を正規化するように知らせます。なぜなら法線は常に [-1, 1] の範囲だからです。</li>
 <li><strong>texture coordinate:</strong> U と V の座標を格納する必要があります。これは 16 ビット符号無し整数で十分な精度が得られます。総サイズは 4 バイトになります。更に WebGL に値を [0, 1] に正規化するように知らせます。</li>
</ol>

<p>例として、以下のような頂点のとき、</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>以下のように配列バッファーに格納されます。</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="配列バッファーの作成">配列バッファーの作成</h3>

<p>まず、{{domxref("DataView")}} を用いた JSON データから動的に配列バッファーを作成します。<code>true</code> の用法に注意してください。WebGL は私達のデータがリトルエンディアンであることを予期しています。</p>

<pre class="brush: js">//ジオメトリーを fetch() と Response.json() で読み込む
const response = await fetch('assets/geometry.json');
const vertices = await response.json();

//配列バッファーを作成
const buffer = new ArrayBuffer(20 * vertices.length);
//配列バッファーを埋める
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>より高いパフォーマンスのために、サーバー側で事前に JSON から ArrayBuffer への変換を行うこともできます。以下のように、Node.js でそれらからバイナリファイルをロードし、それを配列バッファーとして解釈することができます。</p>

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

<h3 id="WebGL_で配列バッファーを使う">WebGL で配列バッファーを使う</h3>

<p>まず、新しい頂点バッファーオブジェクト (VBO) を作成し、配列バッファーを与えます。</p>

<pre class="brush: js">//配列バッファーを頂点バッファーオブジェクトに結合する
const vbo = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, vbo);
gl.bufferData(gl.ARRAY_BUFFER, buffer, gl.STATIC_DRAW);
</pre>

<p>それから、インデックスを自分で設定することで、配列バッファーのメモリーレイアウトを指定します。</p>

<pre class="brush: js, highlight:[3, 6, 9]">//バッファーのレイアウトについて記述します
//1. 位置、正規化しない
gl.vertexAttribPointer(0, 3, gl.FLOAT, false, 20, 0);
gl.enableVertexAttribArray(0);
//2. 法線ベクトル、[-1, 1] に正規化
gl.vertexAttribPointer(1, 4, gl.BYTE, true, 20, 12);
gl.enableVertexAttribArray(1);
//3. テクスチャ座標、[0, 1] に正規化
gl.vertexAttribPointer(2, 2, gl.UNSIGNED_SHORT, true, 20, 16);
gl.enableVertexAttribArray(2);

//頂点シェーダー内の属性を同じインデックスに設定します
gl.bindAttribLocation(shaderProgram, 0, 'position');
gl.bindAttribLocation(shaderProgram, 1, 'normal');
gl.bindAttribLocation(shaderProgram, 2, 'texUV');
//属性のインデックスが変更された場合、シェーダーを再リンクする必要があります
//これにより、以前に設定されたすべてのユニフォームがリセットされることに注意してください。
gl.linkProgram(shaderProgram);
</pre>

<p>あるいは、インデックスを自分で設定する代わりに、グラフィックスカードによって提供されたインデックスを使用することもできます。これはシェーダープログラムの再リンクを避けます。</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="仕様策定状況">仕様策定状況</h2>

<table class="standard-table">
 <tbody>
  <tr>
   <th scope="col">仕様</th>
   <th scope="col">策定状況</th>
   <th scope="col">コメント</th>
  </tr>
  <tr>
   <td>{{SpecName('WebGL', "#5.14.10", "vertexAttribPointer")}}</td>
   <td>{{Spec2('WebGL')}}</td>
   <td>初回定義。</td>
  </tr>
  <tr>
   <td>{{SpecName('OpenGL ES 2.0', "glVertexAttribPointer.xml", "glVertexAttribPointer")}}</td>
   <td>{{Spec2('OpenGL ES 2.0')}}</td>
   <td>OpenGL API のマニュアルページ。</td>
  </tr>
 </tbody>
</table>

<h2 id="ブラウザーの対応">ブラウザーの対応</h2>

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

<h2 id="関連項目">関連項目</h2>

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