您的位置:首页 > 其它

WebGL自学课程(5):使用一张贴图纹理绘制地球

2012-07-14 20:21 666 查看
注:转载请注明出处

《WebGL自学课程(3):原生WebGL+ArcGIS JS API绘制旋转地球》一文中讲述了如何利用地图数据绘制地球的轮廓,但是缺少色彩。本文就是想通过贴图的方式让地球穿上一层靓丽的外衣,并可以通过鼠标拖拽等对绘制的地球进行交互式操作。由于本人《WebGL自学课程(4):WebGL矩阵、Camera基础操作》一文中构建了本人自己常用的代码,封装到World.js中,所以在以后的自学课程中都会在文档中引入World.js,以提高开发效率。

本课程所使用的二维贴图如下:



运行效果截图如下:



本文代码.如下:

<!doctype html>
<html>
<head>
<title>第一个纹理Demo</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 type="text/javascript" src="http://localhost/arcgis_js_api/library/2.7/jsapi/"></script>
<script type="text/javascript" src="World.js"></script>
<script id="shader-vs" type="x-shader/x-vertex">
attribute vec3 aPosition;
attribute vec2 aTextureCoord;
varying vec2 vTextureCoord;

uniform mat4 uModelView;
uniform mat4 uProj;

void main()
{
gl_Position = uProj * uModelView * vec4(aPosition,1.0);
vTextureCoord = aTextureCoord;
}
</script>
<script id="shader-fs" type="x-shader/x-fragment">
precision mediump float;

varying vec2 vTextureCoord;
uniform sampler2D uSampler;

void main()
{
gl_FragColor = texture2D(uSampler, vec2(vTextureCoord.s, vTextureCoord.t));
}
</script>
<script type="text/javascript">
var R = 10;
var canvas = null;
var gl = null;
var shaderProgram = null;
var aPositionLocation;
var aTextureCoordLocation;
var uModelViewLocation;
var uProjLocation;
var uSamplerLocation;

var vertexPositionBuffer;
var textureCoordBuffer;
var modelMatrix = new Matrix();
var camera = new PerspectiveCamera(90,1,1.0,200.0);
camera.look(new Vertice(0,0,1.5*R),new Vertice(0,0,0),new Vector(0,1,0));
var texture;
var bImageLoaded = false;

var bMouseDown = false;
var handleMouseMove;
var previousX=-1;
var previousY=-1;

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);

aTextureCoordLocation = gl.getAttribLocation(shaderProgram,"aTextureCoord");
gl.enableVertexAttribArray(aTextureCoordLocation);

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

function initBuffer(){
vertexPositionBuffer = gl.createBuffer();
textureCoordBuffer = gl.createBuffer();
}

function initTexture(name){
texture = gl.createTexture();
texture.image = new Image();
texture.image.onload = function () {
handleLoadedTexture(texture);
};

texture.image.src = name;
}

function handleLoadedTexture(texture) {
gl.bindTexture(gl.TEXTURE_2D, texture);
gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, texture.image);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
gl.bindTexture(gl.TEXTURE_2D, null);
bImageLoaded = true;
}

function drawEarth(column,row){
var eachLog = 180 / column;
var eachLat = 90 / row;
for(var i = 0;i < column;i++){
for(var j = 0;j < row;j++){
var log1 = eachLog * i;
var log2 = eachLog * (i+1);
var lat1 = eachLat * j;
var lat2 = eachLat * (j+1);
var p1 = getXYZ(log1,lat1,R);
var p2 = getXYZ(log2,lat1,R);
var p3 = getXYZ(log1,lat2,R);
var p4 = getXYZ(log2,lat2,R);

var vertices;
var textureCoords;

//东北半球
vertices = [p1[0],p1[1],p1[2],//左下角点
p2[0],p2[1],p2[2],//右下角点
p3[0],p3[1],p3[2],//左上角点
p4[0],p4[1],p4[2]];//右上角点
textureCoords = [0.5+log1/360,0.5+lat1/180,//左下角点
0.5+log2/360,0.5+lat1/180,//右下角点
0.5+log1/360,0.5+lat2/180,//左上角点
0.5+log2/360,0.5+lat2/180];//右上角点
drawFace(vertices,textureCoords);

//东南半球
vertices = [p3[0],-p3[1],p3[2],
p4[0],-p4[1],p4[2],
p1[0],-p1[1],p1[2],
p2[0],-p2[1],p2[2]];
textureCoords = [0.5+log1/360,0.5-lat2/180,
0.5+log2/360,0.5-lat2/180,
0.5+log1/360,0.5-lat1/180,
0.5+log2/360,0.5-lat1/180];
drawFace(vertices,textureCoords);

//西北半球
vertices = [-p2[0],p2[1],p2[2],
-p1[0],p1[1],p1[2],
-p4[0],p4[1],p4[2],
-p3[0],p3[1],p3[2]];
textureCoords = [0.5-log2/360,0.5+lat1/180,
0.5-log1/360,0.5+lat1/180,
0.5-log2/360,0.5+lat2/180,
0.5-log1/360,0.5+lat2/180];
drawFace(vertices,textureCoords);

//西南半球
vertices = [-p4[0],-p4[1],p4[2],
-p3[0],-p3[1],p3[2],
-p2[0],-p2[1],p2[2],
-p1[0],-p1[1],p1[2]];
textureCoords = [0.5-log2/360,0.5-lat2/180,
0.5-log1/360,0.5-lat2/180,
0.5-log2/360,0.5-lat1/180,
0.5-log1/360,0.5-lat1/180];
drawFace(vertices,textureCoords);
}
}
}

function drawFace(vertices,textureCoords){
gl.bindBuffer(gl.ARRAY_BUFFER,vertexPositionBuffer);
gl.bufferData(gl.ARRAY_BUFFER,new Float32Array(vertices),gl.STATIC_DRAW);
gl.vertexAttribPointer(aPositionLocation,3,gl.FLOAT,false,0,0);

gl.bindBuffer(gl.ARRAY_BUFFER, textureCoordBuffer);
gl.bufferData(gl.ARRAY_BUFFER,new Float32Array(textureCoords),gl.STATIC_DRAW);
gl.vertexAttribPointer(aTextureCoordLocation,2, gl.FLOAT, false, 0, 0);

gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_2D, texture);
gl.uniform1i(uSamplerLocation, 0);

gl.drawArrays(gl.TRIANGLE_STRIP,0,4);
}

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 drawScene(){
gl.viewport(0,0,canvas.width,canvas.height);
gl.clear(gl.COLOR_BUFFER_BIT|gl.DEPTH_BUFFER_BIT);
gl.uniformMatrix4fv(uModelViewLocation,false,camera.getViewMatrix().multiply(modelMatrix).elements);
gl.uniformMatrix4fv(uProjLocation,false,camera.projMatrix.elements);

if(bImageLoaded){
drawEarth(20,10);
}
}

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 canvasMouseDown(){
bMouseDown = true;
handleMouseMove = dojo.connect(dojo.byId("iCanvas"),"onmousemove","canvasMouseMove");
}

function canvasMouseMove(e){
var x = e.layerX||e.offsetX;
var y = e.layerY||e.offsetY;

if(previousX > 0 && previousY > 0){
var changeX = x - previousX;
var changeY = y - previousY;
var horCameraAngle = canvas.width / canvas.height * camera.fov;
var changeHorAngle = changeX / canvas.width * horCameraAngle;
var changeVerAngle = changeY / canvas.height * camera.fov;
camera.worldRotateY(-changeHorAngle*Math.PI/180);
camera.worldRotateX(-changeVerAngle*Math.PI/180);
}
previousX = x;
previousY = y;
}

function canvasMouseUp(){
bMouseDown = false;
dojo.disconnect(handleMouseMove);
previousX = -1;
previousY = -1;
}

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

gl.clearColor(0.9,0.9,0.9,1.0);
gl.enable(gl.DEPTH_TEST);
gl.depthFunc(gl.LEQUAL);
gl.enable(gl.CULL_FACE);//一定要启用裁剪,否则显示不出立体感
gl.cullFace(gl.BACK);//裁剪掉背面
initRequestAnimationFrame();
tick();
}

function init(){
dojo.connect(dojo.byId("iCanvas"),"onmousedown","canvasMouseDown");
dojo.connect(dojo.byId("iCanvas"),"onmouseup","canvasMouseUp");
startWebGL();
}
</script>
</head>
<body onload="init();">
<canvas id="iCanvas" width="600" height="600" style="margin-left:100px;margin-top:30px;border:1px solid #000;"></canvas>
</body>
</html>


注:转载请注明出处
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: