WebGL自學課程(3):原生WebGL+ArcGIS JS API繪製旋轉的地球
阿新 • • 發佈:2019-02-02
注:轉載請註明出處
通過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>
注:轉載請註明出處