• 图形学 旋转与投影矩阵3


    图形学 旋转与投影矩阵-3

    game101 第二次作业; webgl 实现

    使用 THREEJS 作为基础框架,构建各类矩阵,自定义矩阵运算,最终完成

    1. 正确构建模型矩阵
    2. 正确构建透视投影矩阵
    3. 看到变换后的三角形
    4. 按 A 和 D 三角形能够进行旋转
    5. 按 Q 和 E 三角形能够绕任意过原点的向量进行旋转

    最终效果

    第1次作业-最终效果

    前文简介

    在旋转与投影矩阵第二篇中,详细解释了三维变换的原理,详细解释了视图矩阵,规范立方体,投影矩阵,透视投影矩阵参数的概念。三维变换分为缩放变换,平移变换和旋转变换;视图矩阵描述了当前相机的位置和旋转角度得分复原;规范立方体描述了在默认情况下,元素应该处于 [-1. 1]^3 立方体内;投影矩阵描述了将人眼视角转换为标准相机视角,位于原点,lookAt 为 (0, 0, -1),up 为 (0, 1, 0);透视投影参数描述了已知 fov,aspct,near,far 从而推导其他的参数。

    理论基础均已经讲解完毕,本章主要讲代码实现,列出过程量

    构建透视投影矩阵

    构建透视投影矩阵需要四个参数,fov,aspct,near,far,根据四个参数推导 top,right,bottom,left。将透视相机转换为标准相机需要两步:

    1. 将透视投影转换为正交投影,得到转换矩阵
    2. 将正交投影转换为标准相机,得到正交相机矩阵

    \[M_{persp} =M_{ortho} \times M_{persp \rightarrow ortho} \]

    代码实现

    // 构建一个透视投影矩阵,
    function perspMatrix(fov, aspect, near, far){
        fov = Math.PI*fov/180;
        const top = Math.tan(fov/2) * Math.abs(near);
        const right = aspect * top;
        const bottom = -top;
        const left = -right;
    
        const ortho = new THREE.Matrix4()
            .scale(new THREE.Vector3(2/(right-left), 2/(top-bottom), 2/(near-far)))
            .multiply(new THREE.Matrix4().setPosition(-(right+left)/2, -(top+bottom)/2, -(near+far)/2));
    
        const perseToOrtho = new THREE.Matrix4().set(
            near, 0, 0, 0,
            0, near, 0, 0,
            0, 0, near+far, -1*near*far,
            0, 0,1, 0
        )
        return ortho.clone().multiply(perseToOrtho);
    }
    

    设置 fov 为 90,aspect 为 1,near 为 -3,far 为 -9 的结果

    const per = perspMatrix(90, 1, -3, -9);
    

    \[M_{persp}= \left[ \begin{matrix} -1 & 0 & 0 & 0 \\ 0 & -1 & 0 & 0 \\ 0 & 0 & -2 & -9 \\ 0 & 0 & 1 & 0 \end{matrix} \right] \]

    绕任意向量的旋转变换矩阵

    绕任意过原点的轴的旋转变换矩阵理论基础如下,该表示绕过原点的 n 向量逆时针旋转 θ 角度的变换矩阵

    \[R(n, \theta) = \cos(\theta) \times I + (1-\cos(\theta)) \times n \times n^T + \sin(\theta) \times \left[ \begin{matrix} 0 & -n_z & n_y \\ n_z & 0 & -n_x \\ -n_y & n_x & 0 \end{matrix} \right] \\ n_x, n_y, n_z \: 分别是向量\: \overrightarrow{n} \: 的x,y,z值 \]

    绕过任意点的向量如何计算呢?上篇也介绍过,先移动到原点计算再逆移动回去

    代码如下

    // 绕任意过原点的轴的旋转变换矩阵
    function getAnyRotation(axis, angle){
        const cosTheta = Math.cos(angle);
        const matrix1 = new THREE.Matrix3().multiplyScalar(cosTheta);
        const matrix2 = getSelfMul(axis).multiplyScalar(1-cosTheta);
        const matrix3 = new THREE.Matrix3();
        matrix3.set(
            0, -axis.z, axis.y,
            axis.z, 0, -axis.x,
            -axis.y, axis.x, 0
        );
        matrix3.multiplyScalar(Math.sin(angle));
    
        const result = matrix3Add( matrix3Add(matrix1,matrix2), matrix3);
        return new THREE.Matrix4().setFromMatrix3(result);
    }
    

    设置模型矩阵

    三角形的三个顶点坐标初始值已经固定,若想让其旋转,首要方法就是通过矩阵运算改变顶点坐标,改变模型的位置信息的矩阵叫做模型矩阵,理论基础如下

    \[R_x(\theta)= \left[ \begin{matrix} 1 & 0 & 0 & 0 \\ 0 & \cos(\theta) & -\sin(\theta) & 0 \\ 0 & \sin(\theta) & \cos(\theta) & 0 \\ 0 & 0 & 0 & 1 \end{matrix} \right] \\ \\ R_z(\theta)= \left[ \begin{matrix} \cos(\theta) & -\sin(\theta) & 0 & 0 \\ \sin(\theta) & \cos(\theta) & 0 & 0 \\ 0 & 0 & 1 & 0 \\ 0 & 0 & 0 & 1 \end{matrix} \right] \\ \\ R_y(\theta)= \left[ \begin{matrix} \cos(\theta) & 0 & \sin(\theta) & 0 \\ 0 & 1 & 0 & 0 \\ -\sin(\theta) & 0 & \cos(\theta) & 0 \\ 0 & 0 & 0 & 1 \end{matrix} \right] \]

    这里构造一个绕 z 轴逆时针旋转的模型矩阵

    // 等价于 makeRotationZ  model.makeRotationZ(rotation);
    function rotateZ(model, rotate){
        model.set(
            Math.cos(rotate), -Math.sin(rotate), 0, 0,
            Math.sin(rotate), Math.cos(rotate), 0, 0,
            0, 0,1, 0,
            0, 0, 0, 1
        )
    }
    

    添加交互事件

    调用相应函数即可

    window.addEventListener('keydown', ev => {
        if(ev.key === "a" ){
            rotation += 0.2;
            rotateZ(model, rotation)
            return;
        }
        if ( ev.key === 'd'){
            rotation -= 0.2;
            rotateZ(model, rotation);
            return;
        }
        if ( ev.key === 'q'){
            angle -= 0.2;
            uniforms.anyRotate.value = getAnyRotation(vec, angle);
            return;
        }
        if ( ev.key === 'e'){
            angle += 0.2;
            uniforms.anyRotate.value = getAnyRotation(vec, angle);
        }
    })
    

    总结

    至此完成了 games101 的第二次作业,并以 web 端的形式展示出来,算是做了部分创新,有兴趣的伙伴可以一起探讨,加油

    希望读者在看完后能提出意见, 点个赞, 鼓励一下, 我们一起进步. 加油 !!
  • 相关阅读:
    012 spring retry重试原理的解析
    011 @Retryable的使用
    010 @ControllerAdvice
    009 SpringBoot+Swagger的使用
    008 @Import作用
    007 SpringBoot的@EnableAutoConfiguration注解
    001 品牌管理案例
    000 vue各种基本指令
    013 JS
    002 docker基本的命令
  • 原文地址:https://www.cnblogs.com/xiaxiangx/p/15741165.html
Copyright © 2020-2023  润新知