今天想想昨天可能找錯方向了,GIS資料應該是用來顯示特殊的資訊,例如湖泊港口的位置,國家的邊界等。
但是我想要的是地球的材質效果。所以換個方向找馬上就找到了celestia這個open source的專案。
celestia 是一個OpenGL開發的天文軟體,使用者可以在這個虛擬的宇宙自由地觀看各種星體唷。
[http://www.shatters.net/celestia/](http://www.shatters.net/celestia/" style="line-height: 1.6;) 這個是主站
http://www.celestiamotherlode.net/ 這裡有各式各樣的擴充包可以下載,包含太陽~人造衛星~月球~其它星體等
我們就在第二個站抓到了地球的天然景觀大圖!
下載下來的圖有分等級,等級越高的越細緻。例如level 0 的將整個世界地圖分成兩張1024 X 1024的圖,而level 1 分成八張!
將來可以看你要有幾張圖就分別做成幾個材質,或是先把圖組合起來做成一個材質,都是可以的。
接下來就要回到我們做出來的圓球模型,然後算出材質坐標了,這意味著又要進入數學時間!
這邊的數學是使用 Mercator projection
http://en.wikipedia.org/wiki/Mercator_projection
請看這個圓柱投影,我們最終的目標就是要從左邊的P點坐標,到右邊的P' 坐標,在轉換成0~1的材質坐標喲
右邊的P'點 X就是從-180度~180度(或是0~360度)但是重點就在於P'的y坐標,詳細推導請看維基百科,
直接節錄結果:
在之前的sphere物件中加入vertexTextureCoords屬性,
然後再計算點坐標的函式中加入以下:
var firstStep = -Math.PI/2 + Math.PI*1*latPace;
var tanRes = Math.tan(firstStep/2+Math.PI/4);
var totalLength = Math.log(tanRes)*2;
for(var i=0;i<this.latitudinalNum;i++){
for(var j=0;j<this.longitudinalNum;j++){
var texX = j*longPace;
var phi=0;
if(i==0)
phi = -Math.PI/2 + Math.PI*1*latPace;
else if(i==(this.latitudinalNum-1))
phi = -Math.PI/2 + Math.PI*(i-1)*latPace;
else
phi = -Math.PI/2 + Math.PI*(i)*latPace;
tanRes = Math.tan(phi/2+Math.PI/4);
var texY = (Math.log(tanRes)+totalLength/2)/totalLength;
this.vertexTextureCoords.push(texX);
this.vertexTextureCoords.push(texY);
}
}
首先,x 的值就是j*longPace,本來就是0~1,這沒問題。
然後phi的值,因為若是正負九十度帶入公式,y的值是會發散的。
所以這兩個角度我們要取近似,分別是第二個及倒數第二個phi代入公式的值
然後因為要mapping為0~1 所以一開始要先計算全部的長度totalLength,最後要代入公式
var texY = (Math.log(tanRes)+totalLength/2)/totalLength;
此效果相當于
var texY = Math.log(tanRes)/totalLength+0.5
如此一來就材質坐標就計算好了!
接下來再主程式中
var EARTH_TEXTURE = 0;
var earthFieldTextureCoordBuffer = null;
加入地球的材質索引,跟地球的材質坐標buffer!
loadTexture中加入
textureImage[EARTH_TEXTURE] = new Image();
textureImage[EARTH_TEXTURE].onload = function() {
setupTexture(EARTH_TEXTURE);
glProgram.samplerUniform5 = gl.getUniformLocation(glProgram, "uSampler6");
gl.uniform1i(glProgram.samplerUniform5, EARTH_TEXTURE);
}
textureImage[EARTH_TEXTURE].src = "./images/earth_level_0.png";
setupBuffer 中建立地球材質坐標buffer
earthFieldTextureCoordBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER,earthFieldTextureCoordBuffer);
gl.bufferData(gl.ARRAY_BUFFER,new Float32Array(sphere.vertexTextureCoords),gl.STATIC_DRAW);
然後再fragment shader加入
uniform sampler2D uSampler6;
main(){
...
}
else if(textureSwitch==6){
gl_FragColor = texture2D(uSampler6, vec2(vTextureCoord.s, vTextureCoord.t));
}
...
}
如此一來就準備就緒,最後再drawScene中的地球繪圖部分改成
gl.bindTexture(gl.TEXTURE_2D, texture[EARTH_TEXTURE]);
glProgram.textureSwitch = gl.getUniformLocation(glProgram,"textureSwitch");
gl.uniform1i(glProgram.textureSwitch,EARTH_TEXTURE+1);
gl.bindBuffer(gl.ARRAY_BUFFER, sphereVerticeBuffer);
gl.vertexAttribPointer(vertexPositionAttribute, 3, gl.FLOAT, false, 0, 0);
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, sphereIndexBuffer);
gl.drawElements(gl.TRIANGLES, sphereIndexBuffer.number_vertex_points, gl.UNSIGNED_SHORT, 0);
這樣就完成了!
看看結果
好像不錯,但是板塊的位置怎麼怪怪的,明天再研究哪裡出了問題!