WebGL(TypeScript)--- 立方体描画&回転
下記記事にて、WebGLを用いて三角形を描画しました。
WebGL(TypeScript)--- 三角形描画 - 何でもプログラミング
今回は引き続き、立方体を描画&回転を実装してみたいと思います。
上記記事をベースに実装していきますので、解説のない部分は参照をお願いいたします。
Index Buffer作成関数
今回はIndex Bufferを用いて描画を行ってみたいと思います。
ですので、作成関数を準備します。
function createIndexBuffer(gl : WebGLRenderingContext, indices : number[]) : WebGLBuffer { const buffer = gl.createBuffer()!; gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, buffer); gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(indices), gl.STATIC_DRAW); return buffer; }
Matrix4クラス
回転を実現するため、最低限の行列クラスを準備します。
回転行列を作成するrotate関数、転置を行うtranspose関数(WebGLは列優先のため)、左上の3x3行列を取り出すgetMatrix3Data関数(法線変換用)を定義しています。
class Matrix4 { constructor(readonly data: number[]) { } transposed() : Matrix4 { return new Matrix4([ this.data[0], this.data[4], this.data[8], this.data[12], this.data[1], this.data[5], this.data[9], this.data[13], this.data[2], this.data[6], this.data[10], this.data[14], this.data[3], this.data[7], this.data[11], this.data[15], ]); } getMatrix3Data() : number[] { return [ this.data[0], this.data[1], this.data[2], this.data[4], this.data[5], this.data[6], this.data[8], this.data[9], this.data[10], ]; } static rotate(degree : number, x : number, y : number, z : number) : Matrix4 { const length = Math.sqrt(x * x + y * y + z * z); x /= length; y /= length; z /= length; const radian = degree * Math.PI / 180.0; const s = Math.sin(radian); const c = Math.cos(radian); return new Matrix4([ x * x * (1 - c) + c, x * y * (1 - c) - z * s, z * x * (1 - c) + y * s, 0, x * y * (1 - c) + z * s, y * y * (1 - c) + c, y * z * (1 - c) - x * s, 0, z * x * (1 - c) - y * s, y * z * (1 - c) + x * s, z * z * (1 - c) + c, 0, 0, 0, 0, 1 ]); } }
main関数
ライティングを行うShaderの作成と、立方体のVertex Buffer&Index Bufferの作成を行い、requestAnimationFrameにて回転させつつ描画を行っています。
実行すると下記のような表示になります。(実際はアニメーションします。)
let gl : WebGLRenderingContext; function main() : void { const canvas = <HTMLCanvasElement>document.getElementById("gl"); gl = canvas.getContext("webgl")!; // Vertex Shader(頂点と法線を変換して次へ) const vertexCode = ` attribute vec4 position; attribute vec3 normal; uniform mat4 matrix; uniform mat3 normalMatrix; varying vec3 v_normal; void main() { gl_Position = matrix * position; v_normal = normalMatrix * normal; } `; // Fragment Shader(ライティングを行って描画) const fragmentCode = ` precision mediump float; varying vec3 v_normal; void main() { vec3 light = vec3(0.0, 0.0, -1.0); float g = dot(light, normalize(v_normal)); g = max(0.0, g); gl_FragColor = vec4(0.0, g, 0.0, 1.0); } `; const program = createProgram(gl, vertexCode, fragmentCode); // 立方体定義(頂点、法線) const vertices = [ 0.5, 0.5, 0.5, 0.0, 0.0, 1.0, -0.5, 0.5, 0.5, 0.0, 0.0, 1.0, 0.5, -0.5, 0.5, 0.0, 0.0, 1.0, -0.5, -0.5, 0.5, 0.0, 0.0, 1.0, 0.5, 0.5, -0.5, 0.0, 0.0, -1.0, -0.5, 0.5, -0.5, 0.0, 0.0, -1.0, 0.5, -0.5, -0.5, 0.0, 0.0, -1.0, -0.5, -0.5, -0.5, 0.0, 0.0, -1.0, 0.5, 0.5, 0.5, 1.0, 0.0, 0.0, 0.5, -0.5, 0.5, 1.0, 0.0, 0.0, 0.5, 0.5, -0.5, 1.0, 0.0, 0.0, 0.5, -0.5, -0.5, 1.0, 0.0, 0.0, -0.5, 0.5, 0.5, -1.0, 0.0, 0.0, -0.5, -0.5, 0.5, -1.0, 0.0, 0.0, -0.5, 0.5, -0.5, -1.0, 0.0, 0.0, -0.5, -0.5, -0.5, -1.0, 0.0, 0.0, 0.5, 0.5, 0.5, 0.0, 1.0, 0.0, 0.5, 0.5, -0.5, 0.0, 1.0, 0.0, -0.5, 0.5, 0.5, 0.0, 1.0, 0.0, -0.5, 0.5, -0.5, 0.0, 1.0, 0.0, 0.5, -0.5, 0.5, 0.0, -1.0, 0.0, 0.5, -0.5, -0.5, 0.0, -1.0, 0.0, -0.5, -0.5, 0.5, 0.0, -1.0, 0.0, -0.5, -0.5, -0.5, 0.0, -1.0, 0.0, ]; const vertexBuffer = createVertexBuffer(gl, vertices); const indices = [ 0, 2, 1, 1, 2, 3, 4, 5, 6, 5, 7, 6, 8, 10, 9, 9, 10, 11, 12, 13, 14, 13, 15, 14, 16, 18, 17, 17, 18, 19, 20, 21, 22, 21, 23, 22, ]; const indexBuffer = createIndexBuffer(gl, indices); // 初期設定 gl.useProgram(program); const positionAttrib = gl.getAttribLocation(program, "position"); const normalAttrib = gl.getAttribLocation(program, "normal"); const matrixUniform = gl.getUniformLocation(program, "matrix")!; const normalMatrixUniform = gl.getUniformLocation(program, "normalMatrix")!; gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer); gl.enableVertexAttribArray(positionAttrib); gl.enableVertexAttribArray(normalAttrib); gl.vertexAttribPointer(positionAttrib, 3, gl.FLOAT, false, 24, 0); gl.vertexAttribPointer(normalAttrib, 3, gl.FLOAT, false, 24, 12); gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer); gl.enable(gl.DEPTH_TEST); gl.enable(gl.CULL_FACE); // デフォルトで反時計回りが表 // 描画関数 let rotation = 0; function step(time : number) : void { rotation++; const mat = Matrix4.rotate(rotation, 1, 1, 1).transposed(); gl.uniformMatrix4fv(matrixUniform, false, mat.data); gl.uniformMatrix3fv(normalMatrixUniform, false, mat.getMatrix3Data()); gl.clearColor(0.8, 0.8, 0.8, 1); gl.clear(gl.COLOR_BUFFER_BIT); gl.drawElements(gl.TRIANGLES, indices.length, gl.UNSIGNED_SHORT, 0); requestAnimationFrame(step); } // 描画スタート requestAnimationFrame(step); } main();