从零开始实现3D软光栅渲染器 (4) 三维空间变换

2023-11-11 19:10

本文主要是介绍从零开始实现3D软光栅渲染器 (4) 三维空间变换,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

世界是3D的,显示器是2D,将三维空间的物体变换到二维空间,再到最终屏幕上成像的过程,在图形学中叫做3D渲染流水线。这个过程着实有点复杂了,让我们慢慢来,本节我们先介绍一些基本的空间变换知识。

这部分知识点非常重要,不管以后做游戏、仿真项目,还是其他图形应用,物体的空间变换是必须要理解的,因为很多实际的需求需要你自己分析,再去实现。就Unity来说吧,它已经把图形学中的矩阵、向量运算封装的相当易用了,但是如果你不明白它是怎么玩的话,你自己的需求还是很难实现。很多人说Unity上手快,易学,确实,拖拖拽拽,加个脚本组件就能控制游戏对象,但是要想玩得好,就必须要理解相关的图形学背后的原理。虽然你不用真的把Unity封装的那些高数实现一边,但是你要懂,一个函数调用了它会发生什么。

好了,进入今天的主题。

我们先以2D空间为例,推导空间变换的矩阵表示,3D空间的推理是一样的,照葫芦画瓢就行,后面我们给出3D空间的变换的矩阵表示。

什么是矩阵

我们就把它看成一堆神奇的有规律的数即可,不用想的太复杂,它就是个工具。我们不是搞科研,那是数学家的事儿,我们只是应用矩阵这个工具,来解决图形学的问题。我们没必要对矩阵研究非常深入,我们只需要理解它如何在图形学中的应用即可。就像你会开车,不会修车,但这并不会影响你漂移。

记得线性代数书上引入矩阵给了这么个例子:

这个初中就学过了吧 – 二元一次方程。

这俩方程还能写成:

对,这就是矩阵乘法。

一个2x2(两行两列)的矩阵 乘以 一个2x1的矩阵,得到一个2x1的矩阵。

可以看出:一个 n x n 的矩阵 和 一个 n x m 的矩阵相乘,等于一个 n x m 的矩阵。

问题1

那么,请问:n x n 的矩阵 和 m x n 的矩阵相乘,等于多少。

答案:不能相乘。不相信你乘给我看看。

好了。现在已经学会了矩阵乘法了。你看,矩阵也不是很难嘛。

问题2

矩阵和向量(或者说点)相乘怎么计算?

其实点和向量在表示上是没有区别的(x,y),矩阵是工具,它不会因为你是点不是向量而改变运算规则,对于运算的结果,在于人为的解读。举个例子,你炒菜加糖还是加盐,锅是不会拒绝的,但是对于炒出来的菜,你是要负责任的。我想,你应该懂我意思了吧。

这其实不算问题,上面的就是矩阵和向量相乘呀。

如果有人告诉你(x,y)是个向量,那就默认为它是从原点到点(x,y)位置的一个向量。总不能上面把它竖着写,你就不认识了吧。

那个T叫做转置符号,就是横着写和竖着写的区别。向量还是原来的向量,只是后来有钱了,走路都横着走了,但还是原来那个向量。

现在我们知道,向量其实也可以看作是一种特殊的矩阵。

问题3

此时,你可能会问,为什么要转置啊,不转置不行嘛?

行!当然行。

在我看来,转不转置都一样。都是形式而已。中国人这还用问?

不转置的话,上面的矩阵,就得这么写:

1x2 的矩阵 乘以 2x2 的矩阵,得到 1x2 的矩阵。 没毛病。

我们始终要记住,我们最终关心的是最后得到的操作数,怎么解释它是人干的事,或者说你干的事,和你横着写竖着写有关系嘛?只要你遵循对应的运算法则就是咯。你不要问为啥要有转置,存在即合理,就像学校查卫生时,垃圾桶不准放垃圾一样,谁知道那些人咋想的。

问题4

我想看看两个“真”矩阵相乘。

其实,你仔细品下,你自己也能写出来。

下面,我们来看看矩阵是如何表示变换的。

常见的变换就是:平移、旋转、缩放。像其他错切变换、对称变换啥的,哎,怎么说呢,你平时应该用到不到,用到了再学吧。道理都是相通的嘛,学会了基本的,其他的照葫芦画瓢呗。

平移变换

话说,空间中某个物体,算了,来张我的照片吧。

把我的照片从一个地方挪到另一个地方,需要几步?咳咳,你先别管为什么移我的照片。

很简单,数学上,就是把每个点都平移一下。

假设(x,y)为图像上某一点,移动另一个位置(x’,y’),假设就位移了(a,b)吧,意思就是x方向位移了a,y方向位移了b。

那么有:

x' = x + a
y' = y + b

好了。现在你看能不能写成矩阵的形式。

是不是写不出来啊~~~

先放一放啊,我们后面再说。

旋转变换

现在把我的照片转一转。算了,换张你们喜欢看的吧。

现在我们规定逆时针旋转为正。你要规定顺时针旋转为正。也行。前面都说了,关键看人(你)怎么解释。

我们现在取图像上任一点A(x,y),旋转beta角度之后,到达A’

A点原本与X轴的夹角为alpha,为了方便,设: |OA|=|OA’|=r

则我们可以列出以下方程:

整理下:

现在,你把这个写成矩阵乘法的形式看看。

是不是很简单。

缩放变换

这个很简单。

A(x,y) 放大 Sx 和 Sy倍,得到 A’(x’, y’)

x' = x * Sx
y' = y * Sy

写成矩阵的形式:

齐次坐标

好了。基本变换到此结束。

此时,你是不是发现,除了平移变换,旋转和缩放变换都能写成矩阵的形式。别人有的,它当然也想要啊。

所以,引入了齐次坐标。

不知道谁起的这个名字,想象力丰富的我,打死我也想不出来它干的事和它的名字有啥关系。

简单说,就是给向量或者矩阵多加一个维度,方便将基本的变换(平移、旋转、缩放)都可以用矩阵乘法的形式表示。

其实,我们知道,就是为了照顾平移变换。

坐标变换的齐次坐标表示

那么如何表示呢?

我们先将之前的三个变换用齐次坐标表示的矩阵形式写出来:

平移变换:

旋转变换:

缩放变换:

大家快速验算一下,应该都成立吧。

通过观察可知,我们将点(x,y) 变成了齐次坐标(x,y,1).

有时候给你一个(x,y),你知道它是点还是向量?因为大家都知道,向量的代数表示和点是一样的。

这个时候就是齐次坐标的第二个妙用。

点(x,y) 写成齐次坐标是: (x,y,1)

向量(x,y) 写成齐次坐标是: (x,y,0)

为啥?

我也不知道为啥,我就觉得这么干是对的,而且很神奇。

咱们来验算一下:点是有位置的,向量是没有位置的,是吧。

那咱们用平移矩阵来验算一下不就好了嘛。

数学是不是很奇妙。

那这个有啥用呢?

后面推导3D渲染流水线的投影变换阶段好像用到,到时候再说吧。

其实,一个点或者向量的齐次表示不是唯一的,点(x,y)可以表示成(kx,ky,k),向量(x,y)可以表示成(kx,ky,0). 这个不难理解吧。

三维空间变换

三维空间变换矩阵的推导和二维空间很相似,只不过多了一个维度嘛。

三维坐标系,有2种,左手坐标系和右手坐标系,区别就在于Z轴的朝向不同。为啥有2种。额,我想这个和人有左撇子和右撇子道理应该差不多吧。

那么,我们用那种呢?前面已经说了,随便你,只要你选定一种坐标系,数学推导的过程是完全一样的。我们这里推导使用的是右手坐标系,因为我是右撇子。

平移矩阵,几乎和二维变化一样:

旋转矩阵,二维空间绕的是原点,三维空间绕的是坐标轴。

绕z轴旋转某个角度的旋转矩阵:

大家是不是有点眼熟?对吗,这不就是二维旋转矩阵加了一个维度嘛。大家想想,绕z轴旋转,是不是在XY平面内?那是不是就是二维旋转?这不就得了,那绕x轴和绕y轴旋转,不就是一样的道理嘛?

绕x轴旋转某角度:

绕y轴旋转某角度:

有个事情要注意下啊,我们这里推导的变换矩阵和你们在其他地方看到的可能不一样,别着急。你转置一下看看是不是就一样的了。这是最后一遍唠叨了啊,转不转置,你自己看着办,你喜欢用哪种就用哪种,关键要用对场合。我的建议是,根据你以后学习的图形编程API来决定使用那种风格的矩阵、坐标系。比如说,OpenGL,使用的是右手坐标系,使用的是向量右乘(矩阵x向量, 而不是向量x矩阵,前者是列向量,就是竖着写的那个,后者是横向量,即使横着写的那个)。所以,我就是按着这个推导的,并不是因为我是右撇子。

本节介绍的基本变换,经过组合,可以形成复杂的变换。

怎么组合?

多个变换矩阵相乘呀,注意,相乘的顺序哟。这个现在你是感受不到的,只有你实战的时候,你才能发现区别。

好了,本节的内容到此就结束了。

小伙伴们有什么建议,可以留言。我也不知道是不是讲得太细,或者讲得太粗,就按照自己的想法来。大家有什么建议可以说出来。或者如果大家有感兴趣的话题,也可以说出来。只要我会的,我也会写相关的教程。其实,我也不是科班出身,只是兴趣使然,欢迎小伙伴一起探讨学习。

欢迎大家关注我的公众号【OpenGL编程】,定期分享OpenGL相关的3D编程教程、算法、小项目。欢迎大家一起交流。

在这里插入图片描述

这篇关于从零开始实现3D软光栅渲染器 (4) 三维空间变换的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!


原文地址:https://blog.csdn.net/birdflyto206/article/details/105591067
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.chinasem.cn/article/392237

相关文章

linux批量替换文件内容的实现方式

《linux批量替换文件内容的实现方式》本文总结了Linux中批量替换文件内容的几种方法,包括使用sed替换文件夹内所有文件、单个文件内容及逐行字符串,强调使用反引号和绝对路径,并分享个人经验供参考... 目录一、linux批量替换文件内容 二、替换文件内所有匹配的字符串 三、替换每一行中全部str1为st

SpringBoot集成MyBatis实现SQL拦截器的实战指南

《SpringBoot集成MyBatis实现SQL拦截器的实战指南》这篇文章主要为大家详细介绍了SpringBoot集成MyBatis实现SQL拦截器的相关知识,文中的示例代码讲解详细,有需要的小伙伴... 目录一、为什么需要SQL拦截器?二、MyBATis拦截器基础2.1 核心接口:Interceptor

SpringBoot集成EasyPoi实现Excel模板导出成PDF文件

《SpringBoot集成EasyPoi实现Excel模板导出成PDF文件》在日常工作中,我们经常需要将数据导出成Excel表格或PDF文件,本文将介绍如何在SpringBoot项目中集成EasyPo... 目录前言摘要简介源代码解析应用场景案例优缺点分析类代码方法介绍测试用例小结前言在日常工作中,我们经

基于Python实现简易视频剪辑工具

《基于Python实现简易视频剪辑工具》这篇文章主要为大家详细介绍了如何用Python打造一个功能完备的简易视频剪辑工具,包括视频文件导入与格式转换,基础剪辑操作,音频处理等功能,感兴趣的小伙伴可以了... 目录一、技术选型与环境搭建二、核心功能模块实现1. 视频基础操作2. 音频处理3. 特效与转场三、高

Python实现中文文本处理与分析程序的示例详解

《Python实现中文文本处理与分析程序的示例详解》在当今信息爆炸的时代,文本数据的处理与分析成为了数据科学领域的重要课题,本文将使用Python开发一款基于Python的中文文本处理与分析程序,希望... 目录一、程序概述二、主要功能解析2.1 文件操作2.2 基础分析2.3 高级分析2.4 可视化2.5

Java实现预览与打印功能详解

《Java实现预览与打印功能详解》在Java中,打印功能主要依赖java.awt.print包,该包提供了与打印相关的一些关键类,比如PrinterJob和PageFormat,它们构成... 目录Java 打印系统概述打印预览与设置使用 PageFormat 和 PrinterJob 类设置页面格式与纸张

使用Go实现文件复制的完整流程

《使用Go实现文件复制的完整流程》本案例将实现一个实用的文件操作工具:将一个文件的内容完整复制到另一个文件中,这是文件处理中的常见任务,比如配置文件备份、日志迁移、用户上传文件转存等,文中通过代码示例... 目录案例说明涉及China编程知识点示例代码代码解析示例运行练习扩展小结案例说明我们将通过标准库 os

Python实现终端清屏的几种方式详解

《Python实现终端清屏的几种方式详解》在使用Python进行终端交互式编程时,我们经常需要清空当前终端屏幕的内容,本文为大家整理了几种常见的实现方法,有需要的小伙伴可以参考下... 目录方法一:使用 `os` 模块调用系统命令方法二:使用 `subprocess` 模块执行命令方法三:打印多个换行符模拟

SpringBoot+EasyPOI轻松实现Excel和Word导出PDF

《SpringBoot+EasyPOI轻松实现Excel和Word导出PDF》在企业级开发中,将Excel和Word文档导出为PDF是常见需求,本文将结合​​EasyPOI和​​Aspose系列工具实... 目录一、环境准备与依赖配置1.1 方案选型1.2 依赖配置(商业库方案)二、Excel 导出 PDF

Python实现MQTT通信的示例代码

《Python实现MQTT通信的示例代码》本文主要介绍了Python实现MQTT通信的示例代码,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一... 目录1. 安装paho-mqtt库‌2. 搭建MQTT代理服务器(Broker)‌‌3. pytho