解释齐次坐标和投影几何

news2024/11/15 23:38:53

以前的翻译文章,存档。翻译自:https://www.tomdalling.com/blog/modern-opengl/explaining-homogenous-coordinates-and-projective-geometry/, 有改动

此处输入图片的描述
在本文中,我将尽可能简单地解释齐次坐标(即4D坐标)。在之前的文章中,我们用了4D向量来做矩阵乘法,但是我从来没有真正定义过四维数。现在是时候近距离观察投影几何了。

术语

在使用3D技术的大多数时候,我们都在考虑欧几里得几何的关系——即三维空间中的坐标(X、Y、Z)。然而,在某些情况下,用投影几何学来思考是有用的。投影几何除了X、Y和Z之外有一个额外的维度,叫做W。这个四维空间称为“投影空间(projective space)”,投影空间中的坐标称为“齐次坐标(homogeneous coordinates)”。

不是四元数

四元数看起来很像齐次坐标。它们都是4D向量,通常表示为(X,Y,Z,W)。然而,四元数和齐次坐标是不同的概念,有不同的用途。

与2D的一个类比

首先,让我们看看投影几何是如何在2D中工作的,然后再看3D。

想象一个投影仪将二维图像投射到屏幕上。很容易看出投影图像的X和Y轴:

W维是指从投影仪到屏幕的距离

现在,如果你从这幅2D图像前后退一步,看向投影仪和屏幕,你就可以看到W维。W维是指从投影仪到屏幕的距离。

此处输入图片的描述

那么W维到底是做什么的呢?想象一下如果增加或减少W值——也就是说,如果增加或减少投影仪和屏幕之间的距离,二维图像会发生什么。如果你把投影仪靠近屏幕,整个2D图像就会变小。如果你把投影仪从屏幕移开,2D图像会变得更大。正如你所看到的,W的值影响图像的大小(也称为比例)。

此处输入图片的描述

应用于3D

目前还没有3D投影仪,所以很难想象3D的投影几何,但是W值的工作原理和2D是一样的。当W增加时,坐标放大。当W变小时,坐标缩小。W基本上是三维坐标的缩放变换。

当W = 1的时候

对于3D编程初学者通常的建议是,每当将3D坐标转换为4D坐标时,总是设置W=1。这样做的原因是当你把一个坐标缩放1倍时它不会收缩或放大,它会保持相同的大小。当W=1时它对X, Y, Z都没有影响。

因此,当涉及到3D计算机图形时,只有当W=1时,坐标才被认为是“正确的”。如果你用W>1表示坐标,那么所有东西看起来都太小了,而W< 1则所有东西看起来都太大了。如果你尝试用W=0渲染,当程序试图除以0时就会崩溃。在W< 0的情况下,所有的东西都会上下、前后颠倒的。

从数学上讲,不存在“不正确”的齐次坐标。使用W=1的坐标对于3D计算机图形来说只是一个有用的约定。

数学上

假设投影仪离屏幕3米远,在二维图像的坐标(15,21)处有一个点。这给出了投影坐标向量(X,Y,W)=(15,21,3)。

此处输入图片的描述

现在,假设投影仪被推得离屏幕更近,距离是1米。投影仪越接近屏幕,图像就越小。投影仪靠近了三倍,所以图像变小了三倍。如果取原来的坐标向量,把所有的值都除以3,就得到W=1的新向量:

( 15 3 , 21 3 , 3 3 ) = ( 5 , 7 , 1 ) (\frac{15}{3},\frac{21}{3},\frac{3}{3})=(5,7,1) (315,321,33)=(5,7,1)

则点目前的坐标(5,7).

此处输入图片的描述

这就是“不正确的”齐次坐标如何转换成“正确的”坐标的方法:将所有的值除以w。

将一个向量中的所有值除以标量乘以除数的倒数。这里有一个4D的例子:

1 5 ( 10 , 20 , 30 , 5 ) = ( 10 5 , 20 5 , 30 5 , 5 5 ) = ( 2 , 4 , 6 , 1 ) \frac{1}{5} (10, 20, 30, 5) = (\frac{10}{5}, \frac{20}{5}, \frac{30}{5}, \frac{5}{5}) = (2,4,6,1) 51(10,20,30,5)=(510,520,530,55)=(2,4,6,1)

使用GLM编写的c++示例如下:

glm::vec4 coordinate(10, 20, 30, 5);
glm::vec4 correctCoordinate = (1.0/coordinate.w) * coordinate;
//now, correctCoordinate == (2,4,6,1) 

齐次坐标在计算机图形学中的应用

如上所述,对于3D计算机图形,齐次坐标在某些情况下是有用的。我们来看看这里的一些情况。

3D坐标的平移矩阵

旋转和缩放变换矩阵只需要三列。但是,为了进行平移,矩阵至少需要有四列。这就是为什么变换通常是4x4矩阵。然而,由于矩阵乘法的规则,一个四列的矩阵不能与一个三维向量相乘。一个四列矩阵只能与一个四元向量相乘,这就是为什么我们经常使用齐次的4D向量而不是三维向量。

在矩阵变换中使用齐次坐标时,第四维W通常是不变的。当将一个三维坐标转换成一个四维坐标时,w要设置为1,并且在变换矩阵被应用之后,它通常仍然是1,在这一点上,它可以通过忽略W来转换回三维坐标。这适用于所有的平移、旋转和缩放转换,它们是到目前为止最常见的转换类型。值得注意的例外是投影矩阵,它确实影响W值。

透视变换

在3D技术中,“透视”是指物体在距离相机越远的地方越小的现象。如果猫离摄像机足够近的话,远处的山看起来可能比猫还小。

此处输入图片的描述

透视在三维计算机图形中是通过使用变换矩阵来实现的,变换矩阵可以改变每个顶点的W值。将摄像机矩阵应用到每个顶点后,在投影矩阵应用之前,每个顶点的Z值表示距离摄像机的距离。因此,Z越大,顶点的比例就越小。W值影响比例,所以投影矩阵只是根据Z值改变W值。这里有一个透视投影矩阵应用于齐次坐标的例子:

[ 1 0 0 0 0 1 0 0 0 0 1 0 0 0 1 0 ] [ 2 3 4 1 ] = [ 2 3 4 4 ] \begin{bmatrix} 1 & 0 & 0 & 0 \\ 0 & 1 & 0 & 0 \\ 0 & 0 & 1 & 0 \\ 0 & 0 & 1 & 0 \end{bmatrix} \begin{bmatrix} 2 \\ 3 \\ 4 \\ 1 \end{bmatrix} = \begin{bmatrix} 2 \\ 3 \\ 4 \\ 4 \end{bmatrix} 1000010000110000 2341 = 2344

注意W值是如何从Z值变成4的。

应用透视投影矩阵后,每个顶点进行“透视分割”。透视图除法是将齐次坐标转换回W=1的一个特定术语,如本文前面所述。继续上面的例子,透视分割步骤如下:

1 4 ( 2 , 3 , 4 , 4 ) = ( 0.5 , 0.75 , 1 , 1 ) \frac{1}{4} (2, 3, 4, 4) = (0.5, 0.75, 1, 1) 41(2,3,4,4)=(0.5,0.75,1,1)

经过透视分割后,W值被丢弃,我们得到一个3D坐标,这就是根据3D透视投影被正确缩放的值。

在GLM中,这个透视投影矩阵可以使用GLM::perspective或GLM::frustum函数来创建。在老式的OpenGL中,它通常使用gluPerspective或gluFrustum函数创建。在OpenGL中,透视分割是在顶点着色器在每个顶点上运行之后自动发生的。这就是为什么gl_Position(顶点着色器的主要输出)是一个4D向量,而不是一个3D向量的原因之一。

放置方向光

齐次坐标的一个性质是它们允许你在无穷远(无限长度的向量)处有一点,这在三维坐标系中是不可能的。当W=0时,无穷远处的点就会出现。如果你试着把W=0齐次坐标转换成一个普通的W=1坐标,就会得到一系列的乘零运算:

1 0 ( 2 , 3 , 4 , 0 ) = ( 2 0 , 3 0 , 4 0 , 0 0 ) \frac{1}{0} (2, 3, 4, 0) = (\frac{2}{0}, \frac{3}{0}, \frac{4}{0}, \frac{0}{0}) 01(2,3,4,0)=(02,03,04,00)

这意味着W=0的齐次坐标不能转换为三维坐标。

这个能有什么用呢?方向光可以是无限远的点光源。当一个点光源无限远时,光线就会变得平行,所有的光都沿着一个方向传播。这就是方向光的定义。

所以传统上,在3D图形中,方向光与点光源的区别在于光源位置向量中W的值。如果W=1,那么它就是点光源。如果W=0,那么它就是一个方向光。

这更像是一种传统约定,而不是编写光照代码的有用方法。方向光和点光源通常用单独的代码实现,因为它们的行为不同。一个典型的光照着色器可能看起来是这样的:

if(lightPosition.w == 0.0){
    //directional light code here
} else {
    //point light code here
}

总结

齐次坐标有一个额外的维度W,它可以缩放X,Y和Z的值。用于平移和透视投影变换的矩阵只能应用于齐次坐标,这就是为什么它们在3D计算机图形中如此常见。当W=1时,X、Y和Z值被认为是“正确的”。任何齐次坐标都可以通过四维数除以W值来转换为W=1,除非W=0。当W=0时,坐标表示无穷远处的点(一个无限长的矢量),这通常用来表示方向光的方向。

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

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

相关文章

【MySQL练习及单表查询】

一、MySQL练习 一.创建表&#xff1a; 创建员工表employee&#xff0c;字段如下&#xff1a; id&#xff08;员工编号&#xff09; name&#xff08;员工名字&#xff09; gender&#xff08;员工性别&#xff09; salary&#xff08;员工薪资&#xff09; 二.插入数据 1&…

C++primer(第五版)第十四章(重载运算与类型转换)

14.1基本概念 重载运算符和重载其他函数差不多,运算符有多少参数,那么重载运算符就有多少参数.不同的是重载运算符有名字方面的限制,是operator加上运算符号.且除了函数调用运算符()之外,不允许有默认实参. 以上其中逻辑与运算符,逻辑或运算符,逗号运算符,取地址运算符不推荐…

多元分类预测 | Matlab偏小二乘PLS分类预测,多特征输入模型

文章目录 效果一览文章概述部分源码参考资料效果一览 文章概述 多元分类预测 | Matlab偏小二乘PLS分类预测,多特征输入模型 多特征输入单输出的二分类及多分类模型。程序内注释详细,直接替换数据就可以用。程序语言为matlab,程序可出分类效果图,迭代优化图,混淆矩阵图。 部…

AutoHotkey配置鼠标光标在双屏幕之间瞬移

双屏幕带来的问题 使用双屏幕会带来两个问题&#xff1a;一个是窗口的切换移动不方便&#xff0c;另一个是鼠标的切换移动不方便。 如果需要将屏幕 A 的窗口快速移动到屏幕 B&#xff0c;可以用过 win shift 左右键来实现。 但是令人头疼的一个问题是鼠标还停留在屏幕 A&a…

Python 识别某验消消乐验证码

某验的消消乐验证码识别就很简单了,在一个九宫格里,找出相邻的两个元素交换后,保证有一行或者一列的元素相同,如下图: 如果这一个九宫格是一张图片的话,我们将其分割成9个部分,然后每一格都将其标注为一个类别就行,或者直接从源码中拿到9张图片,最终的话,是转成一个…

Devops系列六(CI篇之jenkinsfile)jenkins将gitlab helm yaml和argocd 串联,自动部署到K8S

一、为什么是jenkinsfile 上文我们说了pipeline&#xff0c;已为本文铺路不少&#xff0c;接下里就是将之串联起来。 先想说下&#xff0c;为什么是jenkinsfile, 因为jenkins job还支持pipeline方式。 这种方式&#xff0c;不建议实际使用&#xff0c;仅限于测试或调试groovy…

2 第一个springboot程序

2.1 创建springboot项目 有两种方法&#xff1a;一种是从官网上下载jar包&#xff0c;然后使用idea的import导入。另一种是直接在idea创建springboot项目。 2.1.1 方法一 进入spring官网&#xff08;Spring | Home&#xff09;。 点击projects的spring boot。 点击overview。 …

【java】空字符串“”和null区别,以及判空的方案

空字符串“” 1、类型&#xff1a;“”是一个空字符串&#xff08;String&#xff09;&#xff0c;长度为0&#xff0c;占内存&#xff0c;在内存中分配一个空间&#xff0c;可以使用Object对象中的方法。&#xff08;例如&#xff1a;“”.toString()等&#xff09; 2、内存分…

【Distributed】分布式监控系统zabbix应用(一)

文章目录 前言一、Zabbix基本概述1. Zabbix 的概念2. Zabbix 的工作原理3. Zabbix 6.0 新特性3.1 Zabbix 可以实现高可用3.2 Zabbix 新增监控系统 4. Zabbix 的构成5. Zabbix 的功能组件5.1 数据库5.2 Web 界面5.3 Zabbix Agent5.4 Zabbix Proxy5.5 Java Gateway 6. Zabbix 和 …

【JavaWeb】了解JavaScript DOM API

目录 1、什么是DOM 1.1、DOM树 1.2、 了解事件 2、常用的DOM API 2.1、选中页面元素 2.2、获取/修改元素内容 2.2.1、innerHTML和innerText 2.2、获取/修改元素属性 2.3、获取/修改表单元素属性 3、JQuery框架的简单了解和使用 4、代码案例&#xff1a;实现聚合索引功…

python 常用数据结构-元祖

Tuple 元组 元组定义与使用元组常用方法元组与列表 元组定义 元组是有序的不可变对象集合元组使用小括号包围&#xff0c;各个对象之间使用逗号分隔定义元祖可以不用括号&#xff0c;直接使用,隔开元组是异构的&#xff0c;可以包含多种数据类型 元组使用&#xff1a;创建 …

解决Unity的PostProcess奇怪报错

大家好&#xff0c;我是阿赵。   最近在使用Unity的PostProcess后处理效果的时候&#xff0c;发现了一个问题&#xff0c;下面记录一下这个问题的出现原因和解决办法。 一、出现问题 问题是这样出现的&#xff1a;   在场景里面添加某一个后处理效果后&#xff0c;当这个后…

JUC之ThreadLocal

文章目录 1 基础知识1.1 强软弱虚四种引用 2 ThreadLocal出现的好处3 ThreadLocal源码分析3.1 ThreadLocal内存泄露问题3.2 ThreadLocal为什么使用的是弱引用3.3 清扫过期的Entry 4 ThreadLocal使用建议 1 基础知识 1.1 强软弱虚四种引用 【整体结构】 【强引用】 【软引用…

(文件[夹]批量分类整理_多级匹配_交叉匹配_路径结构交叉调整)文件[夹]批量复制

​ 首先&#xff0c;需要用到的这个工具&#xff1a; 百度 密码&#xff1a;qwu2 蓝奏云 密码&#xff1a;2r1z ​如果没有使用过此工具&#xff0c;建议阅读此教程前&#xff0c;先看下这篇文章&#xff1a;https://mp.csdn.net/mp_blog/creation/editor/113605549 原理&…

【Python爬虫与数据分析】文件读写

目录 一、概述 二、txt文件读写 三、csv文件读写 四、Json文件读写 一、概述 读写模式&#xff1a; r&#xff1a;读数据w&#xff1a;覆盖写入a&#xff1a;追加写入rb&#xff1a;以二进制形式读出wb&#xff1a;以二进制形式写入ab&#xff1a;以二进制形式追加r、w、…

Java log 日志

文章目录 log4j.properties配置 log4j.properties 配置 log4j.rootLoggerINFO, stdoutlog4j.appender.stdoutorg.apache.log4j.ConsoleAppender log4j.appender.stdout.layoutorg.apache.log4j.PatternLayout log4j.appender.stdout.layout.ConversionPattern%d{yyyy-MM-dd H…

MySQL单表查询练习题

目录 第一题 第二题 第三题 第一题 1.创建数据表pet&#xff0c;并对表进行插入、更新与删除操作&#xff0c;pet表结构如表8.3所示。 (1&#xff09;首先创建数据表pet&#xff0c;使用不同的方法将表8.4中的记录插入到pet表中。 mysql> create table pet( name varchar(…

网络安全(黑客)内网渗透基础知识

0x01 内网概述 内网也指局域网&#xff08;Local Area Network&#xff0c;LAN&#xff09;是指在某一区域内由多台计算机互联成的计算机组。一般是方圆几千米以内。局域网可以实现文件管理、应用软件共享、打印机共享、工作组内的历程安排、电子邮件和传真通信服务等功能。 内…

交叉导轨的结构与特长

在交叉导轨中&#xff0c;精密滚柱互相直交地组合在一起的滚柱保持架与设置在专用轨道上的90V形沟槽滚动面组合起来使用。通过将2列滚子导轨平行地装配&#xff0c;使导轨系统能承受4个方向的负荷。而且&#xff0c;因能向交叉滚子导轨施加预压&#xff0c;从而能获得无间隙且高…

zookeeper单机安装

1 检查环境jdk 参考&#xff1a;https://blog.csdn.net/weixin_44098426/article/details/128446376 2 解压安装包 mkdir -p /opt/zookeeper mv /home/wh/software/zk/apache-zookeeper-3.5.7-bin.tar.gz /opt/zookeeper tar -xzvf apache-zookeeper-3.5.7-bin.tar.gz 3 配置…