WebGL の事始め

https://wgld.org/d/webgl/w014.html
まずは、移植から。いきなりオブジェクト指向で脳内変換。
なんで素直に写経するのを嫌うのだ?! > 俺
マトリクスのライブラリーも、後ほど見直すこと。

<!DOCTYPE html>
<meta charset="utf-8">
<title>WebGL</title>

<style>
</style>

<body>
  <canvas width="1024" height="512"</canvas>
  

<!-- ※頂点シェーダ -->
<script id="vshader" type="x-shader/x-vertex">
attribute vec3 position;
uniform   mat4 mvpMatrix;

void main (void){
  gl_Position = mvpMatrix * vec4(position, 1.0);
}
</script>

<!-- ※フラグメントシェーダ -->
<script id="fshader" type="x-shader/x-fragment">
void main(void){
	gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0);
}
</script>


<script>
{
  class Matrix4 {

    constructor (ary = [1,0,0,0,  0,1,0,0,  0,0,1,0,  0,0,0,1]) {
      let
        F = Math.fround,
        M = new Float32Array (16);
      
      for (let i = 0; i < 16; i++)
        M[i] = F (ary[i] || 0);

      this.M = M;
    }


    multiply (mat1) {
      let
        [a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p] = this.M,
        [A, B, C, D, E, F, G, H, I, J, K, L, M, N ,O, P] = mat1.M;

      this.M = new Float32Array ([
        A * a + B * e + C * i + D * m,
        A * b + B * f + C * j + D * n,
        A * c + B * g + C * k + D * o,
        A * d + B * h + C * l + D * p,
        E * a + F * e + G * i + H * m,
        E * b + F * f + G * j + H * n,
        E * c + F * g + G * k + H * o,
        E * d + F * h + G * l + H * p,
        I * a + J * e + K * i + L * m,
        I * b + J * f + K * j + L * n,
        I * c + J * g + K * k + L * o,
        I * d + J * h + K * l + L * p,
        M * a + N * e + O * i + P * m,
        M * b + N * f + O * j + P * n,
        M * c + N * g + O * k + P * o,
        M * d + N * h + O * l + P * p
      ]);
      
      return this;
    }


    scale ([x,y,z]) {
      let m = this.M;
      this.M = new Float32Array ([
        m[0] * x,  m[1] * x,  m[2] * x,  m[3] * x,
        m[4] * y,  m[5] * y,  m[6] * y,  m[7] * y,
        m[8] * z,  m[9] * z,  m[10]* z,  m[11]* z,
        m[12],     m[13],     m[14],     m[15]
      ]);
      
      return this;
    }


    translate ([a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p], [x, y, z]) {
      this.M = new Float32Array ([
        a, b, c, d,
        e, f, g, h,
        i, j, k, l,
        a * x + e * y + i * z + m,
        b * x + f * y + j * z + n,
        c * x + g * y + k * z + o,
        d * x + h * y + l * z + p
      ]);

      return this;
    }


    static lookAt (eye, center, up) {
      let
        [eyeX, eyeY, eyeZ] = eye,
        [upX,  upY,  upZ]  = up,
        [centerX, centerY, centerZ] = center;

      if (eyeX == centerX && eyeY == centerY && eyeZ == centerZ)
        return (new this).identity ();

      let
        sqrt = Math.sqrt,
        z0 = eyeX - centerX,
        z1 = eyeY - centerY,
        z2 = eyeZ - centerZ,
        l = 1 / sqrt (z0 * z0 + z1 * z1 + z2 * z2);

      z0 *= l; z1 *= l; z2 *= l;

      let
        x0 = upY * z2 - upZ * z1,
        x1 = upZ * z0 - upX * z2,
        x2 = upX * z1 - upY * z0;

      l = sqrt(x0 * x0 + x1 * x1 + x2 * x2);
      if (! l) {
        x0 = 0; x1 = 0; x2 = 0;
      } else {
        l = 1 / l;
        x0 *= l; x1 *= l; x2 *= l;
      }

      let
        y0 = z1 * x2 - z2 * x1,
        y1 = z2 * x0 - z0 * x2,
        y2 = z0 * x1 - z1 * x0;
      l = Math.sqrt(y0 * y0 + y1 * y1 + y2 * y2);
      if (! l) {
        y0 = 0; y1 = 0; y2 = 0;
      } else {
        l = 1 / l;
        y0 *= l; y1 *= l; y2 *= l;
      }

      return new this ([
        x0, y0, z0, 0,
        x1, y1, z1, 0,
        x2, y2, z2, 0,
        -(x0 * eyeX + x1 * eyeY + x2 * eyeZ),
        -(y0 * eyeX + y1 * eyeY + y2 * eyeZ),
        -(z0 * eyeX + z1 * eyeY + z2 * eyeZ),
        1
      ]);
    }


    static perspective (fovy, aspect, near, far) {
      let
        t = near * Math.tan (fovy * Math.PI / 360),
        r = t * aspect,
        a = r * 2, b = t * 2, c = far - near;

      return new this ([
        near * 2 / a, 0, 0, 0,
        0, near * 2 / b, 0, 0,
        0, 0, -(far + near) / c, -1,
        0, 0, -(far * near * 2) / c, 0
      ]);
    }

  }
  

  this.Matrix4 = Matrix4;
}

function matIV(){
	this.create = function(){
		return new Float32Array(16);
	};

	this.translate = function(mat, vec, dest){
		dest[0] = mat[0]; dest[1] = mat[1]; dest[2]  = mat[2];  dest[3]  = mat[3];
		dest[4] = mat[4]; dest[5] = mat[5]; dest[6]  = mat[6];  dest[7]  = mat[7];
		dest[8] = mat[8]; dest[9] = mat[9]; dest[10] = mat[10]; dest[11] = mat[11];
		dest[12] = mat[0] * vec[0] + mat[4] * vec[1] + mat[8]  * vec[2] + mat[12];
		dest[13] = mat[1] * vec[0] + mat[5] * vec[1] + mat[9]  * vec[2] + mat[13];
		dest[14] = mat[2] * vec[0] + mat[6] * vec[1] + mat[10] * vec[2] + mat[14];
		dest[15] = mat[3] * vec[0] + mat[7] * vec[1] + mat[11] * vec[2] + mat[15];
		return dest;
	};
	this.rotate = function(mat, angle, axis, dest){
		var sq = Math.sqrt(axis[0] * axis[0] + axis[1] * axis[1] + axis[2] * axis[2]);
		if(!sq){return null;}
		var a = axis[0], b = axis[1], c = axis[2];
		if(sq != 1){sq = 1 / sq; a *= sq; b *= sq; c *= sq;}
		var d = Math.sin(angle), e = Math.cos(angle), f = 1 - e,
			g = mat[0],  h = mat[1], i = mat[2],  j = mat[3],
			k = mat[4],  l = mat[5], m = mat[6],  n = mat[7],
			o = mat[8],  p = mat[9], q = mat[10], r = mat[11],
			s = a * a * f + e,
			t = b * a * f + c * d,
			u = c * a * f - b * d,
			v = a * b * f - c * d,
			w = b * b * f + e,
			x = c * b * f + a * d,
			y = a * c * f + b * d,
			z = b * c * f - a * d,
			A = c * c * f + e;
		if(angle){
			if(mat != dest){
				dest[12] = mat[12]; dest[13] = mat[13];
				dest[14] = mat[14]; dest[15] = mat[15];
			}
		} else {
			dest = mat;
		}
		dest[0] = g * s + k * t + o * u;
		dest[1] = h * s + l * t + p * u;
		dest[2] = i * s + m * t + q * u;
		dest[3] = j * s + n * t + r * u;
		dest[4] = g * v + k * w + o * x;
		dest[5] = h * v + l * w + p * x;
		dest[6] = i * v + m * w + q * x;
		dest[7] = j * v + n * w + r * x;
		dest[8] = g * y + k * z + o * A;
		dest[9] = h * y + l * z + p * A;
		dest[10] = i * y + m * z + q * A;
		dest[11] = j * y + n * z + r * A;
		return dest;
	};
	this.perspective = function(fovy, aspect, near, far, dest){
		var t = near * Math.tan(fovy * Math.PI / 360);
		var r = t * aspect;
		var a = r * 2, b = t * 2, c = far - near;
		dest[0] = near * 2 / a;
		dest[1] = 0;
		dest[2] = 0;
		dest[3] = 0;
		dest[4] = 0;
		dest[5] = near * 2 / b;
		dest[6] = 0;
		dest[7] = 0;
		dest[8] = 0;
		dest[9] = 0;
		dest[10] = -(far + near) / c;
		dest[11] = -1;
		dest[12] = 0;
		dest[13] = 0;
		dest[14] = -(far * near * 2) / c;
		dest[15] = 0;
		return dest;
	};
	this.transpose = function(mat, dest){
		dest[0]  = mat[0];  dest[1]  = mat[4];
		dest[2]  = mat[8];  dest[3]  = mat[12];
		dest[4]  = mat[1];  dest[5]  = mat[5];
		dest[6]  = mat[9];  dest[7]  = mat[13];
		dest[8]  = mat[2];  dest[9]  = mat[6];
		dest[10] = mat[10]; dest[11] = mat[14];
		dest[12] = mat[3];  dest[13] = mat[7];
		dest[14] = mat[11]; dest[15] = mat[15];
		return dest;
	};
	this.inverse = function(mat, dest){
		var a = mat[0],  b = mat[1],  c = mat[2],  d = mat[3],
			e = mat[4],  f = mat[5],  g = mat[6],  h = mat[7],
			i = mat[8],  j = mat[9],  k = mat[10], l = mat[11],
			m = mat[12], n = mat[13], o = mat[14], p = mat[15],
			q = a * f - b * e, r = a * g - c * e,
			s = a * h - d * e, t = b * g - c * f,
			u = b * h - d * f, v = c * h - d * g,
			w = i * n - j * m, x = i * o - k * m,
			y = i * p - l * m, z = j * o - k * n,
			A = j * p - l * n, B = k * p - l * o,
			ivd = 1 / (q * B - r * A + s * z + t * y - u * x + v * w);
		dest[0]  = ( f * B - g * A + h * z) * ivd;
		dest[1]  = (-b * B + c * A - d * z) * ivd;
		dest[2]  = ( n * v - o * u + p * t) * ivd;
		dest[3]  = (-j * v + k * u - l * t) * ivd;
		dest[4]  = (-e * B + g * y - h * x) * ivd;
		dest[5]  = ( a * B - c * y + d * x) * ivd;
		dest[6]  = (-m * v + o * s - p * r) * ivd;
		dest[7]  = ( i * v - k * s + l * r) * ivd;
		dest[8]  = ( e * A - f * y + h * w) * ivd;
		dest[9]  = (-a * A + b * y - d * w) * ivd;
		dest[10] = ( m * u - n * s + p * q) * ivd;
		dest[11] = (-i * u + j * s - l * q) * ivd;
		dest[12] = (-e * z + f * x - g * w) * ivd;
		dest[13] = ( a * z - b * x + c * w) * ivd;
		dest[14] = (-m * t + n * r - o * q) * ivd;
		dest[15] = ( i * t - j * r + k * q) * ivd;
		return dest;
	};
}


{
  class Shader {
    constructor (canvas, vertex, fragment) {
      this.canvas = canvas;
      this.gl = canvas.getContext('webgl');
      this.v_shader = create_shader.call (this, vertex);
      this.f_shader = create_shader.call (this, fragment);
      this.prg = create_program.call (this);
      this.vbo = null;

      
      this.clear ();
    }
    
    
    clear (r = 0, g = 0, b = 0, a = 1) {
      let gl = this.gl;
      gl.clearColor (r, g, b, a);
      gl.clearDepth (1.0);
      gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);

      return this;
    }
    
    
    setVBO (vertex_position = []) {
      let
        gl = this.gl,
        attLocation = gl.getAttribLocation (this.prg, 'position'),
        attStride = 3,
        vbo = create_vbo.call (this, vertex_position);
     
      gl.bindBuffer (gl.ARRAY_BUFFER, vbo);
      gl.enableVertexAttribArray (attLocation);
      gl.vertexAttribPointer (attLocation, attStride, gl.FLOAT, false, 0, 0);
      this.vbo = vbo;

      return this;
    }
    
    
    draw (mvpMatrix) {
      let
        {gl, prg} = this,
        uniLocation = gl.getUniformLocation (prg, 'mvpMatrix');
      gl.uniformMatrix4fv (uniLocation, false, mvpMatrix);
      gl.drawArrays (gl.TRIANGLES, 0, 3);
      gl.flush ();
      
      return this;
    }

  }
  

  const
    SHADER_TYPE = {
      'x-shader/x-vertex': 'VERTEX_SHADER',
      'x-shader/x-fragment': 'FRAGMENT_SHADER'},

    create_shader =
      function (target = null) {
        if (target) {
          let
            gl = this.gl,
            {text, type} = target,
            shader = gl.createShader (gl[SHADER_TYPE[type]]);

          gl.shaderSource (shader, text);
          gl.compileShader (shader);
          if (! gl.getShaderParameter (shader, gl.COMPILE_STATUS))
            throw new Error (['シェーダーのコンパイルエラーがありました',
              gl.getShaderInfoLog (shader)].join ('\n'))

          return shader;
        }
        throw new Error ('要素がありません');
      },


      // プログラムオブジェクトを生成しシェーダをリンクする関数
    create_program =
      function () {
        let
          gl = this.gl,
          program = gl.createProgram();

        gl.attachShader (program, this.v_shader);
        gl.attachShader (program, this.f_shader);
        gl.linkProgram (program);

        if (! gl.getProgramParameter(program, gl.LINK_STATUS))
          throw new Error (['プログラムのオブジェクトの生成に失敗しました',
            gl.getProgramInfoLog (program)].join ('\n'));

        gl.useProgram (program);
        return program;
      },


	  create_vbo =
	    function (data) {
	      let
	        gl = this.gl,
	        vbo = gl.createBuffer ();

        gl.bindBuffer (gl.ARRAY_BUFFER, vbo);
        gl.bufferData (gl.ARRAY_BUFFER, new Float32Array (data), gl.STATIC_DRAW);
        gl.bindBuffer (gl.ARRAY_BUFFER, null);

        return vbo;
      };
      

  //_______
  
  this.Shader = Shader;
}


{//実行スクリプト
	let
	  doc = document,
	  canvas = doc.querySelector ('canvas'),
	  shader = new Shader (
	    canvas,
	    doc.querySelector ('script[type="x-shader/x-vertex"]'),
	    doc.querySelector ('script[type="x-shader/x-fragment"]'),
	  ),
    // モデル(頂点)データ
    vertex_position = [
      0.0, 1.0, 0.0,
      1.0, 0.0, 0.0,
      -1.0, 0.0, 0.0
    ],

    // 各種行列の生成と初期化
    matrix = Matrix4.perspective (90, canvas.width / canvas.height, 0.1, 100)
             .multiply (Matrix4.lookAt ([0.0, 1.0, 3.0], [0, 0, 0], [0, 1, 0]))
             .multiply (new Matrix4);

  shader.setVBO (vertex_position);
	shader.draw (matrix.M);

}



	
	

</script>