1. 对相机的理解
1.1. 点是怎么转到屏幕上
假设一个A点在世界坐标系内的坐标是A(x, y, z), 视口矩阵,投影矩阵,模型视图矩阵为M, N, P,则 M* N * P * A = A1, A1就是点A在屏幕上的坐标。有此可见,在这个过程中只有三个矩阵M,N,P,而没有相机的概念,所谓相机的概念其实是对矩阵M, N, P的一种解释方式,只不过这种解释方式比较形象,容易理解,容易使用。但是除了用相机去解释这三个矩阵,其实还有其它很多解释方式,就像一种颜色在正常人的眼里和色盲的眼里是不一样的。它是什么不重要,也无意义,重要的是你怎么观察它,谁观察它的,怎么解释它。
1.2. 使用相机去解释M,N,P
矩阵P是模型视图矩阵,P又可以拆分出两个矩阵,一个是模型矩阵Model,一个是视图矩阵View, 模型矩阵只作用某一个模型上,而View矩阵是作用到所有的模型上,View又可以拆分出平移矩阵T,和旋转矩阵R,(一般来说不能拆分出缩放矩阵)因此R矩阵就可以理解为视点坐标系,R矩阵的三个列向量就是视点坐标系的X轴,Y轴,Z轴,T矩阵是一个向量,也是视点坐标系的原点,也可以理解为相机的位置,由于对P矩阵有不同的拆分方法,每种拆分方法可以拆分出不同的T和R,所以相机的位置和放置的方向可以有很多种方法。 如下图:
投影矩阵N本来的作用就是把模型变换到一个边长为2的立方体内,方便剪裁,可以把投影矩阵解释为八个顶点形成的棱柱体,这个棱柱可以是正棱柱或者斜棱柱,这个棱柱的八个顶点为参数可以合成一个矩阵,这个矩阵就是N,这个棱柱体的八个顶点的坐标乘以N后,就为变成边长为2的立方体的顶点。如下图:
1.3. 使用坐标变换去解释M,N,P
除了M, N解释为相机外, M* N * P * A = A1又可以理解为 M * N * T * R * Model * A = A1, (T, R, Model的含义上面已经讲过), 还可以从坐标变换的角度去理解,每乘以一个矩阵就相当于进行了一次坐标变换,Model,R, T, N, M, 就相当于进行了一系列变换后生成了新的坐标,这个左边的Z轴最终变成了0和1之间,也就是opengl中深度坐标的范围。最终按照变换后的坐标,使用绘制点的函数绘制到屏幕上。
2. 多相机的使用
既然知道了 M* N * P * A = A1, 也就是一个点变换到屏幕上的过程,并且相机就是指的N, N, 这两个矩阵,那么在什么情况下会用到多相机呢?就是绘制多个模型,但是这几个模型的M,N值不一样的时候,比如说你绘制了一个球,再绘制了一个公告板(就是一个模型的永远朝向相机),在这种情况下,就需要两个相机,一个相机绘制球,一个相机绘制公告板。或者说,你在绘制模型的同时,还想在屏幕的左下角绘制一个坐标系,这个坐标系只能旋转,不能平移缩放,只起个指示模型的方向的作用,在这种情况下,可以单独使用一个相机绘制这个坐标系,其它的模型要使用另一个相机。
3. THREE.JS使用多相机绘制坐标系
在THREE.JS中实现左下角一个坐标轴,只旋转,不平移不缩放,仅仅起指示作用。如下图:
代码如下:
//立即执行函数,绘制坐标轴
//绘制Y轴
(function (){
var sourcePos = new THREE.Vector3(0, 0, 0);
var targetPos = new THREE.Vector3(0, 100, 0);
var direction = new THREE.Vector3().sub(targetPos, sourcePos);
var arrow = new THREE.ArrowHelper(direction.clone().normalize(), sourcePos, direction.length(), 0xff0000, 15, 15);
AxisScene.add(arrow);
})();
//绘制X轴
(function (){
var sourcePos = new THREE.Vector3(0, 0, 0);
var targetPos = new THREE.Vector3(100, 0, 0);
var direction = new THREE.Vector3().sub(targetPos, sourcePos);
var arrow = new THREE.ArrowHelper(direction.clone().normalize(), sourcePos, direction.length(), 0x00ff00, 15, 15);
AxisScene.add(arrow);
})();
//绘制Z轴
(function (){
var sourcePos = new THREE.Vector3(0, 0, 0);
var targetPos = new THREE.Vector3(0, 0, 100);
var direction = new THREE.Vector3().sub(targetPos, sourcePos);
var arrow = new THREE.ArrowHelper(direction.clone().normalize(), sourcePos, direction.length(), 0x0000ff, 15, 15);
AxisScene.add(arrow);
})();
//专门为绘制坐标轴设置一个相机
AxisCamera = new THREE.Plot3DCamera(-100, 100, 100, -100, -500, 500);
renderer.setViewport(0, 0, window.innerWidth, window.innerHeight);
//scene, camera原来的场景节点和相机,绘制场景
renderer.render(scene, camera);
renderer.autoClearColor = false;
//坐标系绘制到左下角
renderer.setViewport(window.innerWidth - 100, window.innerHeight - 100, 100, 100);
//绘制坐标系
renderer.render(CADscene, CADcamera);
交流图形学的一切,每天有原创文章
qq群: 52391108
将近两百篇图形学相关的技术文章,经常更