1. 程式人生 > >WebGL自學課程(5):使用一張貼圖紋理繪製地球

WebGL自學課程(5):使用一張貼圖紋理繪製地球

注:轉載請註明出處

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


注:轉載請註明出處

相關推薦

WebGL自學課程(5):使用張貼紋理繪製地球

注:轉載請註明出處 在《WebGL自學課程(3):原生WebGL+ArcGIS JS API繪製旋轉地球》一文中講述瞭如何利用地圖資料繪製地球的輪廓,但是缺少色彩。本文就是想通過貼圖的方式讓地球穿上一層靚麗的外衣,並可以通過滑鼠拖拽等對繪製的地球進行互動式操作。由於本人《

WebGL自學課程(10):通過OpenStreetMap獲取資料繪製地球

好久沒寫部落格了,今天再寫一篇。前幾天想通過OpenStreetMap訪問資料來繪製一個最最最最最簡單的WebGoogleEarth的雛形,這個Demo比較簡單,只是簡單的獲取OpenStreetMap某一個切片層級下面的所有的切片,然後按照正確的貼圖方式繪製在地球上,也就

WebGL自學課程(7):WebGL載入跨域紋理出錯Cross-origin image load denied by Cross-Origin Resource Sharing policy.

最近在學習WebGL,用圖片對WebGL進行紋理貼圖,其中圖片是從其他網站跨域獲取的,用Chrome 22執行網頁,開始的時候出現了錯誤Uncaught Error: SECURITY_ERR: D

[第五堂課]c#自學課程(5)

idv 密碼 arr bsp 課程 color 視頻 必須 百度網盤 7章 數組和集合 1.一維數組 2.二維數組 3.集合(ArrayList,哈希表) 8章 屬性與方法 1.屬性 2.方法 3.Main方法 9章 結構和類 1.結構 2.類 視頻地址:百

WebGL自學課程(8):WebGL+ArcGIS JS API實現TerrainMap

轉載請註明出處 以前在Esri的部落格上看到了一篇用Silverlight+Balder實現TerrainMap的文章,實現的功能是將指定的二維投影地理範圍轉換成三維地形圖,這是連結地址http://maps.esri.com/sldemos/terrainmap/defa

WebGL自學課程(1):原生WebGL簡單Demo

以下是一個原生WebGL簡單Demo: <!doctype html> <html> <head> <title>World</title> <meta http-equiv="Con

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

注:轉載請註明出處 通過ArcGIS JS API獲取地理資料,然後用原生WebGL將其繪製成旋轉的地球。一共需要241271個點,繪製了247個國家或地區。 截圖: 以下是程式碼: <!doctype html> <html> <h

14、《每天5分鐘玩轉Docker容器技術》學習--了解docker網絡

hostman cloudman cloud openstack docker 14、《每天5分鐘玩轉Docker容器技術》學習--一張圖了解docker網絡

自學程式設計?別傻了!讓你認清自己和科班程式設計師的差別!

自學程式設計和科班程式設計師的差別到底有多大?這也是即將“入坑”的程式設計愛好者,最關心的一個問題。自學和科班最大的差距還是在上車有沒有車票的問題,是起跑線的問題。至於上了車,那就真的是各顯神通了。 知識體系的差別 科班出身的程式設計師,相對於自學程式設計者,具備更加完善的知識體系,在實際工

Windows 8 Directx 開發學習筆記(十)地形紋理

前一篇實現木箱貼圖時,木箱的六個面都正好用一整張紋理圖,即六個面的紋理座標均在[0,1]內。然而在為比較大的模型貼圖時,像山峰河谷模型,如果只用一張紋理圖,那麼每個三角形只得到幾個紋理元素,無法為提供足夠高的解析度。這時可以在模型表面上平鋪紋理貼圖,像給牆面貼磁磚一樣,只需

自學前端開發程序員和科班出身的程序員差別在哪?告訴你

告訴 學習方法 基礎 大學生 迷茫 學生 如果 專業 免費 自學前端開發程序員和大學學計算機的人學習前端開發,差別在哪?誰會更受公司歡迎? 相對於專業就是編程的人來說,學習前端自然會更容易,因為他們具備更加完善的理論體系,所以學習編程會更加容易。 相對於自學前端開發編程的

WebGL簡易教程(十):紋理

目錄 1. 概述 2. 例項 2.1. 準備紋理 2.2. 配置紋理 2.3. 使用紋理 3. 結果 4. 參考

【內存優化】加載像資源到底占據多少內存

div blog 效果 .get round raw tails 整體 spa 0.內容概覽 1. 簡介 2. 問題 3. 概念描述 4. 具體分析 5. 總結 6. 參考文檔 1.簡介 Android中經常要通過ImageView進

詞法分析器——哈工大編譯原理課程

mina == 原理 技術分享 after 文件 編碼 exe warn 詞法分析器——哈工大編譯原理課程(一) 程序輸入:從code.txt文件中讀取內容 程序輸出:識別出的單詞序列,格式為:(種別碼,屬性值)      ①對於關鍵字

天津政府應急系統之GIS(arcgis api for flex)解說(三)顯示地圖坐標系模塊

image blur rda plain 讀取 else important baseline pat config.xml文件的配置例如以下: 1 2 <widget left="3" bottom="3" config="widg

說明CDN網絡的原理

域名 alt 進一步 net 協同 使用 來講 dns 服務 1.用戶向瀏覽器輸入www.web.com這個域名,瀏覽器第一次發現本地沒有dns緩存,則向網站的DNS服務器請求; 2.網站的DNS域名解析器設置了CNAME,指向了www.web.51cdn.co

HiHo1121 : 二分?二分判定(模板題)

性別 不同 nbsp break 不同的 時也 ttl fence 個人 描述 大家好,我是小Hi和小Ho的小夥伴Nettle,從這個星期開始由我來完成我們的Weekly。 新年回家,又到了一年一度大齡剩男剩女的相親時間。Nettle去姑姑家玩的時候看到了一張姑姑寫的相

【ABAP自學系列()】

發的 img api .cn ima code pat 查看 屏幕 一、查看補丁包級別 然後看Patch Level即可。 常用T-code: SE38(寫程序) SE80(屏幕開發) Smartform(開發smartform打印) SE37(可以查看function

自學有感5

測試 定義 容易 圖片 分析 運行 幽默 實踐 過程 通讀全書之後,我認為本書有以下特點:  第一,理論與實踐並重。本書介紹了軟件工程的相關概念,如:軟件工程、單元測試、軟件開發流程、敏捷開發、軟件需求、用戶體驗、軟件測試、質量保障等。在介紹這些基本概念的同時,作者也全面地

輕松搞懂javascript event對象的clientX,offsetX,screenX,pageX區別

png javascrip 區域 文檔 .cn 標準屬性 分享 clas src 先總結下區別: event.clientX、event.clientY 鼠標相對於瀏覽器窗口可視區域的X,Y坐標(窗口坐標),可視區域不包括工具欄和滾動條。IE事件和標準事件都定義了這2