リファクタリングする

VertexAttributeSetのリファクタリング

Primitiveクラスがコンストラクタで受け付けるVertexAttributeSetですが、number[] | Float32Arrayの両方を受け入れる形になっていて少々緩さが否めませんね。実装内部で条件分岐が増えてしまって良くないので、Float32Arrayに限定しましょう。

image.png

冗長だったgl.bufferData周りがシンプルになりました。

image.png

頂点属性がない場合の仮データ作成をやめる

従来は、存在しない頂点属性については、デフォルト値を頂点数分詰めた頂点属性データを作成していました。これは実行コストが高くつきます。

private _setupVertexBuffer(_array: number[] | Float32Array, defaultArray: number[]) { let array = _array; if (array == null) { array = []; for (let i=0; i<this._vertexNumber; i++) { array = array.concat(defaultArray); } }

代わりに、gl.VertexAttrib*を使いましょう。これは、シェーダー内で利用されている頂点属性のデータが、実際には提供されいないような状況で、ダミーとしての定数値を頂点属性に設定できるWebGL関数です。以前の回では「WebGL呼び出しが複雑になりそうなので使わない」と書きましたが、実際やってみたらそれほどでもなかったので、やっぱり使うことにしました。

private _setVertexAttrib(vertexBuffer: WebGLBuffer | undefined, attributeSlot: number, componentNumber: number, defaultValue: number[]) { if (vertexBuffer != null) { const gl = Context.gl; gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer); gl.enableVertexAttribArray(attributeSlot); gl.vertexAttribPointer( attributeSlot, componentNumber, gl.FLOAT, false, 0, 0); } else { const gl = Context.gl; gl.disableVertexAttribArray(attributeSlot); //追記 if (defaultValue.length === 3) { //追記 gl.vertexAttrib3fv(attributeSlot, defaultValue); //追記 } else if (defaultValue.length === 4) { //追記 gl.vertexAttrib4fv(attributeSlot, defaultValue); //追記 } } }

呼び出し型はこのような形で、存在しない場合のダミーの定数値を最後に指定しています。

draw(entity: Entity) { const gl = Context.gl; this._setVertexAttrib(this._positionBuffer, this.material.program!._attributePosition, Primitive._positionComponentNumber, [0, 0, 0]); //変更 this._setVertexAttrib(this._colorBuffer, this.material.program!._attributeColor, Primitive._colorComponentNumber, [1, 1, 1, 1]); //変更 this._material.useProgram(gl); this._material.setUniformValues(gl);

Contextクラスをstaticにする

いろいろ悩んだのですが、ContextクラスをStaticな利用形式にしました。

image.png

これまではglコンテキストを得たいがために、contextオブジェクトをPrimitiveクラスやMaterialクラスのコンストラクタに渡すなど、煩雑なことになっていましたが、Staticにしたことで、どこからでも次のようにglコンテキストを参照できるようになりました。

const gl = Contest.gl;

この方針は、コードがシンプルになる反面、ライブラリとしてglコンテキストを実質一つしか扱えない設計になることを意味しています。でもSpinelはシンプルさを重視したライブラリなので、それでよしとしましょう。

また、Canvasの横幅、縦幅、アスペクト比を取得するメンバ関数も追加しています。

Cameraコンポーネントにaspect比が与えられない場合は画面のアスペクト比を利用するようにした。

glTFのPerspective Cameraの仕様 を見ていると、カメラのアスペクト比のプロパティは必須項目ではありません。

image.png

与えられない場合は、ビューポート(Canvas画面)のアスペクト比を利用すること、と書いてありますね。

image.png

そのための対応をしましょう。

image.png

ContextクラスをStatic利用にしたことで、簡単に画面のアスペクト比が取得でき、コード実装が楽にできましたね。

ここまでの作業はリポジトリのこちら から参照できます。

19日目:Cameraコンポーネントを作る

21日目:Orbitカメラコントロールを追加する