1. 程式人生 > >WebGL自學課程(3):原生WebGL+ArcGIS JS API繪製旋轉的地球

WebGL自學課程(3):原生WebGL+ArcGIS JS API繪製旋轉的地球

注:轉載請註明出處

通過ArcGIS JS API獲取地理資料,然後用原生WebGL將其繪製成旋轉的地球。一共需要241271個點,繪製了247個國家或地區。

截圖:


以下是程式碼:

<!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,0,0.9,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;
			
			var countries;
			var R = 20;
			var bStop = false;
			
			var count=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(){
				count = 0;
                
                gl.viewport(0,0,canvas.width,canvas.height);
                gl.clear(gl.COLOR_BUFFER_BIT|gl.DEPTH_BUFFER_BIT);
				
				if(!countries)
					return;
				projMatrix = getPerspectiveMatrix(90,canvas.width/canvas.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,-(R*1.5));
					if(!bStop){
						angle++;						
					}
					rotateMatrix44(mvMatrix,angle*Math.PI/180,0,1,0);
					
					gl.uniformMatrix4fv(uModelViewLocation,false,mvMatrix);
                    gl.uniformMatrix4fv(uProjLocation,false,projMatrix);
					gl.bindBuffer(gl.ARRAY_BUFFER,vertexPositionBuffer);
					
					for(var i=0;i<countries.length;i++){
						var country = countries[i];
						drawCountry(country);
					}
					
					console.log(count);
							       
				mvPopMatrix();
            }
			
			function drawCountry(country){
				if(country){						
					for(var j=0;j<country.geometry.rings.length;j++){
						var ring = country.geometry.rings[j];
						var vertices = [];
						if(ring.vertices){
							vertices = ring.vertices;
						}
						else{
							if(ring.length > 3){
								for(var k=0;k<ring.length;k++){
									var coord = ring[k];
									var vertice = getXYZ(coord[0],coord[1],R);
									vertices.push(vertice[0]);
									vertices.push(vertice[1]);
									vertices.push(vertice[2]);
								}
								ring.vertices = vertices;
								count += vertices.length/3;
							}
						}
						if(vertices.length>9)
							drawPrimitive(vertices);
					}
				}
			}
			
			function drawPrimitive(vertices){
				//gl.bindBuffer(gl.ARRAY_BUFFER,vertexPositionBuffer);
				gl.bufferData(gl.ARRAY_BUFFER,new Float32Array(vertices),gl.STATIC_DRAW);
				//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.LINE_LOOP,0,vertices.length/3);
			}
			
			function getXYZ(longitude,latitude,r){
				var vertice = [];
				var radianLog = Math.PI/180*longitude;
				var radianLat = Math.PI/180*latitude;
				var sin1 = Math.sin(radianLog);
				var cos1 = Math.cos(radianLog);
				var sin2 = Math.sin(radianLat);
				var cos2 = Math.cos(radianLat);
				var x = r*sin1*cos2;
				var y = r*sin2;
				var z = r*cos1*cos2;
				vertice.push(x);
				vertice.push(y);
				vertice.push(z);
				return vertice;
			}
			
			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.9,0.9,0.9,1.0);
                gl.enable(gl.DEPTH_TEST);
				//gl.clearDepth(1.0);
                gl.depthFunc(gl.LEQUAL);
				//gl.lineWidth(2);//設定了lineWidth,沒有效果
				initRequestAnimationFrame();
                tick();
            }
			
			function doQuery(){
				var query = new esri.tasks.Query();
				query.where = "1=1";
				query.outSpatialReference = new esri.SpatialReference({wkid:4326}); 
				query.returnGeometry = true;
				var queryTask = new esri.tasks.QueryTask("http://localhost/ArcGIS/rest/services/World/MapServer/9");
				queryTask.execute(query,function(featureSet){					
					countries = featureSet.features;
					console.log(countries);
				});
			}
			
			function stop(){
				bStop = !bStop;
			}
			
			function init(){
				var btnQuery = dojo.byId("btnQuery");
				dojo.connect(btnQuery,"onclick","doQuery");
				var btnStop = dojo.byId("btnStop");
				dojo.connect(btnStop,"onclick","stop");
				
				startWebGL();
			}
			
			dojo.addOnLoad(init);
        </script>
	</head>
	<body onload="startWebGL();">
        <canvas id="iCanvas" width="600" height="600" style="margin-left:100px;margin-top:30px;border:1px solid #000;"></canvas>
		<input type="button" id="btnQuery" value="繪製"></input>
		<input type="button" id="btnStop" value="旋轉/停止旋轉"></input>
	</body>
</html>



注:轉載請註明出處