webgl投影视图矩阵

透视图形的原理

1、人眼观察观看物体的效果是进大远小,就向我们观察远处的铁轨,近的地方是平行的,远的地方观察是相交的,如下图所示

图片来源于网络

为啥会产生这种效果呢,这就和人眼成像有所关联。眼睛中的晶状体会根据物体的距离而调节其对光的折射程度,使得光线能够聚焦在视网膜上。当观察远处的物体时,晶状体会变得较扁平,使得光线能够更集中地聚焦在视网膜上,因此远处的物体会看起来较小。而当观察近处的物体时,晶状体会变得较圆,使得光线的聚焦点稍微后移,物体的像在视网膜上放大,所以近处的物体会看起来较大。这种现象被称为视角大小的变化。 

如何产生透视图形

在计算图形学中,我可以通过三种矩阵来进行模拟,这三个矩阵分别是:

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的矩阵生成方式

  • 6
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值