WebGL自学课程(3):原生WebGL+ArcGIS JS API绘制旋转的地球

时间:2022-07-02 03:46:16

注:转载请注明出处


通过ArcGIS JS API获取地理数据,然后用原生WebGL将其绘制成旋转的地球。一共需要241271个点,绘制了247个国家或地区。

截图:

WebGL自学课程(3):原生WebGL+ArcGIS JS API绘制旋转的地球

以下是代码:

<!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>


 

注:转载请注明出处