WebGL独学コース(2):カスタムglTranslateとglRotateの使用


OpenGLではglTranslateとglRotateを直接使用できますが、WebGL 1.0仕様にはこの関数は含まれていません.使用するには、自分で実装する必要があります.サンプルコードは次のとおりです.
<!doctype html>
<html>
	<head>
		<title>World</title>
        <meta http-equiv="Content-Type" content="text/html" />
        <meta name="charset" content="utf-8"/>
        <style type="text/css">
            html,body,div{margin:0;padding:0}
        </style>
        <script id="shader-vs" type="x-shader/x-vertex">
            attribute vec3 aPosition;

            uniform mat4 uModelView;
            uniform mat4 uProj;

            void main()
            {
                gl_Position = uProj * uModelView * vec4(aPosition,1.0);
            }
        </script>
        <script id="shader-fs" type="x-shader/x-fragment">
            void main()
            {
                gl_FragColor = vec4(0.5,0.5,0,1.0);
            }
        </script>
		<script type="text/javascript" src="http://localhost/arcgis_js_api/library/2.7/jsapi/"></script>
        <script type="text/javascript">
            var canvas = null;
            var gl = null;
            var shaderProgram = null;
            var aPositionLocation;
            var uModelViewLocation;
            var uProjLocation;

            var vertexPositionBuffer = null;
			var mvMatrix = null;
            var projMatrix = null;
            var mvMatrixStack = [];
			var angle=0;

            function mvPushMatrix(){
                var array = [];
                for(i=0;i<mvMatrix.length;i++){
                    array.push(mvMatrix[i]);
                }
                var matrix = new Float32Array(array);
                mvMatrixStack.push(matrix);
            }

            function mvPopMatrix(){
                if(mvMatrixStack.length==0){
                    throw "Invalid PopMatrix";
                }
                mvMatrix = mvMatrixStack.pop();
            }

            function initWebGL(canvas){
                try{
                    gl = canvas.getContext("experimental-webgl",{antialias:true});
                }
                catch(e){
                    alert(" WebGL!");
                }

                if(!gl)
                    alert(" WebGL!");
            }

            function getShader(gl,id){
                var shaderScript = document.getElementById(id);
                if(!shaderScript)
                    return null;

                var shader = null;
                if(shaderScript.type=="x-shader/x-vertex"){
                    shader = gl.createShader(gl.VERTEX_SHADER);
                }
                else if(shaderScript.type=="x-shader/x-fragment"){
                    shader = gl.createShader(gl.FRAGMENT_SHADER);
                }
                else{
                    return null;
                }

                gl.shaderSource(shader,shaderScript.text);
                gl.compileShader(shader);

                if(!gl.getShaderParameter(shader,gl.COMPILE_STATUS)){
                    alert(gl.getShaderInfoLog(shader));
                    gl.deleteShader(shader);
                    return null;
                }

                return shader;
            }

            function initShaders(){
                var vertexShader = getShader(gl,"shader-vs");
                var fragmentShader = getShader(gl,"shader-fs");

                shaderProgram = gl.createProgram();
                gl.attachShader(shaderProgram,vertexShader);
                gl.attachShader(shaderProgram,fragmentShader);
                gl.linkProgram(shaderProgram);

                if(!gl.getProgramParameter(shaderProgram,gl.LINK_STATUS)){
                    alert("Could not link program");
                    gl.deleteProgram(shaderProgram);
                    gl.deleteShader(vertexShader);
                    gl.deleteShader(fragmentShader);
                    return;
                }

                gl.useProgram(shaderProgram);

                aPositionLocation = gl.getAttribLocation(shaderProgram,"aPosition");
                gl.enableVertexAttribArray(aPositionLocation);

                uModelViewLocation = gl.getUniformLocation(shaderProgram,"uModelView");
                uProjLocation = gl.getUniformLocation(shaderProgram,"uProj");
            }

            function initBuffer(){
                vertexPositionBuffer = gl.createBuffer();
                gl.bindBuffer(gl.ARRAY_BUFFER,vertexPositionBuffer);
                var vertices = [ 0.0,  1.0,  0.0,
                                -1.0, -1.0,  0.0,
                                 1.0, -1.0,  0.0];
                gl.bufferData(gl.ARRAY_BUFFER,new Float32Array(vertices),gl.STATIC_DRAW);
            }
			
			function getPerspectiveMatrix(fov,aspect,near,far){
                var a = 1.0/Math.tan(fov / 2 * Math.PI / 180);
                var b = far/(far-near);
                var c = -near*far/(far-near);
                
                var perspectiveMatrix = new Float32Array([
                    a/aspect, 0, 0, 0,
                    0, a, 0, 0,
                    0, 0, b, c,
                    0, 0, 1, 0
                ]);

                return perspectiveMatrix;
            }

            function drawScene(){
                var width = dojo.style(canvas,"width");
                var height = dojo.style(canvas,"height");
                gl.viewport(0,0,width,height);
                gl.clear(gl.COLOR_BUFFER_BIT|gl.DEPTH_BUFFER_BIT);
                projMatrix = getPerspectiveMatrix(90,width/height,1.0,100.0);
                mvMatrix = new Float32Array([1.0,0.0,0.0,0.0,
                                             0.0,1.0,0.0,0.0,
                                             0.0,0.0,1.0,0.0,
                                             0.0,0.0,0.0,1.0]);
                mvPushMatrix();
					translate(mvMatrix,0,0,-5);
					angle++;
					rotateMatrix44(mvMatrix,angle*Math.PI/180,0,1,0);
                    gl.bindBuffer(gl.ARRAY_BUFFER,vertexPositionBuffer);
                    gl.vertexAttribPointer(aPositionLocation,3,gl.FLOAT,false,0,0);
                    gl.uniformMatrix4fv(uModelViewLocation,false,mvMatrix);
                    gl.uniformMatrix4fv(uProjLocation,false,projMatrix);
                    gl.drawArrays(gl.TRIANGLES,0,3);
                mvPopMatrix();
            }
			
			function loadIdentity(){
				mvMatrix = new Float32Array([1.0,0.0,0.0,0.0,
                                             0.0,1.0,0.0,0.0,
                                             0.0,0.0,1.0,0.0,
                                             0.0,0.0,0.0,1.0]);
			}
			
			function translate(M16,x,y,z){
				M16[12] += x;
				M16[13] += y;
				M16[14] += z;
				return M16;
			}

			function rotateMatrix44(M16, angle, x, y, z) {
				var length, s, c;
				var xx, yy, zz, xy, yz, zx, xs, ys, zs, one_c;

				s = Math.sin(angle);
				c = Math.cos(angle);

				length = Math.sqrt( x*x + y*y + z*z );

				// Rotation matrix is normalized
				x /= length;
				y /= length;
				z /= length;

				//#define M(row,col)  m[col*4+row]

				xx = x * x;
				yy = y * y;
				zz = z * z;
				xy = x * y;
				yz = y * z;
				zx = z * x;
				xs = x * s;
				ys = y * s;
				zs = z * s;
				one_c = 1.0 - c;

				M16[0] = (one_c * xx) + c;//M(0,0)
				M16[4] = (one_c * xy) - zs;//M(0,1)
				M16[8] = (one_c * zx) + ys;//M(0,2)
				//M16[12] = 0.0;//M(0,3)  X

				M16[1] = (one_c * xy) + zs;//M(1,0)
				M16[5] = (one_c * yy) + c;//M(1,1)
				M16[9] = (one_c * yz) - xs;//M(1,2)
				//M16[13] = 0.0;//M(1,3)   Y

				M16[2] = (one_c * zx) - ys;//M(2,0)
				M16[6] = (one_c * yz) + xs;//M(2,1)
				M16[10] = (one_c * zz) + c;//M(2,2)
				//M16[14] = 0.0;//M(2,3)   Z

				M16[3] = 0.0;//M(3,0)
				M16[7] = 0.0;//M(3,1)
				M16[11] = 0.0;//M(3,2)
				M16[15] = 1.0;//M(3,3)

				return M16;
			}
			
			function initRequestAnimationFrame(){
				window.requestAnimationFrame = window.requestAnimationFrame
										|| window.mozRequestAnimationFrame
										|| window.webkitRequestAnimationFrame
										|| window.msRequestAnimationFrame
										|| window.oRequestAnimationFrame
										|| function(callback) {
											setTimeout(callback, 1000 / 60);
										};
			}
			
			function tick() {				
                window.requestAnimationFrame(tick);
                drawScene();
            }

            function startWebGL(){
                canvas = document.getElementById("iCanvas");
                initWebGL(canvas);
                initShaders();
                initBuffer();

                gl.clearColor(0.0,0.0,1.0,1.0);
                gl.enable(gl.DEPTH_TEST);
				//gl.clearDepth(1.0);
                //gl.depthFunc(gl.LEQUAL);
				initRequestAnimationFrame();
                tick();
            }
        </script>
	</head>
	<body onload="startWebGL();">
		<canvas id="iCanvas" style="border: 1px solid #000;width:800px;height: 600px;"></canvas>
	</body>
</html>