WebGL自学课程(4):WebGL矩阵、Camera基础操作

25 篇文章 8 订阅
25 篇文章 3 订阅

直接使用WebGL进行开发比较困难,所以用WebGL进行三维开发一般都要使用框架,本人也在学习WebGL,所以想构建一个自己习惯的框架,正好加深自己对WebGL技术的理解。WebGL框架中最重要的部分应该是矩阵变换以及Camera操作,所以本人首先构建矩阵和摄像机方面的基础代码。本人的代码还比较基础,谈不上框架,只是为了是自己加深理解而已。代码如下:

isZero = function(value){
	if(Math.abs(value) < 0.000001){
		return true;
	}
	else{
		return false;
	}
};
//
Vertice = function(x,y,z){
	this.x = x||0;
	this.y = y||0;
	this.z = z||0;
};

//
Vector = function(x,y,z){
	this.x = x||0;
	this.y = y||0;
	this.z = z||0;
};

Vector.prototype = {
	constructor: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(!isZero(length)){
			this.x /= length;
			this.y /= length;
			this.z /= length;
		}
		else{
			this.x = 0;
			this.y = 0;
			this.z = 0;
		}
		
		return this;
	},
	
	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 Vector(x,y,z);
	}
};
//
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
	);
};

Matrix.prototype = {
	constructor: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 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 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 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 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 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 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 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(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 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 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 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 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);
	}
};


Object3D = function(){
	this.matrix = new Matrix();
}
Object3D.prototype = {
	constructor:Object3D,
	
	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);
	}
};


PerspectiveCamera = function(fov,aspect,near,far){
	this.fov = fov;
	this.aspect = aspect;
	this.near = near;
	this.far = far;
	this.matrix = new Matrix();//相当于Camera的一般的模型矩阵
	this.projMatrix = new Matrix();
	this.getPerspectiveMatrix(fov||90,aspect||1,near||0.1,far||100);
}
PerspectiveCamera.prototype = new Object3D();
PerspectiveCamera.prototype.constructor = PerspectiveCamera;

PerspectiveCamera.prototype.getPerspectiveMatrix = function(fov,aspect,near,far){
	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);
};

PerspectiveCamera.prototype.getViewMatrix = function(){	
	var columnTrans = this.matrix.getColumnTrans();
	var transX = columnTrans.x;
	var transY = columnTrans.y;
	var transZ = columnTrans.z;
	
    var mat1 = new Matrix();
	mat1.setMatrixByOther(this.matrix);
	
	mat1.transpose();//因为视点矩阵与模型矩阵相反,所以要对各个方向进行转置操作,从而实现设置模型矩阵的XYZ方向。通过Camera的一般的模型矩阵对XYZ方向进行转置,得到视点矩阵的XYZ方向
	mat1.setColumnTrans(0,0,0);
	mat1.setLastRowDefault();
	
	var mat2 = new Matrix();
	mat2.setColumnTrans(-transX,-transY,-transZ);
	
	var viewMatrix = mat1.multiply(mat2);
	viewMatrix.checkZero();
	return viewMatrix;
};

PerspectiveCamera.prototype.look = function(/*vertice*/ cameraPnt,/*vertice*/ targetPnt,/*vector*/ upDirection){
	var transX = cameraPnt.x;
	var transY = cameraPnt.y;
	var transZ = cameraPnt.z;
	var up = upDirection||new Vector(0,1,0);
	var zAxis = new 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();	
};


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值