Real-Time-Rendering 向量

        Real-Time-Rendering从第四章开始讲解图形学中常用的数学知识。由于线性代数、几何等相关知识等还给老师了。。。后面开始根据第四章的知识点并参考大学线性代数课本、3D数学基础:图形与游戏开发等书籍记下图形学中常用的几种工具及相关原理。

        首先肯定是向量了,对应unity中Vector2结构体与Vector3结构体。下面通过C#重新实现一个Vector3的结构体来梳理向量相关的知识点。

        在三维坐标中三个点可以表示一个空间中确定的点,同时也可以表示一个方向确定的向量。在unityVector3中写好的静态Up、Down、Back、Forward、Left、Right分别为三维坐标系中长度为1的单位向上、向下、向后、向前、向左、向右的6条向量。同时也可以理解为6个距离原点为1的点。

        新建脚本Vector3。这里类似unity使用结构体。定义x、y、z分量代表向量在x轴、y轴、z轴的值。

        Vector3.Magnitude用来算出当前向量的长度。假设一个向量A(x, y, z),向量长度为√(x² + y² + z²)。Vector3.sqrMagnitude为长度不开根号的结果。在比较两个向量长度时等价于比较长度的平方,并且效率较高。代码如下:

public float Magnitude {
            get {
                return (float)Math.Sqrt(Math.Pow(this.x, 2) + Math.Pow(this.y, 2) + Math.Pow(this.z, 2));
            }
        }

        Vector3.Normalize用来获得当前向量的标准化向量。即与原向量方向相同的长度为1的向量。仅需当前向量各分量分别除以当前向量的长度即可。

public Vector3 Normalized {
            get {
                return this / Magnitude;
            }
        }

        Vector3.Equals用来比较两个向量是否相同。这里重写C#中的Equals方法。同时需要重写Object基类要求重写的GetHashCode方法。只要三个分量完全相同。两条向量则一定朝向一致。即为同一条向量。

public override bool Equals(object other)
        {
            if (!(other is Vector3))
            {
                return false;
            }
            Vector3 vec = (Vector3)other;
            return vec.x == this.x && vec.y == this.y && vec.z == this.z;
        }


        public override int GetHashCode()
        {
            return base.GetHashCode();
        }

        然后是向量运算,这里我们将重写运算符方法中的+、-、*、/、==、!=。

        向量允许相加,只需将各向量的分量相加即可。

        同样向量允许相减,各分量依次相减即可。同时向量允许获取负值,即可分量取负数。

        一般*在向量中的运算叫做点乘或点积。

        向量点乘公式:a·b = [a1 a2 ... an] * [b1 b2 ... bn] = a1b1 + a2b2 + ... + anbn。

        点乘等于向量大小与向量夹角的cos值的积:a·b = |a| |b| cosθ。

        根据上述公式显然能看出点乘的集合意义在于表示两个向量的相似程度。值域在-1到1之间。越接近1,两条向量越相似。越接近-1,两条向量越相反。当乘积在0时,说明此时夹角为90度。即两向量正交。

        同时乘法可用于向量与变量运算,只需把各分量与标量分别相乘即可。

       

public static Vector3 operator *(Vector3 a, float d)
        {
            return new Vector3(a.x * d, a.y * d, a.z * d);
        }


        public static Vector3 operator *(float d, Vector3 a)
        {
            return new Vector3(a.x * d, a.y * d, a.z * d);
        }


        /// <summary>
        /// dot product
        /// </summary>
        /// <param name="a"></param>
        /// <param name="b"></param>
        /// <returns></returns>
        public static float operator *(Vector3 a, Vector3 b) {
            return a.x * b.x + a.y * b.y + a.z * b.z;
        }

        在数学中x运算符对于向量代表的运算称为叉乘。

        向量叉乘公式:a x b = [a1 b1 c1] x [a2 b2 c2] = [(b1c2 - c1b2) (c1a2 - a1c2) (a1b2 - b1a2)]

        注意上式仅用于3维向量相叉乘,并且不满足交换律。

        向量叉乘得到的向量垂直于原来两个向量,该向量的长度等于向量的大小与向量夹角sin值的积。

        |a x b| = |a| |b| sinθ。

        

public static Vector3 Cross(Vector3 a, Vector3 b) {
            return new Vector3(a.y * b.z - a.z * b.y, a.z * b.x - a.x * b.z, a.x * b.y - a.y * b.x);
        }

        Vector3.Angle可获得两个向量的夹角。由上面点乘公式很容易得出:

        θ = arccos(a · b / |a| |b|) = a1b1 + a2b2 + ... + anbn / |a| |b|

public static float Angle(Vector3 a, Vector3 b) {
            double radian = Math.Acos(a * b / (a.Magnitude * b.Magnitude));
            return (float)(radian * 180 / Math.PI);
        }

        Vector3.Product可获取一个向量相对另一个向量的投影。投影公式的推导如下:

        投影公式

public static Vector3 Project(Vector3 vector, Vector3 onNormal)
        {
            return onNormal * (onNormal * vector / (float)(Math.Pow(onNormal.x, 2) + Math.Pow(onNormal.y, 2) + Math.Pow(onNormal.z, 2)));
        }

       

Vector3.Reflect可获得一个向量相对于另一个向量的反射向量。推导如下:

反射公式

public static Vector3 Reflect(Vector3 inDirection, Vector3 inNormal) {
            Vector3 res = inDirection + 2 * (-inDirection) * inNormal * inNormal;
            return res;
        } 

        Vector3.Lerp提供线性插值功能,可获得两个向量各分量相对于t的线性插值。

public static Vector3 Lerp(Vector3 a, Vector3 b, float t) {
            t = Math.Max(0, t);
            t = Math.Min(1, t);
            return new Vector3(a.x + (b.x - a.x) * t, a.y + (b.y - a.y) * t, a.z + (b.z - a.z) * t);
        }

        Vector3.Slerp提供球面插值功能。暂时先放一下。因为球面插值需要用到矩阵或四元数相关的知识。在后面的文章中会补齐。

        

        源码地址:https://github.com/guishengshi/Real-Time-Rendering

        在工程中Scene/Mathlib文件夹可找到Vector3.unity场景文件。这个就是上述实例了。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值