(转)从零实现3D图像引擎:(6)向量函数库

1. 数学分析

1) 基本定义:

向量由多个分量组成,2D/3D向量表示一条有向线段。下面的ux,uy就是两个分量。

向量u = <ux, uy>,如果从点P1(x1, y1)指向点P2(x2, y2),则:

U = p2 - p1 = (x2-x1, y2-y1) = <Ux, Uy>

向量被定义后,总是相对于原点的,所以可以用一个点来表示从原点指向该点的向量。

2) 向量的范数(norm)

范数就是向量长度,是从原点到终点的距离。用|u|表示,所以:

|U| = sqrt(Ux2 + Uy2)

|U| = sqrt(Ux2 + Uy2 + Uz2)

3) 单位向量与归一化

有时候,我们只关心向量的方向而不关心其长度,所以可以对向量做归一化,使其方向不变,而长度缩放为1,以方便计算。用n'表示。

归一化公式:

n' = n / |n|

4) 标量与向量乘法

对于标量k,标量与向量相乘的公式为:
k * u = k * <ux, uy> = <k * ux, k * uy>

标量与向量乘法的几何意义:缩放一个向量。也可以乘以-1来反转向量。

5) 向量之间相加,将各分量相加即可。

u + v = <ux, uy> + <vx, vy> = <ux + vx, uy + vy>

向量相加的几何意义:平移v的起点至u的终点,则结果为u的起点到平移后的v的终点的线段。如下图:

2011042516034486.png

6) 向量相减,分量相减

u - v = <ux, uy> - <vx, vy> = <ux - vx, uy - vy>

几何意义:减数向量的终点指向被减数向量的终点的线段,如下图:

2011042516070240.png

7) 点积

由于两个向量的分量直接相乘没有什么实际的几何意义,所以一般没用。而点积就十分有用。定义如下:

u.v = ux*vx + uy*vy

点积运算是将两个向量的分量分别相乘,然后再相加,所得的结果是一个标量。

点积的几何意义体现在这个点积公式上:

u.v = |u| * |v| * cos(theta)

即:点积等于两个向量的长度积,再乘以它们之间的夹角的余弦。于是便可以推得夹角的计算方法:

theta = arccos(u.v / (|u| * |v|))

这个公式是很多3D图形学算法的基础,并且如果u和v都是单位向量的话,则|u| = |v| = 1,那么:

theta = arccos(u.v)

下面有4个点积非常重要的定理:

1. 如果u与v垂直,则u.v = 0

2. 如果夹角为锐角,则u.v > 0

3. 如果夹角为钝角,则u.v < 0

4. 如果u与v相等,则u.v = |u| = |v|

那么根据点积的这些性质,我们可以发现由点积带来的一大用途——计算向量在给定方向上的投影向量。

先看下图:

2011042516072987.png

其实思路很简单,既然是求u在v分量上的投影向量,那么方向已经可以知道了,所以所求投影向量的单位向量就等于v的单位向量,所以已经可以求得了该投影向量的单位向量:p(单位) = v / |v|

现在就差长度了,通过上图,可以知道|p| = |u| * cos(theta),综合一下,就可以求得:

p = (v / |v|) * (|u| * cos(theta))

还记得点积公式吗? u.v = |u| * |v| * cos(theta)

所以可以简化上面咱们的推导,得:

p = (u.v * v) / (|v| * |v|)

另外,点积满足以下乘法定律,很好证明,这里省略:

u.v = v.u

u.(v+w) = (u.v + u.w)

k*(u.v) = (k*u).v = u.(k*v)

8) 叉积

首先给出叉积的定义:

u × v = |u| * |v| * sin(theta) * n

其中n是垂直于u和v的单位法向量。

如何求n呢?我们需要建立一个矩阵:

|  i    j    k  |

| ux uy uz |

| vx vy vz  |

其中i,j,k分别是与X、Y、Z轴平行的单位向量。

n是三个标量乘以X、Y、Z轴单位向量的线性组合:

n = (uy*vz - vy*uz)*i - (ux*vz - vx*uz)*j + (ux*vy - vx*uy)*k

所以n = <uy*vz - vy*uz, -ux*vz + vx*uz, ux*vy - vx*uy>

这样求得的n不一定是单位向量,所以需要进行归一化再使用。

其实后面求n不叉积的定义更重要,因为如果要求角度,点积就可以直接计算出来了,所以一般用叉积都是来求法线向量的。

叉积的乘法定律:

u×v = -(v×u)

u×(v+w) = u×v + u×w

(u+v)×w = u×w + v×w

k*(u×v) = (k*u)×v = u×(k*v)

9) 位移向量

先看图:

2011042516075377.png

p1是从原点到点P1的向量,Vd是从点P1到点P2的向量,v'是Vd的单位向量,p是从原点到P2的向量。

还记得向量加法么,我们引入一个参数t来表示所相加的比例,则:

p = p1 + t*v' 其中t的取值范围是[0, |vd|]

或者

p = p1 + t*vd 其中t的取值范围是[0, 1]

这个概念非常重要,因为在游戏中跟踪直线、线段、曲线时,非常有用。

2. 代码实现

void _CPPYIN_Math::VectorAdd(VECTOR2D_PTR va, VECTOR2D_PTR vb, VECTOR2D_PTR vsum)
{vsum->x = va->x + vb->x;vsum->y = va->y + vb->y;
}void _CPPYIN_Math::VectorAdd(VECTOR3D_PTR va, VECTOR3D_PTR vb, VECTOR3D_PTR vsum)
{vsum->x = va->x + vb->x;vsum->y = va->y + vb->y;vsum->z = va->z + vb->z;
}void _CPPYIN_Math::VectorAdd(VECTOR4D_PTR va, VECTOR4D_PTR vb, VECTOR4D_PTR vsum)
{vsum->x = va->x + vb->x;vsum->y = va->y + vb->y;vsum->z = va->z + vb->z;vsum->w = 1;
}void _CPPYIN_Math::VectorSub(VECTOR2D_PTR va, VECTOR2D_PTR vb, VECTOR2D_PTR vsum)
{vsum->x = va->x - vb->x;vsum->y = va->y - vb->y;
}void _CPPYIN_Math::VectorSub(VECTOR3D_PTR va, VECTOR3D_PTR vb, VECTOR3D_PTR vsum)
{vsum->x = va->x - vb->x;vsum->y = va->y - vb->y;vsum->z = va->z - vb->z;
}void _CPPYIN_Math::VectorSub(VECTOR4D_PTR va, VECTOR4D_PTR vb, VECTOR4D_PTR vsum)
{vsum->x = va->x - vb->x;vsum->y = va->y - vb->y;vsum->z = va->z - vb->z;vsum->w = 1;
}void _CPPYIN_Math::VectorScale(double k, VECTOR2D_PTR va, VECTOR2D_PTR vscaled)
{vscaled->x = k * va->x;vscaled->y = k * va->y;
}void _CPPYIN_Math::VectorScale(double k, VECTOR3D_PTR va, VECTOR3D_PTR vscaled)
{vscaled->x = k * va->x;vscaled->y = k * va->y;vscaled->z = k * va->z;
}void _CPPYIN_Math::VectorScale(double k, VECTOR4D_PTR va, VECTOR4D_PTR vscaled)
{vscaled->x = k * va->x;vscaled->y = k * va->y;vscaled->z = k * va->z;vscaled->w = 1;
}double _CPPYIN_Math::VectorDot(VECTOR2D_PTR va, VECTOR2D_PTR vb)
{return (va->x * vb->x) + (va->y * vb->y);
}double _CPPYIN_Math::VectorDot(VECTOR3D_PTR va, VECTOR3D_PTR vb)
{return (va->x * vb->x) + (va->y * vb->y) + (va->z * va->z);
}double _CPPYIN_Math::VectorDot(VECTOR4D_PTR va, VECTOR4D_PTR vb)
{return (va->x * vb->x) + (va->y * vb->y) + (va->z * va->z);
}void _CPPYIN_Math::VectorCross(VECTOR3D_PTR va, VECTOR3D_PTR vb, VECTOR3D_PTR vn)
{vn->x =  ((va->y * vb->z) - (va->z * vb->y));vn->y = -((va->x * vb->z) - (va->z * vb->x));vn->z =  ((va->x * vb->y) - (va->y * vb->x)); 
}void _CPPYIN_Math::VectorCross(VECTOR4D_PTR va, VECTOR4D_PTR vb, VECTOR4D_PTR vn)
{vn->x =  ((va->y * vb->z) - (va->z * vb->y));vn->y = -((va->x * vb->z) - (va->z * vb->x));vn->z =  ((va->x * vb->y) - (va->y * vb->x)); vn->w = 1;
}double _CPPYIN_Math::VectorLength(VECTOR2D_PTR va)
{return sqrt(va->x * va->x + va->y * va->y);
}double _CPPYIN_Math::VectorLength(VECTOR3D_PTR va)
{return sqrt(va->x * va->x + va->y * va->y + va->z * va->z);
}double _CPPYIN_Math::VectorLength(VECTOR4D_PTR va)
{return sqrt(va->x * va->x + va->y * va->y + va->z * va->z);
}void _CPPYIN_Math::VectorNormalize(VECTOR2D_PTR va, VECTOR2D_PTR vn)
{vn->x = 0;vn->y = 0;double length = VectorLength(va);if (length < EPSILON){return;}else{double lengthdao = 1 / length;vn->x = va->x * lengthdao;vn->y = va->y * lengthdao;}
}void _CPPYIN_Math::VectorNormalize(VECTOR3D_PTR va, VECTOR3D_PTR vn)
{vn->x = 0;vn->y = 0;vn->z = 0;double length = VectorLength(va);if (length < EPSILON){return;}else{double lengthdao = 1 / length;vn->x = va->x * lengthdao;vn->y = va->y * lengthdao;vn->z = va->z * lengthdao;}
}void _CPPYIN_Math::VectorNormalize(VECTOR4D_PTR va, VECTOR4D_PTR vn)
{vn->x = 0;vn->y = 0;vn->z = 0;vn->w = 0;double length = VectorLength(va);if (length < EPSILON){return;}else{double lengthdao = 1 / length;vn->x = va->x * lengthdao;vn->y = va->y * lengthdao;vn->z = va->z * lengthdao;vn->w = 1;}
}double _CPPYIN_Math::VectorCos(VECTOR2D_PTR va, VECTOR2D_PTR vb)
{return VectorDot(va, vb) / (VectorLength(va) * VectorLength(vb));
}double _CPPYIN_Math::VectorCos(VECTOR3D_PTR va, VECTOR3D_PTR vb)
{return VectorDot(va, vb) / (VectorLength(va) * VectorLength(vb));
}double _CPPYIN_Math::VectorCos(VECTOR4D_PTR va, VECTOR4D_PTR vb)
{return VectorDot(va, vb) / (VectorLength(va) * VectorLength(vb));
}

不用多说了,都是按照上面数学推导出来的公式直接实现。

3. 代码下载

完整项目源代码下载:>>点击进入下载页<<

之前的一直忘了改资源分默认是1,从这次开始我都改成0了。

4. 补充内容

1) 对于求向量范数的问题,其实这个实现方式效率不高,现在使用的勾股开方的形式实现,而其实可以使用泰勒级数来计算近似值,虽然有一点点误差,但是运算速度大大提高。

2) 你可以发现我在做向量归一化的时候,是先求了lengthdao = 1 / length,然后再去和三个分量做乘法,而不是让他们分别去除以length。其实也是效率原因,计算机做除法的速度远远慢于做乘法,所以我们只做一次除法,而做三次乘法,这样简单的优化带来的效果却是非常明显的。

转自:http://blog.csdn.net/cppyin/archive/2011/02/07/6174087.aspx

转载于:https://www.cnblogs.com/CoolJie/archive/2011/03/03/1970223.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/275642.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

chrome黑暗模式_黑暗模式-并非时尚

chrome黑暗模式In this post I’ve shared my research and views on how the extremely popular “Dark Mode” has moved beyond it’s initial label of “The App Design Fad of 2019”.在这篇文章中&#xff0c;我分享了我的研究和看法&#xff0c;探讨了非常受欢迎的“黑…

花了一天精选了20多篇好文,只为与你分享

大家好&#xff0c;我是若川。很多小伙伴因工作繁忙而没有很多自己的时间去学习新知识&#xff0c;更多的是通过一些碎片化的时间来阅读一些他人的技术文章来提升自己的技术视野以及扩展自己的知识储备。这次我精心整理了一批大佬们的优秀文章&#xff0c;感兴趣的可以阅读关注…

matlab判断电话播键音,MATLAB电话拨号音的合成与识别

1.实验目的1.本实验内容基于对电话通信系统中拨号音合成与识别的仿真实现。主要涉及到电话拨号音合成的基本原理及识别的主要方法&#xff0c;利用 MATLAB 软件以及 FFT 算法实现对电话通信系统中拨号音的合成与识别。并进一步利用 MATLAB 中的图形用户界面 GUI 制作简单直观的…

jquery插件之无缝循环新闻列表

一、效果图&#xff1a; tips源码下载&#xff1a;http://files.cnblogs.com/waitingbar/newslist.rar 二、jquery源码: (function($){$.fn.extend({newsList:function(options){var defaults {actName:li, //显示条数名&#xff1b;maxShowNum:6, //最多的显示…

素描的几大基础知识点_2020年让您感到惊奇的5大素描资源

素描的几大基础知识点Sketch is my favorite stand-alone software that I use every day. It is simple, stable, and fast. During my working process, I use other resources that allow me to create UX/UI design faster. These tools have a different direction, but s…

你不知道的 Chrome DevTools 玩法

大家好&#xff0c;我是若川。今天再分享一篇 chrome devtools 的文章。之前分享过多篇。Chrome DevTools 全攻略&#xff01;助力高效开发 前端容易忽略的 debugger 调试技巧‍笔者在前段时间的开发时&#xff0c;需要通过 Chrome DevTools来分析一个接口&#xff0c;调试中发…

排版人员 快速排版_选择排版前应了解的事项

排版人员 快速排版Design is everywhere, and with design comes text and the content that you’re trying to reach the user with. But before creating your design and choosing what font you want to use, there are some things you should know that will help you a…

若川诚邀你加源码共读群,帮助更多人学会看源码~

小提醒&#xff1a;若川视野公众号面试、源码等文章合集在菜单栏中间【源码精选】按钮&#xff0c;欢迎点击阅读&#xff0c;也可以星标我的公众号&#xff0c;便于查找。回复pdf&#xff0c;可以获取前端优质书籍。最近我创建了一个源码共读的前端交流群&#xff0c;希望尝试帮…

imessage_重新设计iMessage以获得更好的用户体验— UX案例研究

imessage体验设计 (EXPERIENCE DESIGN) Communication is a vital part of our everyday lives. We almost don’t even have to think about it. With social media and our devices as prime tools, we’re constantly finding new ways to stay connected. Instant messagin…

mysql 生成时间轴,MYSQL 时间轴数据 获取同一天数据的前3条

创建表数据CREATE TABLE praise_info (id bigint(20) NOT NULL AUTO_INCREMENT COMMENT ID,pic_id varchar(64) DEFAULT NULL COMMENT 图片ID,created_time datetime DEFAULT CURRENT_TIMESTAMP COMMENT 创建时间,PRIMARY KEY (id),KEY pic_id (pic_id) USING BTREE) ENGINEInn…

【招聘】永辉招前端

大家好&#xff0c;我是若川。这应该招聘第六期。友情帮好友宣传招聘。之前在跟各位读者朋友分享下公众号运营策略 文中提到 公众号主旨是帮助5年内前端小伙伴提升&#xff0c;找到好工作&#xff0c;所以有招聘文。上海 高级前端 本科 25k-50k 16薪岗位职责&#xff1a;1、…

插图 引用 同一行两个插图_插图的目的

插图 引用 同一行两个插图If you’re a designer in tech you’ve likely come across them. Any search for UI or product design on Dribbble will yield at least a few. Amid the sea of pastel blues and pinks, accented neon purples and gamboge yellows, these facel…

VSCode 竟然可以无缝调试浏览器了!

大家好&#xff0c;我是若川。今天周末&#xff0c;分享一篇相对比较简单的文章。学习源码系列、面试、年度总结、JS基础系列。2021-07-16 微软发布了一篇博客专门介绍了这个功能&#xff0c;VSCode 牛逼&#xff01;在此之前&#xff0c;你想要在 vscode 内调试 chrome 或者 e…

最少的编码

Knowing how to code HTML email can bring you many opportunities, such as working as a digital designer, collaborating with front end developers, finding freelancing projects.知道如何对HTML电子邮件进行编码可以为您带来许多机会&#xff0c;例如担任数字设计师&a…

Hulu CEO预计网站本年营收将达5亿美元

网易科技讯 3月2日动静&#xff0c;据国外媒体报道&#xff0c;美国在线视频网站Hulu CEO杰森吉拉尔&#xff08;Jason Kilar&#xff09;明天不日发挥分析&#xff0c;Hulu本年告白及订阅营收将达5亿美元&#xff0c;是去年的两倍。吉拉尔周一在由互联网告白局举办的“2011年年…

面对 this 指向丢失,尤雨溪在 Vuex 源码中是怎么处理的

1. 前言大家好&#xff0c;我是若川。好久以前我有写过《面试官问系列》&#xff0c;旨在帮助读者提升JS基础知识&#xff0c;包含new、call、apply、this、继承相关知识。其中写了 面试官问&#xff1a;this 指向 文章。在掘金等平台收获了还算不错的反馈。最近有小伙伴看我的…

单选按钮步骤流程向导 js_创建令人愉快的按钮的6个步骤

单选按钮步骤流程向导 jsThere is no modern interactive UI without buttons. They are an fundamental part of every digital solution. Learn how to improve the style of your buttons and delight users with perfect style.没有按钮&#xff0c;就没有现代的交互式UI。…

axios怎么封装,才能提升效率?

大家好&#xff0c;我是若川。今天分享一篇axios封装的文章。学习源码系列、面试、年度总结、JS基础系列。作为前端开发者&#xff0c;每个项目基本都需要和后台交互&#xff0c;目前比较流行的ajax库就是axios了&#xff0c;当然也有同学选择request插件&#xff0c;这个萝卜白…

护肤产生共鸣_通过以人为本的设计编织共鸣的20个指针

护肤产生共鸣Deep into a project right now, I can’t help but reflect on how I practice empathy in design. Human centered design means empathising with and designing for people, keeping our focus on people throughout. It is not just one stage, it is a minds…

谷歌已推送 Android Q Beta 1

开发四年只会写业务代码&#xff0c;分布式高并发都不会还做程序员&#xff1f; >>> 今日凌晨&#xff0c;谷歌正式推送了 Android Q 的首个 Beta 版本&#xff0c;Pixel 全系列手机可以尝鲜体验这款最新的系统。 据官方博客介绍&#xff0c;Android Q 为用户带来了…