【OpenGL的数学01】从窗口空间计算视空间

文章目录

  • 一、说明
  • 二、定义
  • 三、来自gl_FragCoord
  • 四、来自gl_FragCoord的XYZ
    • 4.1 从窗口到ndc
    • 4.2 从NDC到剪辑
    • 4.3 从剪辑到眼睛
    • 4.4 GLSL示例
  • 五、从gl_FragCoord的XYZ优化方法

一、说明

本文将解释如何在给定窗口空间顶点位置的情况下重新计算眼空间顶点位置。以及相反的计算。其中包括一些数学概念,需要仔细推敲。

二、定义

在开始之前,我们需要定义一些符号:

象征意义程序对应
M投影矩阵 M p r j M_{prj} Mprj
P眼空间位置,4D 矢量Lookat
C剪辑空间位置,4D 矢量
N归一化器件坐标空间位置,3D矢量模型坐标
W窗口空间位置,3D 矢量窗口中心的绝对位置
Vx, y传递给 glViewport 的 X 和 Y 值
Vw, h传递给 glViewport 的 width 和 height 值
Dn, f传递给 glDepthRange 的 near 和 far 值三维视线锥体的绝对位置

上表中,世界坐标就是绝对坐标,是一切度量的根本原点坐标。

三、来自gl_FragCoord

gl_FragCoord.xyz 是窗口空间位置 W,一个 3D 向量。gl_FragCoord.w 包含剪辑空间 W 的倒数:

g l _ F r a g C o o r d w = 1 C w . gl\_FragCoord_{w}={\tfrac {1}{C_{w}}}. gl_FragCoordw=Cw1.

给定这些值,我们有一个相当简单的方程组:
在这里插入图片描述

在 GLSL 片段着色器中,代码如下所示:

vec4 ndcPos;
ndcPos.xy = ((2.0 * gl_FragCoord.xy) - (2.0 * viewport.xy)) / (viewport.zw) - 1;
ndcPos.z = (2.0 * gl_FragCoord.z - gl_DepthRange.near - gl_DepthRange.far) /(gl_DepthRange.far - gl_DepthRange.near);
ndcPos.w = 1.0;vec4 clipPos = ndcPos / gl_FragCoord.w;
vec4 eyePos = invPersMatrix * clipPos;

这假定存在一个名为 viewport 的统一,该 viewport 是一个 vec4,按照传递给该函数的顺序与 glViewport 的参数匹配。此外,这假设 invPersMatrix 是透视投影矩阵的逆矩阵(不提倡在片段着色器中计算)。请注意,gl_DepthRange 是可用于片段着色器的内置变量。

四、来自gl_FragCoord的XYZ

这种情况对于延迟渲染技术非常有用,但最后一种方法也非常有用。在延迟渲染中,我们将对象的材质参数渲染为图像。然后,我们对这些图像进行多次传递,加载这些材料参数并对它们执行照明计算。

在光通道中,我们需要重建眼空间顶点位置,以便进行照明。但是,我们实际上没有gl_FragCoord;不适用于产生材料参数的片段。取而代之的是,我们有 gl_FragCoord.xy 中的窗口空间 X 和 Y 位置,并且我们有窗口空间深度,通过访问深度缓冲区进行采样,该缓冲区也是从延迟传递中保存的。

我们缺少的是原始的窗口空间 W 坐标。

因此,我们必须找到一种方法,从窗口空间 XYZ 坐标和透视投影矩阵来计算它。本讨论将假定您的透视投影矩阵采用以下形式:

[ xx xx xx xx ]
[ xx xx xx xx ]
[ 0 0 T1 T2 ]
[ 0 0 E1 0 ]
xx 表示“任何”,它们可以是您在投影中使用的任何值。0 在投影矩阵中必须为零。T1、T2 和 E1 可以是任意项,具体取决于投影矩阵的工作方式。

如果您的投影矩阵不适合此形式,则以下代码将变得更加复杂。

4.1 从窗口到ndc

我们有窗口空间的 XYZ:

W ⃗ = [ g l _ F r a g C o o r d . x g l _ F r a g C o o r d . y f r o m D e p t h T e x t u r e ] {\vec W}={\begin{bmatrix}gl\_FragCoord.x\\gl\_FragCoord.y\\fromDepthTexture\end{bmatrix}} W = gl_FragCoord.xgl_FragCoord.yfromDepthTexture

从窗口空间计算 NDC 空间与上述相同:

N ⃗ = [ ( 2 ∗ W x ) − ( 2 ∗ V x ) V w − 1 ( 2 ∗ W y ) − ( 2 ∗ V y ) V h − 1 ( 2 ∗ W z ) − D f − D n D f − D n ] {\displaystyle {\vec {N}}={\begin{bmatrix}{\tfrac {(2*W_{x})-(2*V_{x})}{V_{w}}}-1\\{\tfrac {(2*W_{y})-(2*V_{y})}{V_{h}}}-1\\{\tfrac {(2*W_{z})-D_{f}-D_{n}}{D_{f}-D_{n}}}\end{bmatrix}}} N = Vw2Wx2Vx1Vh2Wy2Vy1DfDn2WzDfDn

请记住:在本例中,视口和景深范围参数是用于渲染原始场景的参数。当然,视口不应该改变,但深度范围肯定可以改变(假设你甚至在延迟渲染器的光照通道中有一个深度范围)。

4.2 从NDC到剪辑

为简单起见,以下是从 NDC 空间到剪辑空间的公式:

C w = T 2 N z − T 1 E 1 C ⃗ x y z = N ⃗ ∗ C w {\begin{aligned}C_{w}&={\tfrac {T2}{N_{z}-{\tfrac {T1}{E1}}}}\\{\vec C}_{{xyz}}&={\vec N}*C_{w}\end{aligned}} CwC xyz=NzE1T1T2=N Cw

推导
推导这两个等价位是非常不平凡的;这是一个相当大的绊脚石。让我们从我们所知道的开始。

我们可以从剪辑空间转换为 NDC 空间,这样我们就可以返回:

N ⃗ = C ⃗ C w C ⃗ = N ⃗ ∗ C w {\begin{aligned}{\vec N}&={\tfrac {{\vec C}}{C_{w}}}\\{\vec C}&={\vec N}*C_{w}\end{aligned}} N C =CwC =N Cw

问题是我们没有 Cw。我们之前能够使用 gl_FragCoord.w 来计算它,但是当我们在延迟照明通过中事后执行此操作时,这不可用。

那么我们如何计算它呢?好吧,我们知道剪辑空间位置最初是这样计算的:

C ⃗ = M ∗ P ⃗ {\vec C}=M*{\vec P} C =MP

因此,我们知道 Cw 是由 P 的点积与 M 的第四行计算得出的。鉴于我们上面对 M 第四行的定义,我们可以得出结论:
C w = E 1 ∗ P z N ⃗ = C ⃗ E 1 ∗ P z {\begin{aligned}C_{w}&=E1*P_{z}\\{\vec N}&={\tfrac {{\vec C}}{E1*P_{z}}}\end{aligned}} CwN =E1Pz=E1PzC

当然,这只是用一个未知来换取另一个未知。但是我们可以使用它。事实证明,Nz 与此有一些共同点:
N z = C z E 1 ∗ P z N_{z}={\tfrac {C_{z}}{E1*P_{z}}} Nz=E1PzCz

看看 Cz 的来源很有趣。和以前一样,我们知道它是由 P 的点积与第三行 M 计算得出的。再一次,鉴于我们上面对 M 的定义,我们可以得出结论:

C z = T 1 ∗ P z + T 2 ∗ P w N z = T 1 ∗ P z + T 2 ∗ P w E 1 ∗ P z {\begin{aligned}C_{z}&=T1*P_{z}+T2*P_{w}\\N_{z}&={\tfrac {T1*P_{z}+T2*P_{w}}{E1*P_{z}}}\end{aligned}} CzNz=T1Pz+T2Pw=E1PzT1Pz+T2Pw

我们这里仍然有两个未知值,Pz 和 Pw。但是,我们可以假设 Pw 为 1.0,因为眼空间位置通常就是这种情况。给定这个假设,我们只有一个未知数 Pz,我们可以求解它:
在这里插入图片描述

KaTeX parse error: Expected & or \\ or \cr or \end at position 169: …{T2}{E1*P_{z}}}}̲ \\E1*P_{z}&={\…

现在有了 Pz,我们可以计算 Cw:

C w = E 1 ∗ P z C w = T 2 N z − T 1 E 1 {\begin{aligned}C_{w}&=E1*P_{z}\\C_{w}&={\tfrac {T2}{N_{z}-{\tfrac {T1}{E1}}}}\end{aligned}} CwCw=E1Pz=NzE1T1T2

因此,我们可以从中计算出 C 的其余部分:

C ⃗ x y z = N ⃗ ∗ C w C ⃗ x y z = N ⃗ ∗ ( T 2 N z − T 1 E 1 ) {\begin{aligned}{\vec C}_{{xyz}}&={\vec N}*C_{w}\\{\vec C}_{{xyz}}&={\vec N}*({\tfrac {T2}{N_{z}-{\tfrac {T1}{E1}}}})\end{aligned}} C xyzC xyz=N Cw=N NzE1T1T2

4.3 从剪辑到眼睛

计算完整的 4D 向量 C 后,我们可以像以前一样计算 P:
P ⃗ = M − 1 C ⃗ {\vec P}=M^{{-1}}{\vec C} P =M1C

4.4 GLSL示例

下面是一些 GLSL 示例代码,说明这将是什么样子的:

uniform mat4 persMatrix;
uniform mat4 invPersMatrix;
uniform vec4 viewport;
uniform vec2 depthrange;vec4 CalcEyeFromWindow(in vec3 windowSpace)
{vec3 ndcPos;ndcPos.xy = ((2.0 * windowSpace.xy) - (2.0 * viewport.xy)) / (viewport.zw) - 1;ndcPos.z = (2.0 * windowSpace.z - depthrange.x - depthrange.y) /(depthrange.y - depthrange.x);vec4 clipPos;clipPos.w = persMatrix[3][2] / (ndcPos.z - (persMatrix[2][2] / persMatrix[2][3]));clipPos.xyz = ndcPos * clipPos.w;return invPersMatrix * clipPos;
}

视口是包含视口参数的矢量。depthrange 是包含 glDepthRange 参数的 2D 向量。windowSpace 向量是 gl_FragCoord 的前两个分量,第三个坐标是从深度缓冲区读取的深度。

五、从gl_FragCoord的XYZ优化方法

前面的方法当然有用,但速度有点慢。我们可以通过使用顶点着色器来提供帮助,从而显着帮助计算眼距位置。这使我们能够完全避免使用逆透视矩阵。

此方法分为两步。我们首先计算 Pz,即眼空间 Z 坐标。然后用它来计算完整的眼距位置。

第一部分其实很简单。我们上面使用的大多数计算都是必要的,因为我们需要 Cw,我们必须这样做,因为我们需要一个完整的剪辑空间位置。这种优化的方法只需要得到 Pz,我们可以直接从 Wz、深度范围和投影矩阵的三个分量中计算出来:

N z = ( 2 ∗ W z ) − D f − D n D f − D n P z = T 2 E 1 ∗ N z − T 1 {\displaystyle {\begin{aligned}N_{z}&={\tfrac {(2*W_{z})-D_{f}-D_{n}}{D_{f}-D_{n}}}\\P_{z}&={\tfrac {T2}{E1*N_{z}-T1}}\end{aligned}}} NzPz=DfDn2WzDfDn=E1NzT1T2

请注意,这也意味着我们不需要片段着色器中的视口设置。我们只需要深度范围和透视矩阵项。

这种方法的诀窍如下:我们如何从 Pz 到完整的眼空间位置 P。要了解其工作原理,下面是一些快速的几何图形:
在这里插入图片描述

图中的 E 表示眼睛位置,这是眼睛空间的原点。P是我们想要的位置,Pz是我们拥有的位置。那么,我们需要什么才能从 Pz 中得到 P?我们所需要的只是一个指向 P 的向量方向,但 z 分量为 1.0。这样,我们只需将该向量乘以 Pz;结果必然是 P。

那么我们如何得到这个向量呢?

这就是顶点着色器的用武之地。在延迟渲染中,顶点着色器通常是一个简单的直通着色器,不执行实际计算,也不传递用户定义的输出。因此,我们可以自由地将其用于某些事情。

在顶点着色器中,我们简单地构造一个从原点到眼空间中每个顶点坐标的向量,即近平面上的相应位置。我们将向量的 Z 距离设置为 -1.0。这构建了一个矢量,该矢量指向眼睛空间中相机前方的场景。近平面的范围可以很容易地从对焦和纵横比中计算出来。

此值的线性插值将确保为片段计算的每个向量的 Z 值为 -1.0。线性插值也将保证它直接指向生成的片段。

我们本可以在片段着色器中计算出来,但何必呢?这需要向片段着色器提供视口转换(以便我们可以将 Wxy 转换为眼睛空间)。而且 VS 并没有做任何事情…

一旦我们有了这个值,我们只需将其乘以 Pz 即可得到我们的眼距位置 P。

下面是一些着色器代码。

// Vertex shader
// Half the size of the near plane {tan(fovy/2.0) * aspect, tan(fovy/2.0) }
uniform vec2 halfSizeNearPlane; layout (location=0) in vec2 clipPos;
// UV for the depth buffer/screen access.
// (0,0) in bottom left corner (1, 1) in top right corner
layout (location=1) in vec2 texCoord;out vec3 eyeDirection;
out vec2 uv;void main()
{uv = texCoord;eye_direction = vec3((2.0 * halfSizeNearPlane * texCoord) - halfSizeNearPlane , -1.0);gl_Position = vec4(clipPos, 0, 1);
}// Fragment shader
in vec3 eyeDirection;
in vec2 uv;uniform mat4 persMatrix;
uniform vec2 depthrange;uniform sampler2D depthTex;

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

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

相关文章

【Obsidian】视频笔记插件Media Extended的强大功能

我将开设一个专栏,介绍当下最好用的笔记软件Obsidian的使用经验和技巧。欢迎持续关注。 摘要:本文将首先向您介绍一款功能强大的笔记软件Obsidian,然后为您详细解析Obsidian的一款实用插件——Media Extended,帮助您更好地利用Obs…

如何在Mac 电脑上安装 Homebrew

1、打开终端应用程序 在终端中输入以下命令并回车: /usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)" 这个命令会自动下载并运行 Homebrew 的安装脚本。 系统可能会提示您输入管理员密码,请输入您的 Mac 登录…

易图讯科技三维电子沙盘系统

深圳易图讯科技有限公司(www.3dgis.top)创立于2013年,专注二三维地理信息、三维电子沙盘、电子地图、虚拟现实、大数据、物联网和人工智能技术研发,获得20多项软件著作权和软件检测报告,成功交付并实施了1000多个项目&…

十、Redis内存回收策略和机制

1、Redis的内存回收 在Redis中可以设置key的过期时间,以期可以让Redis回收内存,循环使用。在Redis中有4个命令可以设置Key的过期时间。分别为 expire、pexpire、expireat、pexpireat。 1.1、expire expire key ttl:将key的过期时间设置为tt…

QTreeView学习 branch 虚线设置

1、方法一&#xff1a; #include <QStyleFactory> ui.treeView->setStyle(QStyleFactory::create("windows")); 2、方法二&#xff1a; QString strtyle2 R"( QTreeView::branch:has-siblings:!adjoins-item { border-image: url(:/TreeViewDe…

【计算机毕业设计】用于日语词汇学习的微信小程SSM

日语词汇学习小程序是高校人才培养计划的重要 组成部分&#xff0c;是实现人才培养目标、培养学生科研能力与创新思维、检验学生综合素质与实践能力的重要手段与综合性实践教学环节。本学生所在学院多采用半手工管理日语词汇学习小程序的方式&#xff0c;所以有必要开发日语词汇…

Qt---窗口系统

一、QMainWindow QMainWindow是一个为用户提供主窗口程序的类&#xff0c;包含一个菜单栏&#xff08;menu bar&#xff09;、多个工具栏(tool bars)、多个锚接部件(dock widgets)、一个状态栏(status bar)及一个中心部件(central widget) 1. 菜单栏(最多有一个) QMenuBar *bar…

【Python基础】装饰器(3848字)

文章目录 [toc]闭包什么是装饰器装饰器示例不使用装饰器语法使用装饰器语法 装饰器传参带参数的装饰器类装饰器魔术方法\__call__()类装饰器示例带参数类装饰器property装饰器分页操作商品价格操作 个人主页&#xff1a;丷从心 系列专栏&#xff1a;Python基础 学习指南&…

Spring MVC(建立连接 + 请求)

文章目录 一、建立客户端和服务器的连接二、如何构造请求&#xff08;传参&#xff09;2.1 构造请求方式 参数通用注解2.2 传递单个参数2.3 传递多个参数2.4 传递数组/集合2.5 传递对象2.6 传递JSON 三、相关的其他请求操作3.1 获取URL中的参数 PathVariable3.2 上传文件 Requ…

maven找不到依赖,in offline mode

问题描述&#xff1a; [ERROR] Plugin org.jetbrains.kotlin:kotlin-maven-plugin:1.2.71 or one of its dependencies could not be resolved: Failed to read artifact descriptor for org.jetbrains.kotlin:kotlin-maven-plugin:jar:1.2.71: Cannot access aliyunmaven (ht…

未来娱乐新地标?气膜球幕影院的多维体验—轻空间

在中国&#xff0c;一座独特的娱乐场所正在崭露头角&#xff1a;气膜球幕影院。这个融合了气膜建筑与激光投影技术的创新场所&#xff0c;不仅令人惊叹&#xff0c;更带来了前所未有的科幻娱乐体验。让我们一起探索这个未来的娱乐空间&#xff0c;感受其中的多维魅力。 现场演出…

【刷题篇】滑动窗口(二)

文章目录 1、水果成篮2、找到字符串中所有字母异位词3、串联所有单词的子串4、最小覆盖子串 1、水果成篮 你正在探访一家农场&#xff0c;农场从左到右种植了一排果树。这些树用一个整数数组 fruits 表示&#xff0c;其中 fruits[i] 是第 i 棵树上的水果 种类 。 你想要尽可能多…

【Python技术】使用akshare、pandas打印合成生物概念股

前不久写过一篇文章【python技术】使用akshare抓取东方财富所有概念板块&#xff0c;并把指定板块概念的成分股保存excel 简单示例 &#xff0c;当初是用低空经济作为demo示例&#xff0c;得到概念股。 今天简单跑了下&#xff0c;提示 未找到板块名称合成生物&#xff0c;请检…

SM4-GCM Library代码示例

sm4-gcm加密解密测试代码: fn main() {let key Sm4Key([0u8; 16]);let nonce [0u8; 12];let plaintext b"Hello World!";let ciphertext sm4_gcm::sm4_gcm_encrypt(&key, &nonce, plaintext);println!("Encrypted: {}", hex::encode(&cip…

String,StringBuilder,StringBuffer

String&#xff0c;StringBuffer&#xff0c;StringBuilder String类 概念:String是不可变类&#xff0c;即一旦一个String对象被创建&#xff0c;包含在这个对象中的字符序列是不可改变的&#xff0c;直至该对象被销毁&#xff0c;并且String类是final类&#xff0c;不能有子…

Debian安装Redis、RabbitMQ、Nacos

安装Redis&#xff1a; 启动Redis、开机自启动 sudo systemctl start redis-server #启动sudo systemctl enable redis-server #开机自启 Redis状态(是否在运行) sudo systemctl status redis-server #查看运行状态 redis-cli ping # 客户端尝试连接 安装RabbitMQ&#xff0c;…

【JavaEE 初阶(五)】文件操作和IO

❣博主主页: 33的博客❣ ▶️文章专栏分类:JavaEE◀️ &#x1f69a;我的代码仓库: 33的代码仓库&#x1f69a; &#x1faf5;&#x1faf5;&#x1faf5;关注我带你了解更多文件操作 目录 1.前言2.认识文件3.文件操作3.1File 属性3.2构造方法3.3File类方法 4.文件内容操作4.1R…

「JavaEE」多线程案例1:单例模式阻塞队列

&#x1f387;个人主页&#xff1a;Ice_Sugar_7 &#x1f387;所属专栏&#xff1a;JavaEE &#x1f387;欢迎点赞收藏加关注哦&#xff01; 多线程案例分析 &#x1f349;单例模式&#x1f34c;饿汉模式&#x1f34c;懒汉模式&#x1f34c;指令重排序 &#x1f349;阻塞队列&a…

Matlab|考虑极端天气线路脆弱性的配电网分布式电源和储能优化配置模型

1主要内容 程序主要参考《考虑极端天气线路脆弱性的配电网分布式电源配置优化模型-马宇帆》&#xff0c;针对极端天气严重威胁配电网安全稳定运行的问题。基于微气象、微地形对配电网的线路脆弱性进行分析&#xff0c;然后进行分布式电源接入位置与极端天气的关联性分析&#…

易图讯三维电子沙盘-大数据处理服务

易图讯科技10名高级大数据工程师&#xff0c;高效、快速进行POI、DEM、高清卫星影像、地形地貌、路网、矢量地图等海量大数据处理服务。 免费专业提供POI、AOI、DEM、高清卫星影像、地形地貌、路网、矢量地图等海量大数据处理服务。 1年更新2次POI、高清卫星影像。