WebGL自學課程(9):WebGL框架World.js(0.3.5版本)
前一段時間在學WebGL,做了一個TerrainMap的Demo,順便把一些常用的WebGL程式碼封裝成了一個框架,起了個名字叫做World.js,現在的版本是0.3.5,還很不完善,發到部落格上主要是為了方便自己查閱。
以下是頂點渲染器VertexShader.txt的程式碼:
attribute vec3 aPosition;
attribute vec2 aTextureCoord;
varying vec2 vTextureCoord;
uniform mat4 uModelView;
uniform mat4 uProj;
attribute vec3 aVertexNormal;
uniform mat4 uNormalMatrix;
uniform vec3 uAmbientColor;
uniform vec3 uLightDirection;
uniform vec3 uDirectionalColor;
uniform bool uUseAmbientLight;
uniform bool uUseParallelLlight;
varying vec3 vLightWeighting;
void main()
{
gl_Position = uProj * uModelView * vec4(aPosition,1.0);
vTextureCoord = aTextureCoord;
if(uUseAmbientLight||uUseParallelLlight)
{
if(uUseAmbientLight)
{
vLightWeighting += uAmbientColor;
}
if(uUseParallelLlight)
{
vec3 transformedNormal = (uNormalMatrix * vec4(aVertexNormal, 1.0)).xyz;
float directionalLightWeighting = max(dot(transformedNormal,-uLightDirection), 0.0);
vLightWeighting += uDirectionalColor * directionalLightWeighting;
}
}
else
{
vLightWeighting = vec3(1.0, 1.0, 1.0);
}
}
以下是片段渲染器FragmentShader.txt的程式碼:
precision mediump float;
varying vec2 vTextureCoord;
uniform sampler2D uSampler;
varying vec3 vLightWeighting;
void main()
{
vec4 textureColor = texture2D(uSampler, vec2(vTextureCoord.s, vTextureCoord.t));
gl_FragColor = vec4(textureColor.rgb * vLightWeighting, textureColor.a);
}
以下是World0.3.5.js程式碼:
/*
Author:孫群
Email:sunqun1989@126.com
Version:0.3.5
* */
window.gl = null;
////////////////////////////////////////////////////////////////////////////////////
World = {
gl : null,
canvas : null,
aPositionLocation : -1,
aTextureCoordLocation : -1,
aVertexNormalLocation : -1,
uModelViewLocation : -1,
uProjLocation : -1,
uNormalMatrixLocation : -1,
uUseAmbientLightLocation : -1,
uUseParallelLlightLocation : -1,
uAmbientColorLocation : -1,
uLightDirectionLocation : -1,
uDirectionalColorLocation : -1,
uSamplerLocation : -1,
vertexPositionBuffer : null,
vertexNormalBuffer : null,
textureCoordBuffer : null,
shaderProgram : null,
idCounter : 0,
bUseAmbientLight : false,
bUseParallelLight : false
};
World.enableAmbientLight = function(r,g,b){
gl.uniform1i(World.uUseAmbientLightLocation, true);
gl.uniform3f(World.uAmbientColorLocation,r||0.2,g||0.2,b||0.2);//預設環境光是(0.2,0.2,0.2)
World.bUseAmbientLight = true;
};
World.disableAmbientLight = function(){
gl.uniform1i(World.uUseAmbientLightLocation, false);
World.bUseAmbientLight = false;
};
World.enableParallelLlight = function(/*World.Vector*/ direction,/*World.Vertice*/ color){
gl.uniform1i(World.uUseParallelLlightLocation, true);
direction.normalize();
gl.uniform3f(World.uLightDirectionLocation,direction.x,direction.y,direction.z);
gl.uniform3f(World.uDirectionalColorLocation,color.x,color.y,color.z);
World.bUseParallelLight = true;
};
World.disableParallelLlight = function(direction,color){
gl.uniform1i(World.uUseParallelLlightLocation, false);
World.bUseParallelLight = false;
};
/////////////////////////////////////////////////////////////////////////////////////
window.requestAnimationFrame = window.requestAnimationFrame
|| window.mozRequestAnimationFrame
|| window.webkitRequestAnimationFrame
|| window.msRequestAnimationFrame
|| window.oRequestAnimationFrame
|| function(callback) {
setTimeout(callback, 1000 / 60);
};
/////////////////////////////////////////////////////////////////////////////////////
World.WebGLRenderer = function(canvas,vertexShaderText,fragmentShaderText){
window.renderer = this;//之所以在此處設定window.renderer是因為要在tick函式中使用
this.scene = null;
this.camera = null;
this.bAutoRefresh = false;
function initWebGL(canvas){
try{
var contextList = ["webgl","experimental-webgl"];
for(var i=0;i<contextList.length;i++){
window.gl = canvas.getContext(contextList[i],{antialias:true});
if(window.gl){
World.gl = window.gl;
World.canvas = canvas;
break;
}
}
}
catch(e){
}
}
function getShader(gl,shaderType,shaderText){
if(!shaderText)
return null;
var shader = null;
if(shaderType=="VERTEX_SHADER"){
shader = gl.createShader(gl.VERTEX_SHADER);
}
else if(shaderType=="FRAGMENT_SHADER"){
shader = gl.createShader(gl.FRAGMENT_SHADER);
}
else{
return null;
}
gl.shaderSource(shader,shaderText);
gl.compileShader(shader);
if(!gl.getShaderParameter(shader,gl.COMPILE_STATUS)){
alert(gl.getShaderInfoLog(shader));
gl.deleteShader(shader);
return null;
}
return shader;
}
function initShaders(vertexShaderText,fragmentShaderText){
var vertexShader = getShader(World.gl,"VERTEX_SHADER",vertexShaderText);
var fragmentShader = getShader(World.gl,"FRAGMENT_SHADER",fragmentShaderText);
World.shaderProgram = gl.createProgram();
gl.attachShader(World.shaderProgram,vertexShader);
gl.attachShader(World.shaderProgram,fragmentShader);
gl.linkProgram(World.shaderProgram);
if(!gl.getProgramParameter(World.shaderProgram,gl.LINK_STATUS)){
alert("Could not link program");
gl.deleteProgram(World.shaderProgram);
gl.deleteShader(vertexShader);
gl.deleteShader(fragmentShader);
return;
}
gl.useProgram(World.shaderProgram);
World.aPositionLocation = gl.getAttribLocation(World.shaderProgram,"aPosition");
gl.enableVertexAttribArray(World.aPositionLocation);
World.aVertexNormalLocation = gl.getAttribLocation(World.shaderProgram, "aVertexNormal");
gl.enableVertexAttribArray(World.aVertexNormalLocation);
World.aTextureCoordLocation = gl.getAttribLocation(World.shaderProgram,"aTextureCoord");
gl.enableVertexAttribArray(World.aTextureCoordLocation);
World.uModelViewLocation = gl.getUniformLocation(World.shaderProgram,"uModelView");
World.uProjLocation = gl.getUniformLocation(World.shaderProgram,"uProj");
World.uSamplerLocation = gl.getUniformLocation(World.shaderProgram,"uSampler");
World.uNormalMatrixLocation = gl.getUniformLocation(World.shaderProgram,"uNormalMatrix");
World.uUseAmbientLightLocation = gl.getUniformLocation(World.shaderProgram,"uUseAmbientLight");
World.uUseParallelLlightLocation = gl.getUniformLocation(World.shaderProgram,"uUseParallelLlight");
World.uAmbientColorLocation = gl.getUniformLocation(World.shaderProgram,"uAmbientColor");
World.uLightDirectionLocation = gl.getUniformLocation(World.shaderProgram,"uLightDirection");
World.uDirectionalColorLocation = gl.getUniformLocation(World.shaderProgram,"uDirectionalColor");
//設定預設值
var squareArray = [1,0,0,0,
0,1,0,0,
0,0,1,0,
0,0,0,1];
var squareMatrix = new Float32Array(squareArray);
gl.uniformMatrix4fv(World.uNormalMatrixLocation,false,squareMatrix);
gl.uniform1i(World.uUseAmbientLightLocation, false);
gl.uniform1i(World.uUseParallelLlightLocation, false);
gl.uniform3f(World.uAmbientColorLocation,0.2,0.2,0.2);//預設環境光是(0.2,0.2,0.2)
}
function initBuffers(){
World.vertexPositionBuffer = gl.createBuffer();
World.textureCoordBuffer = gl.createBuffer();
World.vertexNormalBuffer = gl.createBuffer();
}
initWebGL(canvas);
if(!window.gl){
alert("瀏覽器不支援WebGL!");
return;
}
initShaders(vertexShaderText,fragmentShaderText);
initBuffers();
gl.clearColor(0.9,0.9,0.9,1.0);
gl.enable(gl.DEPTH_TEST);
gl.depthFunc(gl.LEQUAL);
//gl.depthMask(true);
//gl.enable(gl.CULL_FACE);//一定要啟用裁剪,否則顯示不出立體感
//gl.frontFace(gl.CCW);
//gl.cullFace(gl.BACK);//裁剪掉背面
gl.enable(gl.TEXTURE_2D);
};
World.WebGLRenderer.prototype = {
constructor:World.WebGLRenderer,
render:function(scene,camera){
gl.viewport(0,0,World.canvas.width,World.canvas.height);
gl.clear(gl.COLOR_BUFFER_BIT|gl.DEPTH_BUFFER_BIT);
for(var i=0;i<scene.objectList.length;i++){
var obj = scene.objectList[i];
if(obj){
obj.draw(camera);
}
}
},
bindScene : function(scene){
this.scene = scene;
},
bindCamera : function(camera){
this.camera = camera;
},
tick : function(){
//注意tick方法的執行環境是window,也就是是說在tick函式內this代表的不是WebGLRenderer,而是window
var renderer = window.renderer;
if(renderer.scene && renderer.camera){
renderer.render(renderer.scene, renderer.camera);
}
if(renderer.bAutoRefresh){
window.requestAnimationFrame(renderer.tick);
}
},
setIfAutoRefresh : function(bAuto){
this.bAutoRefresh = bAuto;
if(this.bAutoRefresh){
this.tick();
}
}
};
/////////////////////////////////////////////////////////////////////////////////////
World.Scene = function(){
this.objectList = [];
};
World.Scene.prototype = {
constructor:World.Scene,
findObjById:function(objId){
for(var i = 0;i<this.objectList.length;i++){
var obj = this.objectList[i];
if(obj.id == objId){
obj.index = i;
return obj;
}
}
return null;
},
add:function(obj){
if(this.findObjById(obj.id) != null){
alert("obj已經存在於Scene中,無法將其再次加入!");
return;
}
this.objectList.push(obj);
obj.scene = this;
},
remove:function(obj){
if(obj){
var result = this.findObjById(obj.id);
if(result == null){
alert("obj不存在於Scene中,所以無法將其從中刪除!");
return;
}
obj.scene = null;
this.objectList.splice(result.index,1);
}
obj = null;
},
clear:function(){
this.objectList = [];
}
};
/////////////////////////////////////////////////////////////////////////////////////
World.isZero = function(value){
if(Math.abs(value) < 0.000001){
return true;
}
else{
return false;
}
};
//////////////////////////////////////////////////////////////////////////////////////
World.Vertice = function(x,y,z){
this.x = x||0;
this.y = y||0;
this.z = z||0;
};
//////////////////////////////////////////////////////////////////////////////////////
World.Vector = function(x,y,z){
this.x = x||0;
this.y = y||0;
this.z = z||0;
};
World.Vector.prototype = {
constructor:World.Vector,
getLength:function(){
return Math.sqrt(this.x*this.x+this.y*this.y+this.z*this.z);
},
normalize:function(){
var length = this.getLength();
if(!World.isZero(length)){
this.x /= length;
this.y /= length;
this.z /= length;
}
else{
this.x = 0;
this.y = 0;
this.z = 0;
}
return this;
},
setLength:function(length){
this.normalize();
this.x *= length;
this.y *= length;
this.z *= length;
},
cross:function(other){
var x = this.y * other.z - this.z * other.y;
var y = this.z * other.x - this.x * other.z;
var z = this.x * other.y - this.y * other.x;
return new World.Vector(x,y,z);
},
dot:function(other){
var result = this.x*other.x+this.y*other.y+this.z*other.z;
return result;
}
};
//////////////////////////////////////////////////////////////////////////////////////
World.Matrix = function(m11,m12,m13,m14,m21,m22,m23,m24,m31,m32,m33,m34,m41,m42,m43,m44){
this.elements = new Float32Array(16);
/*this.setElements(
m11||1, m12||0, m13||0, m14||0,
m21||0, m22||1, m23||0, m24||0,
m31||0, m32||0, m33||1, m34||0,
m41||0, m42||0, m43||0, m44||1
);大Bug,當m11為0時,會自動將m11||1的值計算為1,應該是0*/
this.setElements(
(m11== undefined ?1:m11),(m12== undefined ?0:m12),(m13== undefined ?0:m13),(m14== undefined ?0:m14),
(m21== undefined ?0:m21),(m22== undefined ?1:m22),(m23== undefined ?0:m23),(m24== undefined ?0:m24),
(m31== undefined ?0:m31),(m32== undefined ?0:m32),(m33== undefined ?1:m33),(m34== undefined ?0:m34),
(m41== undefined ?0:m41),(m42== undefined ?0:m42),(m43== undefined ?0:m43),(m44== undefined ?1:m44)
);
};
World.Matrix.prototype = {
constructor:World.Matrix,
setElements:function(m11,m12,m13,m14,m21,m22,m23,m24,m31,m32,m33,m34,m41,m42,m43,m44){
var values = this.elements;
values[0]=m11;values[4]=m12;values[8]=m13;values[12]=m14;
values[1]=m21;values[5]=m22;values[9]=m23;values[13]=m24;
values[2]=m31;values[6]=m32;values[10]=m33;values[14]=m34;
values[3]=m41;values[7]=m42;values[11]=m43;values[15]=m44;
},
setColumnX:function(x,y,z){
this.elements[0] = x;
this.elements[1] = y;
this.elements[2] = z;
},
getColumnX:function(){
return new World.Vertice(this.elements[0],this.elements[1],this.elements[2]);
},
setColumnY:function(x,y,z){
this.elements[4] = x;
this.elements[5] = y;
this.elements[6] = z;
},
getColumnY:function(){
return new World.Vertice(this.elements[4],this.elements[5],this.elements[6]);
},
setColumnZ:function(x,y,z){
this.elements[8] = x;
this.elements[9] = y;
this.elements[10] = z;
},
getColumnZ:function(){
return new World.Vertice(this.elements[8],this.elements[9],this.elements[10]);
},
setColumnTrans:function(x,y,z){
this.elements[12] = x;
this.elements[13] = y;
this.elements[14] = z;
},
getColumnTrans:function(){
return new World.Vertice(this.elements[12],this.elements[13],this.elements[14]);
},
setLastRowDefault:function(){
this.elements[3]=0;
this.elements[7]=0;
this.elements[11]=0;
this.elements[15]=1;
},
transpose:function(){
var result = new World.Matrix();
result.elements[0]=this.elements[0];
result.elements[4]=this.elements[1];
result.elements[8]=this.elements[2];
result.elements[12]=this.elements[3];
result.elements[1]=this.elements[4];
result.elements[5]=this.elements[5];
result.elements[9]=this.elements[6];
result.elements[13]=this.elements[7];
result.elements[2]=this.elements[8];
result.elements[6]=this.elements[9];
result.elements[10]=this.elements[10];
result.elements[14]=this.elements[11];
result.elements[3]=this.elements[12];
result.elements[7]=this.elements[13];
result.elements[11]=this.elements[14];
result.elements[15]=this.elements[15];
this.setMatrixByOther(result);
},
setMatrixByOther:function(otherMatrix){
for(var i=0;i<otherMatrix.elements.length;i++){
this.elements[i]=otherMatrix.elements[i];
}
},
setSquareMatrix:function(){
this.setElements(1,0,0,0,
0,1,0,0,
0,0,1,0,
0,0,0,1);
},
copy:function(){
var clone = new World.Matrix(this.elements[0],this.elements[4],this.elements[8],this.elements[12],
this.elements[1],this.elements[5],this.elements[9],this.elements[13],
this.elements[2],this.elements[6],this.elements[10],this.elements[14],
this.elements[3],this.elements[7],this.elements[11],this.elements[15]);
return clone;
},
multiply:function(otherMatrix){
var values1 = this.elements;
var values2 = otherMatrix.elements;
var m11 = values1[0]*values2[0]+values1[4]*values2[1]+values1[8]*values2[2]+values1[12]*values2[3];
var m12 = values1[0]*values2[4]+values1[4]*values2[5]+values1[8]*values2[6]+values1[12]*values2[7];
var m13 = values1[0]*values2[8]+values1[4]*values2[9]+values1[8]*values2[10]+values1[12]*values2[11];
var m14 = values1[0]*values2[12]+values1[4]*values2[13]+values1[8]*values2[14]+values1[12]*values2[15];
var m21 = values1[1]*values2[0]+values1[5]*values2[1]+values1[9]*values2[2]+values1[13]*values2[3];
var m22 = values1[1]*values2[4]+values1[5]*values2[5]+values1[9]*values2[6]+values1[13]*values2[7];
var m23 = values1[1]*values2[8]+values1[5]*values2[9]+values1[9]*values2[10]+values1[13]*values2[11];
var m24 = values1[1]*values2[12]+values1[5]*values2[13]+values1[9]*values2[14]+values1[13]*values2[15];
var m31 = values1[2]*values2[0]+values1[6]*values2[1]+values1[10]*values2[2]+values1[14]*values2[3];
var m32 = values1[2]*values2[4]+values1[6]*values2[5]+values1[10]*values2[6]+values1[14]*values2[7];
var m33 = values1[2]*values2[8]+values1[6]*values2[9]+values1[10]*values2[10]+values1[14]*values2[11];
var m34 = values1[2]*values2[12]+values1[6]*values2[13]+values1[10]*values2[14]+values1[14]*values2[15];
var m41 = values1[3]*values2[0]+values1[7]*values2[1]+values1[11]*values2[2]+values1[15]*values2[3];
var m42 = values1[3]*values2[4]+values1[7]*values2[5]+values1[11]*values2[6]+values1[15]*values2[7];
var m43 = values1[3]*values2[8]+values1[7]*values2[9]+values1[11]*values2[10]+values1[15]*values2[11];
var m44 = values1[3]*values2[12]+values1[7]*values2[13]+values1[11]*values2[14]+values1[15]*values2[15];
return new World.Matrix(m11,m12,m13,m14,m21,m22,m23,m24,m31,m32,m33,m34,m41,m42,m43,m44);
},
checkZero:function(){
for(var i = 0;i < this.elements.length;i++){
if(World.isZero(this.elements[i])){
this.elements[i] = 0;
}
}
},
worldTranslate:function(x,y,z){
this.elements[12] += x;
this.elements[13] += y;
this.elements[14] += z;
},
worldRotateX:function(radian){
var c = Math.cos(radian);
var s = Math.sin(radian);
var m = new World.Matrix(1,0,0,0,
0,c,-s,0,
0,s,c,0,
0,0,0,1);
var result = m.multiply(this);
result.checkZero();
this.setMatrixByOther(result);
},
worldRotateY:function(radian){
var c = Math.cos(radian);
var s = Math.sin(radian);
var m = new World.Matrix(c,0,s,0,
0,1,0,0,
-s,0,c,0,
0,0,0,1);
var result = m.multiply(this);
result.checkZero();
this.setMatrixByOther(result);
},
worldRotateZ:function(radian){
var c = Math.cos(radian);
var s = Math.sin(radian);
var m = new World.Matrix(c,-s,0,0,
s,c,0,0,
0,0,1,0,
0,0,0,1);
var result = m.multiply(this);
result.checkZero();
this.setMatrixByOther(result);
},
worldRotateByVector:function(radian,vector){
var x = vector.x;
var y = vector.y;
var z = vector.z;
var length, s, c;
var xx, yy, zz, xy, yz, zx, xs, ys, zs, one_c;
s = Math.sin(radian);
c = Math.cos(radian);
length = Math.sqrt( x*x + y*y + z*z );
// Rotation matrix is normalized
x /= length;
y /= length;
z /= length;
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;
var m11 = (one_c * xx) + c;//M(0,0)
var m12 = (one_c * xy) - zs;//M(0,1)
var m13 = (one_c * zx) + ys;//M(0,2)
var m14 = 0.0;//M(0,3) 表示平移X
var m21 = (one_c * xy) + zs;//M(1,0)
var m22 = (one_c * yy) + c;//M(1,1)
var m23 = (one_c * yz) - xs;//M(1,2)
var m24 = 0.0;//M(1,3) 表示平移Y
var m31 = (one_c * zx) - ys;//M(2,0)
var m32 = (one_c * yz) + xs;//M(2,1)
var m33 = (one_c * zz) + c;//M(2,2)
var m34 = 0.0;//M(2,3) 表示平移Z
var m41 = 0.0;//M(3,0)
var m42 = 0.0;//M(3,1)
var m43 = 0.0;//M(3,2)
var m44 = 1.0;//M(3,3)
var mat = new World.Matrix(m11,m12,m13,m14,
m21,m22,m23,m24,
m31,m32,m33,m34,
m41,m42,m43,m44);
var result = mat.multiply(this);
result.checkZero();
this.setMatrixByOther(result);
},
localRotateX:function(radian){
var transX = this.elements[12];
var transY = this.elements[13];
var transZ = this.elements[14];
this.worldTranslate(-transX,-transY,-transZ);
var columnX = this.getColumnX();
this.worldRotateByVector(radian,columnX);
this.worldTranslate(transX,transY,transZ);
},
localRotateY:function(radian){
var transX = this.elements[12];
var transY = this.elements[13];
var transZ = this.elements[14];
this.worldTranslate(-transX,-transY,-transZ);
var columnY = this.getColumnY();
this.worldRotateByVector(radian,columnY);
this.worldTranslate(transX,transY,transZ);
},
localRotateZ:function(radian){
var transX = this.elements[12];
var transY = this.elements[13];
var transZ = this.elements[14];
this.worldTranslate(-transX,-transY,-transZ);
var columnZ = this.getColumnZ();
this.worldRotateByVector(radian,columnZ);
this.worldTranslate(transX,transY,transZ);
}
};
////////////////////////////////////////////////////////////////////////////////////////////////////
World.Object3D = function(x,y,z){
this.matrix = new World.Matrix();
this.setPosition(x||0,y||0,z||0);
this.id = ++World.idCounter;
};
World.Object3D.prototype = {
constructor:World.Object3D,
scene:null,
baseDraw:function(camera){
gl.uniformMatrix4fv(World.uModelViewLocation,false,camera.getViewMatrix().multiply(this.matrix).elements);
gl.uniformMatrix4fv(World.uProjLocation,false,camera.projMatrix.elements);
var squareMatrix = new Float32Array([1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]);
gl.uniformMatrix4fv(World.uNormalMatrixLocation,false,squareMatrix);
},
getPosition:function(){
var position = this.matrix.getColumnTrans();
return position;
},
setPosition:function(x,y,z){
this.matrix.setColumnTrans(x,y,z);
},
worldTranslate:function(x,y,z){
this.matrix.worldTranslate(x,y,z);
},
worldRotateX:function(radian){
this.matrix.worldRotateX(radian);
},
worldRotateY:function(radian){
this.matrix.worldRotateY(radian);
},
worldRotateZ:function(radian){
this.matrix.worldRotateZ(radian);
},
worldRotateByVector:function(radian,vector){
this.matrix.worldRotateByVector(radian,vector);
},
localRotateX:function(radian){
this.matrix.localRotateX(radian);
},
localRotateY:function(radian){
this.matrix.localRotateY(radian);
},
localRotateZ:function(radian){
this.matrix.localRotateZ(radian);
}
};
////////////////////////////////////////////////////////////////////////////////////////////////////
World.HeightMap = function(row,column,elevations,imageUrl,heightScale){
//this.setPosition(-column/2,0,-row/2);
this.setElevationInfo(row, column,elevations);
this.texture = gl.createTexture();
this.setTextureImageUrl(imageUrl);
this.heightScale = heightScale||1;
};
World.HeightMap.prototype = new World.Object3D();
World.HeightMap.prototype.constructor = World.HeightMap;
World.HeightMap.prototype.setElevationInfo = function(row,column,elevations){
this.row = row;
this.column = column;
this.elevations = elevations;
this.elevations.getElevation = function(r,c){
var index = (r-1)*column + c-1;
return elevations[index];
};
};
World.HeightMap.prototype.setTextureImageUrl = function(imageUrl){
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);
}
this.texture.image = new Image();
var texture = this.texture;
this.texture.image.onload = function(){
handleLoadedTexture(texture);
};
this.texture.image.crossOrigin = '';//很重要,因為圖片是跨域獲得的,所以一定要加上此句程式碼
this.texture.image.src = imageUrl;
};
World.HeightMap.prototype.draw = function(camera){
this.baseDraw(camera);
/*
WebGL在每次呼叫gl.drawArray等函式開始進行繪製之前必須給頂點渲染器中所有attribute賦值,否則會出錯,Uniforms變數則沒有此要求。
* */
function drawTriangles(texture,vertices,textureCoords,normals){
gl.bindBuffer(gl.ARRAY_BUFFER,World.vertexPositionBuffer);
gl.bufferData(gl.ARRAY_BUFFER,new Float32Array(vertices),gl.STATIC_DRAW);
gl.vertexAttribPointer(World.aPositionLocation,3,gl.FLOAT,false,0,0);
gl.bindBuffer(gl.ARRAY_BUFFER,World.textureCoordBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(textureCoords),gl.STATIC_DRAW);
gl.vertexAttribPointer(World.aTextureCoordLocation,2,gl.FLOAT,false,0,0);
gl.bindBuffer(gl.ARRAY_BUFFER,World.vertexNormalBuffer);
gl.bufferData(gl.ARRAY_BUFFER,new Float32Array(normals),gl.STATIC_DRAW);
gl.vertexAttribPointer(World.aVertexNormalLocation,3,gl.FLOAT,false,0,0);
gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_2D,texture);
gl.uniform1i(World.uSamplerLocation,0);
gl.drawArrays(gl.TRIANGLES,0,vertices.length/3);
gl.bindTexture(gl.TEXTURE_2D,null);
}
//currentRow和currentColumn表示當前的一個小Grid的中心點所在的行與列
var currentColumn,centerColumn = (this.column + 1) / 2;//列對應著j
var currentRow,centerRow = (this.row + 1) / 2;//行對應著i
/*i表示行序,j表示列序,第一個for迴圈指定了要進行遍歷的列,第二個for迴圈進入列內進行遍歷元素*/
//逐行繪製除去四周邊框的內部主體
var vertices = [];
var textureCoords = [];
var normals = [];
var yAxis = this.matrix.getColumnY();
var yDirection = new World.Vector(yAxis.x,yAxis.y,yAxis.z);
yDirection.normalize();
for(var i=1;i<=this.row-1;i++){
//for(var i=this.row-1;i>=1;i--){
for(var j=1;j<=this.column-1;j++){
//計算左上角資料
currentRow = i; currentColumn = j;
var leftTopX = currentColumn - centerColumn;
var leftTopY = this.elevations.getElevation(currentRow, currentColumn)*this.heightScale;
var leftTopZ = currentRow - centerRow;
var leftTopTextureX = (currentColumn-0.5)/this.column;
var leftTopTextureY = 1-(currentRow-0.5)/this.row;
//計算左下角資料
currentRow = i + 1; currentColumn = j;
var leftBottomX = currentColumn - centerColumn;
var leftBottomY = this.elevations.getElevation(currentRow,currentColumn)*this.heightScale;
var leftBottomZ = currentRow - centerRow;
var leftBottomTextureX = (currentColumn-0.5)/this.column;
var leftBottomTextureY = 1-(currentRow-0.5)/this.row;
//計算右上角資料
currentRow = i; currentColumn = j + 1;
var rightTopX = currentColumn - centerColumn;
var rightTopY = this.elevations.getElevation(currentRow,currentColumn)*this.heightScale;
var rightTopZ = currentRow - centerRow;
var rightTopTextureX = (currentColumn-0.5)/this.column;
var rightTopTextureY = 1-(currentRow-0.5)/this.row;
//計算右下角資料
currentRow = i + 1; currentColumn = j + 1;
var rightBottomX = currentColumn - centerColumn;
var rightBottomY = this.elevations.getElevation(currentRow,currentColumn)*this.heightScale;
var rightBottomZ = currentRow - centerRow;
var rightBottomTextureX = (currentColumn-0.5)/this.column;
var rightBottomTextureY = 1-(currentRow-0.5)/this.row;
///////////////////////////////////////////////////////////////////////////////
//加入左上角點
vertices.push(leftTopX);
vertices.push(leftTopY);
vertices.push(leftTopZ);
textureCoords.push(leftTopTextureX);
textureCoords.push(leftTopTextureY);
normals.push(yDirection.x);
normals.push(yDirection.y);
normals.push(yDirection.z);
//加入左下角點
vertices.push(leftBottomX);
vertices.push(leftBottomY);
vertices.push(leftBottomZ);
textureCoords.push(leftBottomTextureX);
textureCoords.push(leftBottomTextureY);
normals.push(yDirection.x);
normals.push(yDirection.y);
normals.push(yDirection.z);
//加入右下角點
vertices.push(rightBottomX);
vertices.push(rightBottomY);
vertices.push(rightBottomZ);
textureCoords.push(rightBottomTextureX);
textureCoords.push(rightBottomTextureY);
normals.push(yDirection.x);
normals.push(yDirection.y);
normals.push(yDirection.z);
////////////////////////////////////////////////////////////////////////////////////////
//加入右下角點
vertices.push(rightBottomX);
vertices.push(rightBottomY);
vertices.push(rightBottomZ);
textureCoords.push(rightBottomTextureX);
textureCoords.push(rightBottomTextureY);
normals.push(yDirection.x);
normals.push(yDirection.y);
normals.push(yDirection.z);
//加入右上角點
vertices.push(rightTopX);
vertices.push(rightTopY);
vertices.push(rightTopZ);
textureCoords.push(rightTopTextureX);
textureCoords.push(rightTopTextureY);
normals.push(yDirection.x);
normals.push(yDirection.y);
normals.push(yDirection.z);
//加入左上角點
vertices.push(leftTopX);
vertices.push(leftTopY);
vertices.push(leftTopZ);
textureCoords.push(leftTopTextureX);
textureCoords.push(leftTopTextureY);
normals.push(yDirection.x);
normals.push(yDirection.y);
normals.push(yDirection.z);
}
}
drawTriangles(this.texture,vertices,textureCoords,normals);
};
////////////////////////////////////////////////////////////////////////////////////////////////////
World.Cube = function(length,width,height){
this.length = length;
this.width = width;
this.height = height;
};
World.Cube.prototype = new World.Object3D();
World.Cube.prototype.constructor = World.Cube;
World.Cube.prototype.draw = function(){
this.baseDraw(camera);
function drawTriangles(vertices,textureCoords,normals){
gl.bindBuffer(gl.ARRAY_BUFFER,World.vertexPositionBuffer);
gl.bufferData(gl.ARRAY_BUFFER,new Float32Array(vertices),gl.STATIC_DRAW);
gl.vertexAttribPointer(World.aPositionLocation,3,gl.FLOAT,false,0,0);
gl.bindBuffer(gl.ARRAY_BUFFER,World.textureCoordBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(textureCoords),gl.STATIC_DRAW);
gl.vertexAttribPointer(World.aTextureCoordLocation,2,gl.FLOAT,false,0,0);
gl.bindBuffer(gl.ARRAY_BUFFER,World.vertexNormalBuffer);
gl.bufferData(gl.ARRAY_BUFFER,new Float32Array(normals),gl.STATIC_DRAW);
gl.vertexAttribPointer(World.aVertexNormalLocation,3,gl.FLOAT,false,0,0);
gl.drawArrays(gl.TRIANGLES,0,vertices.length/3);
}
var halfLength = this.length/2;
var halfHeight = this.height/2;
var halfWidth = this.width/2;
var vertices = [];
//var textureCoords = [];
var normals = [];
var frontLeftTopVertice = [-halfLength,halfHeight,halfWidth];//前面左上角點
var frontLeftBottomVertice = [-halfLength,-halfHeight,halfWidth];//前面左下角點
var frontRightTopVertice = [halfLength,halfHeight,halfWidth];//前面右上角點
var frontRightBottomVertice = [halfLength,-halfHeight,halfWidth];//前面右下角點
var behindLeftTopVertice = [-halfLength,halfHeight,-halfWidth];//後面左上角點
var behindLeftBottomVertice = [-halfLength,-halfHeight,-halfWidth];//後面左下角點
var behindRightTopVertice = [halfLength,halfHeight,-halfWidth];//後面右上角點
var behindRightBottomVertice = [halfLength,-halfHeight,-halfWidth];//後面右下角點
var frontNormal = [0,0,1];
var behindNormal = [0,0,-1];
var leftNormal = [-1,0,0];
var rightNormal = [1,0,0];
var topNormal = [0,1,0];
var bottomNormal = [0,-1,0];
};
////////////////////////////////////////////////////////////////////////////////////////////////////
World.EarthOSM = function(radius,level){
this.textures = [];
this.radius = radius;
this.level = level||2;
if(this.level < 2){
this.level = 2;
}
this.getTiles(this.radius,this.level);
}
World.EarthOSM.prototype = new World.Object3D();
World.EarthOSM.prototype.constructor = World.EarthOSM;
World.EarthOSM.prototype.getTiles = function (r,level){
var n = Math.pow(2,level);
for(var column = 0;column < n;column++){
for(var row = 0;row < n;row++){
var texture = this.initTexture(level,row,column,r);
this.textures.push(texture);
}
}
}
World.EarthOSM.prototype.initTexture = function (level,row,column,R){
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);
}
var texture = gl.createTexture();
texture.level = level;
texture.row = row;
texture.column = column;
var n = Math.pow(2,level);
var eachLog = 360 / n;//每列所跨的經度
var eachLat = 90 * 2 / n;//每列所跨的緯度
texture.minLog = -180 + eachLog * column;//每個圖片的經度範圍中的最小值
texture.maxLog = texture.minLog + eachLog;//每個圖片的經度範圍中的最大值
texture.maxLat = 90 - eachLat * row;//每個圖片的緯度範圍中的最大值
texture.minLat = texture.maxLat - eachLat;//每個圖片的緯度範圍中的最小值
var pLeftBottom = this.getXYZ(texture.minLog,texture.minLat,R);
var pRightBottom = this.getXYZ(texture.maxLog,texture.minLat,R);
var pLeftTop = this.getXYZ(texture.minLog,texture.maxLat,R);
var pRightTop = this.getXYZ(texture.maxLog,texture.maxLat,R);
var vertices = [pLeftBottom[0],pLeftBottom[1],pLeftBottom[2],
pRightBottom[0],pRightBottom[1],pRightBottom[2],
pLeftTop[0],pLeftTop[1],pLeftTop[2],
pRightTop[0],pRightTop[1],pRightTop[2]];
var textureCoords = [0,0,
1,0,
0,1,
1,1];
var normals = [pLeftBottom[0],pLeftBottom[1],pLeftBottom[2],
pRightBottom[0],pRightBottom[1],pRightBottom[2],
pLeftTop[0],pLeftTop[1],pLeftTop[2],
pRightTop[0],pRightTop[1],pRightTop[2]];
texture.vertices = vertices;
texture.textureCoords = textureCoords;
texture.normals = normals;
texture.image = new Image();
texture.image.onload = function () {
handleLoadedTexture(texture);
};
texture.image.crossOrigin = '';//很重要,因為圖片是跨域獲得的,所以一定要加上此句程式碼
//"http://otile1.mqcdn.com/tiles/1.0.0/osm/"+level+"/"+column+"/"+row+".jpg";
texture.image.src = "http://a.tile.openstreetmap.org/"+level+"/"+column+"/"+row+".png";
return texture;
}
World.EarthOSM.prototype.getXYZ = function(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;
}
World.EarthOSM.prototype.draw = function(){
this.baseDraw(camera);
for(var i = 0;i < this.textures.length;i++){
var texture = this.textures[i];
if(texture.image){
gl.bindBuffer(gl.ARRAY_BUFFER,World.vertexPositionBuffer);
gl.bufferData(gl.ARRAY_BUFFER,new Float32Array(texture.vertices),gl.STATIC_DRAW);
gl.vertexAttribPointer(World.aPositionLocation,3,gl.FLOAT,false,0,0);
gl.bindBuffer(gl.ARRAY_BUFFER, World.textureCoordBuffer);
gl.bufferData(gl.ARRAY_BUFFER,new Float32Array(texture.textureCoords),gl.STATIC_DRAW);
gl.vertexAttribPointer(World.aTextureCoordLocation,2, gl.FLOAT, false, 0, 0);
gl.bindBuffer(gl.ARRAY_BUFFER,World.vertexNormalBuffer);
gl.bufferData(gl.ARRAY_BUFFER,new Float32Array(texture.normals),gl.STATIC_DRAW);
gl.vertexAttribPointer(World.aVertexNormalLocation,3,gl.FLOAT,false,0,0);
gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_2D, texture);
gl.uniform1i(World.uSamplerLocation, 0);
gl.drawArrays(gl.TRIANGLE_STRIP,0,4);
}
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////
World.PerspectiveCamera = function(fov,aspect,near,far){
this.fov = fov||90;
this.aspect = aspect||1;
this.near = near||0.1;
this.far = far||100;
this.matrix = new World.Matrix();//相當於Camera的一般的模型矩陣
this.projMatrix = new World.Matrix();
this.setPerspectiveMatrix(this.fov,this.aspect,this.near,this.far);
};
World.PerspectiveCamera.prototype = new World.Object3D();
World.PerspectiveCamera.prototype.constructor = World.PerspectiveCamera;
World.PerspectiveCamera.prototype.setPerspectiveMatrix = function(fov,aspect,near,far){
debugger;
this.fov = fov||this.fov;
this.aspect = aspect||this.aspect;
this.near = near||this.near;
this.far = far||this.far;
/*
原來錯誤的投影程式碼
var a = 1.0/Math.tan(this.fov / 2 * Math.PI / 180);
var b = this.far/(this.far-this.near);
var c = -this.near*this.far/(this.far-this.near);
this.projMatrix.setElements(a/aspect, 0, 0, 0,
0, a, 0, 0,
0, 0, b, 1,
0, 0, c, 0);*/
var mat = [1,0,0,0,
0,1,0,0,
0,0,1,0,
0,0,0,1];
var f=this.fov*Math.PI/360;
var a=this.far-this.near;
var e=Math.cos(f)/Math.sin(f);
mat[0]=e/this.aspect;
mat[5]=e;
mat[10]=-(this.far+this.near)/a;
mat[11]=-1;
mat[14]=-2*this.near*this.far/a;
mat[15]=0;
this.projMatrix.setElements(mat[0],mat[1],mat[2],mat[3],
mat[4],mat[5],mat[6],mat[7],
mat[8],mat[9],mat[10],mat[11],
mat[12],mat[13],mat[14],mat[15]);
};
World.PerspectiveCamera.prototype.getLightDirection = function(){
var dirVertice = this.matrix.getColumnZ();
var direction = new World.Vector(-dirVertice.x,-dirVertice.y, -dirVertice.z);
direction.normalize();
return direction;
};
World.PerspectiveCamera.prototype.setPosition = function(/*World.Vertice*/ position){
this.look(position,this.getTarget());
};
World.PerspectiveCamera.prototype.getTarget = function(){
var direction = this.getLightDirection();
direction.setLength(this.far);
var position = this.getPosition();
var target = new World.Vertice();
target.x = position.x + direction.x;
target.y = position.y + direction.y;
target.z = position.z + direction.z;
return target;
};
World.PerspectiveCamera.prototype.setTarget = function(/*World.Vertice*/ targetPnt,/*vector option*/ upDirection){
this.lookAt(targetPnt,upDirection);
};
World.PerspectiveCamera.prototype.getViewFrustumDistance = function(){
var distance = this.far - this.near;
return distance;
};
World.PerspectiveCamera.prototype.setFov = function(){
this.setPerspectiveMatrix(fov,this.aspect,this.near,this.far);
};
World.PerspectiveCamera.prototype.setAspect = function(aspect){
this.setPerspectiveMatrix(this.fov, aspect, this.near,this.far);
};
World.PerspectiveCamera.prototype.setNear = function(near){
this.setPerspectiveMatrix(this.fov,this.aspect,near,this.far);
};
World.PerspectiveCamera.prototype.setFar = function(far){
this.setPerspectiveMatrix(this.fov, this.aspect,this.near, far);
};
World.PerspectiveCamera.prototype.setViewFrustumDistance = function(newDistance,bCameraPntMove){
if(bCameraPntMove == true){
//設定視景體前後面的距離,target不變,通過改變視距,從而改變視點(即Camera的位置)
var preDistance = this.getViewFrustumDistance();
var changeLength = newDistance - preDistance;
var changeDirection = this.getLightDirection();
changeDirection.setLength(changeLength);
var oldPosition = this.getPosition();
var newPosition = new World.Vertice(oldPosition.x+changeDirection.x,oldPosition.y+changeDirection.y,oldPosition.z+changeDirection.z);
var target = this.getTarget();
this.look(newPosition,target);
}
else{
//預設情況
//設定視景體前後面的距離,視點(即Camera的位置)和this.near不變,通過改變視距,從而改變this.far,相當於改變了target
this.setFar(newDistance + this.near);
}
};
World.PerspectiveCamera.prototype.getViewMatrix = function(){
var columnTrans = this.matrix.getColumnTrans();
var transX = columnTrans.x;
var transY = columnTrans.y;
var transZ = columnTrans.z;
var mat1 = new World.Matrix();
mat1.setMatrixByOther(this.matrix);
mat1.transpose();//因為視點矩陣與模型矩陣相反,所以要對各個方向進行轉置操作,從而實現設定模型矩陣的XYZ方向。通過Camera的一般的模型矩陣對XYZ方向進行轉置,得到視點矩陣的XYZ方向
mat1.setColumnTrans(0,0,0);
mat1.setLastRowDefault();
var mat2 = new World.Matrix();
mat2.setColumnTrans(-transX,-transY,-transZ);
var viewMatrix = mat1.multiply(mat2);
//viewMatrix.checkZero();
return viewMatrix;
};
World.PerspectiveCamera.prototype.look = function(/*World.Vertice*/ cameraPnt,/*World.Vertice*/ targetPnt,/*vector option*/ upDirection){
var transX = cameraPnt.x;
var transY = cameraPnt.y;
var transZ = cameraPnt.z;
var up = upDirection||new World.Vector(0,1,0);
var zAxis = new World.Vector(cameraPnt.x-targetPnt.x,cameraPnt.y-targetPnt.y,cameraPnt.z-targetPnt.z).normalize();
var xAxis = up.cross(zAxis).normalize();
var yAxis = zAxis.cross(xAxis).normalize();
this.matrix.setColumnX(xAxis.x,xAxis.y,xAxis.z);//此處相當於對Camera的模型矩陣(還不是視點矩陣)設定X軸方向
this.matrix.setColumnY(yAxis.x,yAxis.y,yAxis.z);//此處相當於對Camera的模型矩陣(還不是視點矩陣)設定Y軸方向
this.matrix.setColumnZ(zAxis.x,zAxis.y,zAxis.z);//此處相當於對Camera的模型矩陣(還不是視點矩陣)設定Z軸方向
this.matrix.setColumnTrans(transX,transY,transZ);//此處相當於對Camera的模型矩陣(還不是視點矩陣)設定偏移量
this.matrix.setLastRowDefault();
var deltaX = cameraPnt.x - targetPnt.x;
var deltaY = cameraPnt.y - targetPnt.y;
var deltaZ = cameraPnt.z - targetPnt.z;
var far = Math.sqrt(deltaX*deltaX+deltaY*deltaY+deltaZ*deltaZ);
this.setFar(far);
};
World.PerspectiveCamera.prototype.lookAt = function(/*World.Vertice*/ targetPnt,/*vector option*/ upDirection){
var position = this.getPosition();
this.look(position,targetPnt,upDirection);
};
相關文章
- WebGL自學課程(14):WebGL使用Mipmap紋理Web
- WebGL自學課程(15):WebGL在WebGIS上的應用——WebGlobeWeb
- WebGL自學課程(8):WebGL+ArcGIS JS API實現TerrainMapWebJSAPIAI
- WebGL自學課程(12):光照模型與渲染方式Web模型
- WebGL自學課程(13):獲得三維拾取向量Web
- WebGL自學課程(16):WebGlobe實現的基本演算法原理Web演算法
- WebGL自學課程(10):通過OpenStreetMap獲取資料繪製地球Web
- WebGL自學課程(7):WebGL載入跨域紋理出錯Cross-origin image load denied by Cross-Origin Resource Sharing policy.Web跨域ROS
- WebGL自學課程(11):ELSL著色器程式設計中內建的運算子與函式Web程式設計函式
- webgl 系列 —— 初識 WebGLWeb
- webgl入門(1)-什麼是webglWeb
- webgl入門(2)-初識webgl和著色器Web
- webgl 系列 —— 繪製一個點(版本2、版本3、版本4、版本5)Web
- WEBGL橢圓Web
- WebGL-DemoWeb
- Webgl 紋理Web
- 認識WebGLWeb
- test 2D渲染器 WebGL WebGL2Web
- WebGL 的 Hello WorldWeb
- Web前端開發最好用的幾個WebGL框架Web前端框架
- 《WebGL程式設計指南》學習筆記——1.WebGL概述Web程式設計筆記
- [WebGL入門]二,開始WebGL之前,先了解一下canvasWebCanvas
- webgl實現故障效果Web
- webgl實現火焰效果Web
- WebGL 3d賀卡Web3D
- WebGL載入本地模型Web模型
- JavaScript WebGL 使用圖片JavaScriptWeb
- unity Webgl播放視屏UnityWeb
- WebGL之延遲著色Web
- JavaScript WebGL 基礎概念JavaScriptWeb
- WebGL沒有通道APIWebAPI
- webgl世界 matrix入門Web
- WebGL&SL資源Web
- 微軟加入WebGL工作組微軟Web
- Khronos Group的WebGL工作組主席Ken Russell為《WebGL入門指南》作序Web
- WebGL基礎(一): 從一個滑鼠畫點開始瞭解原生webGLWeb
- WebGL之物體選擇Web
- WebGL 紋理顏色原理Web