透视图形的原理
1、人眼观察观看物体的效果是进大远小,就向我们观察远处的铁轨,近的地方是平行的,远的地方观察是相交的,如下图所示
![](https://img-blog.csdnimg.cn/direct/2f886ab424fb418ea51dd41e685c1217.png)
为啥会产生这种效果呢,这就和人眼成像有所关联。眼睛中的晶状体会根据物体的距离而调节其对光的折射程度,使得光线能够聚焦在视网膜上。当观察远处的物体时,晶状体会变得较扁平,使得光线能够更集中地聚焦在视网膜上,因此远处的物体会看起来较小。而当观察近处的物体时,晶状体会变得较圆,使得光线的聚焦点稍微后移,物体的像在视网膜上放大,所以近处的物体会看起来较大。这种现象被称为视角大小的变化。
如何产生透视图形
在计算图形学中,我可以通过三种矩阵来进行模拟,这三个矩阵分别是:
1、模型矩阵
模型矩阵用于描述物体在世界坐标系中的位置、旋转和缩放变换。它将物体从局部坐标系(对象坐标系)转换到世界坐标系,为物体赋予了在世界中的位置和方向。模型矩阵通常由平移、旋转和缩放矩阵组成,可以表示为:
M=T⋅R⋅S
其中,T是平移矩阵,R是旋转矩阵,S是缩放矩阵。通过模型矩阵,我们可以将物体从其局部坐标系变换到世界坐标系中的任意位置、旋转和大小。
function getModeMatrix(scale: number, translate: number, rotate: number,) {
const scaleMatrix = new Matrix4()
scaleMatrix.set(
scale, 0, 0, 0,
0, scale, 0, 0,
0, 0, scale, 0,
0, 0, 0, 1,
)
const translateMatrix = new Matrix4()
translateMatrix.set(
1, 0, 0, -translate,
0, 1, 0, -translate,
0, 0, 1, -translate,
0, 0, 0, 1,
)
const rotateXMatrix = new Matrix4();
const rotateYMatrix = new Matrix4();
rotateXMatrix.set(
1, 0, 0, 0,
0, Math.cos(rotate), -Math.sin(rotate), 0,
0, Math.sin(rotate), Math.cos(rotate), 0,
0, 0, 0, 1
)
rotateYMatrix.set(
Math.cos(rotate), 0, Math.sin(rotate), 0,
0, 1, 0, 0,
-Math.sin(rotate), 0, Math.cos(rotate), 0,
0, 0, 0, 1
)
const m = new Matrix4()
m.multiply(scaleMatrix)
.multiply(rotateXMatrix)
.multiply(translateMatrix)
return m.transpose();
}
2、视图矩阵
视图矩阵用于描述相机的位置和方向,即相机在世界坐标系中的位置和朝向。它将世界坐标系中的物体变换到相机坐标系中,为我们提供了相机的视角。视图矩阵通常由相机的位置和朝向决定,可以表示为:
𝑉=lookAt(𝑒𝑦𝑒,𝑐𝑒𝑛𝑡𝑒𝑟,𝑢𝑝)
其中,𝑒𝑦𝑒eye是相机的位置,𝑐𝑒𝑛𝑡𝑒𝑟center是相机观察的目标点,𝑢𝑝up是相机的上向量。通过视图矩阵,我们可以将世界中的物体变换到相机的视角下,为后续的投影和渲染提供了正确的视角和位置。
function getViewMatrix(eye: Vector3) {
const center = new Vector3(0, 0, 0)
const up = new Vector3(0, 1, 0);
const zAxis = eye.clone().sub(center).normalize();
const xAxis = up.clone().cross(zAxis).normalize();
const yAxis = zAxis.clone().cross(xAxis).normalize()
let viewMatrix = new Matrix4()
viewMatrix.set(
xAxis.x, xAxis.y, xAxis.z, -xAxis.dot(eye),
yAxis.x, yAxis.y, yAxis.z, -yAxis.dot(eye),
zAxis.x, zAxis.y, zAxis.z, -zAxis.dot(eye),
0, 0, 0, 1
);
return viewMatrix
}
3、透视矩阵
透视矩阵用于描述透视投影,将三维场景投影到二维屏幕上。它定义了视景体(View Frustum),确定了相机的视野范围和透视效果。透视矩阵通常由近裁剪面、远裁剪面、视角和屏幕宽高比决定,可以表示为:
𝑃=perspective(𝑓𝑜𝑣,𝑎𝑠𝑝𝑒𝑐𝑡,𝑛𝑒𝑎𝑟,𝑓𝑎𝑟)
其中,𝑓𝑜𝑣是视角大小,𝑎𝑠𝑝𝑒𝑐𝑡是屏幕宽高比,𝑛𝑒𝑎𝑟和𝑓𝑎𝑟分别是近裁剪面和远裁剪面的距离。通过透视矩阵,我们可以将场景中的物体投影到屏幕上,并呈现出透视效果,使远处的物体看起来比近处的物体小。
function getProjectMatrix(fov: number, aspect: number, near: number, far: number) {
const angle = (fov * Math.PI) / 180;
const t = near * Math.tan(angle / 2);
const r = t * aspect;
const l = -r;
const b = -t;
const translateMatrix = new Matrix4()
const scaleMatrix = new Matrix4()
const projectionMatrix = new Matrix4();
const m = new Matrix4()
translateMatrix.set(
1, 0, 0, -(l + r) / 2,
0, 1, 0, -(t + b) / 2,
0, 0, 1, -(near + far) / 2,
0, 0, 0, 1
)
scaleMatrix.set(
2 / (r - l), 0, 0, 0,
0, 2 / (t - b), 0, 0,
0, 0, 2 / (far - near), 0,
0, 0, 0, 1
)
projectionMatrix.set(
near, 0, 0, 0,
0, near, 0, 0,
0, 0, far + near, -far * near,
0, 0, 1, 0
)
const mt = new Matrix4();
mt.set(
1, 0, 0, 0,
0, 1, 0, 0,
0, 0, -1, 0,
0, 0, 0, 1)
projectionMatrix.multiply(mt)
m.multiply(scaleMatrix).multiply(translateMatrix).multiply(projectionMatrix)
return m;
}
位置信息的生成
function getPosition() {
var positions = [
// Front
-1.0, -1.0, 1.0,
1.0, -1.0, 1.0,
1.0, 1.0, 1.0,
-1.0, 1.0, 1.0,
// Back
-1.0, -1.0, -1.0,
-1.0, 1.0, -1.0,
1.0, 1.0, -1.0,
1.0, -1.0, -1.0,
// Top
-1.0, 1.0, -1.0,
-1.0, 1.0, 1.0,
1.0, 1.0, 1.0,
1.0, 1.0, -1.0,
// Bottom
-1.0, -1.0, -1.0,
1.0, -1.0, -1.0,
1.0, -1.0, 1.0,
-1.0, -1.0, 1.0,
// Right
1.0, -1.0, -1.0,
1.0, 1.0, -1.0,
1.0, 1.0, 1.0,
1.0, -1.0, 1.0,
// Left
-1.0, -1.0, -1.0,
-1.0, -1.0, 1.0,
-1.0, 1.0, 1.0,
-1.0, 1.0, -1.0
];
var colorsOfFaces = [
[0.3, 1.0, 1.0, 1.0],
[1.0, 0.3, 0.3, 1.0],
[0.3, 1.0, 0.3, 1.0],
[0.3, 0.3, 1.0, 1.0],
[1.0, 1.0, 0.3, 1.0],
[1.0, 0.3, 1.0, 1.0]
];
var colors: number[] = [];
for (var j = 0; j < 6; j++) {
var polygonColor = colorsOfFaces[j];
for (var i = 0; i < 4; i++) {
colors = colors.concat(polygonColor);
}
}
var elements = [
0, 1, 2, 0, 2, 3,
4, 5, 6, 4, 6, 7,
8, 9, 10, 8, 10, 11,
12, 13, 14, 12, 14, 15,
16, 17, 18, 16, 18, 19,
20, 21, 22, 20, 22, 23
]
return {
positions: positions,
elements: elements,
colors: colors
}
}
const eye = new Vector3(0, 0, 20);
const modelMatrix = getModeMatrix(5, 0, 0.2 * Math.PI);
const projectionMatrix = getProjectMatrix(90,
canvas.width / canvas.height, 1, 50);
const viewMatrix = getViewMatrix(eye);
生成图像
const v = `
precision mediump float;
attribute vec3 a_position;
attribute vec4 a_color;
uniform mat4 u_model;
uniform mat4 u_view;
uniform mat4 u_project;
varying vec4 vColor;
void main(){
vec4 pos = u_project * u_view* u_model* vec4(a_position,1.0);
vColor = a_color;
gl_Position = pos;
}
`
const f = `
precision mediump float;
varying vec4 vColor;
void main() {
gl_FragColor = vColor;
}
`
通过运行上面的代码产生的效果如下图:
总结
模型矩阵、视图矩阵和透视矩阵是三维图形渲染中的关键变换矩阵,它们分别描述了物体的模型变换、相机的视角和透视投影。通过合理地组合和使用这些矩阵,我们可以实现复杂的三维场景渲染,并呈现出逼真的视觉效果。深入理解这些矩阵的原理和应用,对于掌握三维图形渲染技术具有重要意义。代码中使用的threejs的矩阵生成方式