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

注:转载请注明出处


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

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

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

运行效果截图如下:

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

本文代码.如下:

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


注:转载请注明出处

你可能感兴趣的:(function,null,buffer,callback,360,shader)