当前位置: 首页 > news >正文

Metal 学习笔记五:3D变换

在上一章中,您通过在 vertex 函数中计算position,来平移顶点和在屏幕上移动对象。但是,在 3D 空间中,您还想执行更多操作,例如旋转和缩放对象。您还需要一个场景内摄像机,以便您可以在场景中移动。

要移动、缩放和旋转三角形,您将使用矩阵 - 一旦您掌握了一个三角形,就可以一次旋转具有数千个三角形的模型!

对于我们这些不是数学天才的人来说,向量和矩阵可能有点可怕。幸运的是,在使用数学时,您不必总是需要知道引擎盖下的内容。为了提供帮助,本章的重点不是数学,而是矩阵。在学习本章的过程中,您将逐渐扩展您的线性代数知识,因为您将了解矩阵可以为您做什么以及如何操作它们。

变换

查看下面的图片:

使用矢量图像编辑器 Affinity Designer,您可以通过一系列仿射变换来缩放和旋转猫。Affinity Designer 不会单独计算每个位置,而是创建一个包含变换组合的变换矩阵。然后,它将变换应用于每个元素。

注: 仿射表示完成变换后,所有平行线都保持平行。

当然,没有人愿意移动、缩放和旋转猫,因为它们可能会咬人。因此,您将平移、缩放和旋转三角形。

开始项目和设置

➤ 打开并运行位于本章 starter 文件夹中的 starter 项目。

此项目渲染一个三角形两次,而不是渲染一个四边形。
在 Renderer 的 draw(in:) 中,您将看到两个 draw 调用(每个三角形一个)。渲染器将 position 传递给 vertex 函数,将 color 传递给 fragment 函数;它对每个三角形执行此操作。灰色三角形位于其原始位置,红色三角形应用变换。

➤ 在继续下一步之前,请确保您理解 Renderer 的 draw(in:) 中的代码和 Shaders.metal 中的 vertex 函数。

ContentView.swift 现在位于 SwiftUI 视图组中,它在Metal View上显示网格,以便您可以更轻松地可视化顶点位置。

平移

starter项目展示两个三角形:
• 没有任何变换的灰色三角形。
• 使用position = simd_float3(0.3, -0.4, 0) 平移的红色三角形。

在上一章的第一个挑战中,您计算了着色器函数中每个顶点的位置。更常见的计算机图形范例是,在顶点缓冲区中设置模型每个顶点的位置,通常从文件加载,然后将矩阵发送到顶点着色器,着色器包含模型当前位置、旋转和缩放。

顶点和矩阵

您可以更好地将position描述为位移矢量[0.3, -0.4, 0]。每个顶点从 x 方向上移动 0.3 个单位,在 y 方向上移动 -0.4 个单位。

在下图中,蓝色箭头是矢量。

左侧蓝色箭头是值为 [-1, 2] 的向量。右侧的蓝色箭头(靠近 cat 的箭头)也是值为 [-1, 2] 的向量。position(point)是空间中的位置,而向量是空间中的位移。换句话说,向量包含移动的数量和方向。如果你要用蓝色向量来移动猫,它最终会到达点 (2, 4)。这是猫的位置 (3, 2) 加上向量 [-1, 2]。

此 2D 向量是一个 1x2 矩阵。它有一列和两行。

注: 您可以按行或列对矩阵进行排序。simd 库按列优先顺序构造矩阵,这意味着列在内存中是连续的。simd_double2x4 是包含两列四行的矩阵。

矩阵是二维数组。即使是单个数字 1 也是一个 1×1 矩阵。实际上,数字1是独一无二的,因为用任何数字乘以1都是它本身。所有方阵(行数和列数相等的矩阵),都有一个矩阵具有和1的概念相等的矩阵——单位矩阵。任何向量或矩阵乘以单位矩阵,都是它本身。

4×4 单位矩阵如下所示(全是 0,对角线 1 除外):


3D 变换矩阵有 4 行和 4 列。变换矩阵左上角的 3×3 矩阵中包含缩放和旋转信息,平移信息在最后一列中。将向量和矩阵相乘时,左侧矩阵或向量的列数必须等于右侧的行数。例如,您不能将 float3 乘以 float4×4。

矩阵的魔力

当您将矩阵相乘时,您将它们合并为一个矩阵。然后,您可以将向量乘以此矩阵来变换向量。例如,您可以设置旋转矩阵和平移矩阵。然后,您可以使用以下代码计算转换后的位置:

   translationMatrix * rotationMatrix * positionVector

矩阵乘法从右向左进行。在这里,旋转应用于平移之前的位置。
这是线性代数的基础——如果你想继续学习计算机图形学,你需要更全面地理解线性代数。目前,了解设置转换矩阵的概念可以让你走很长的路。 

创建矩阵

➤ 打开 Renderer.swift,找到你在 draw(in:) 中渲染第一个灰色三角形的位置。
➤ 将position代码从:

var position = simd_float3(0, 0, 0)
renderEncoder.setVertexBytes(&position,length: MemoryLayout<SIMD3<Float>>.stride,index: 11)

改为:

var translation = matrix_float4x4()
translation.columns.0 = [1, 0, 0, 0]
translation.columns.1 = [0, 1, 0, 0]
translation.columns.2 = [0, 0, 1, 0]
translation.columns.3 = [0, 0, 0, 1]
var matrix = translation
renderEncoder.setVertexBytes(&matrix,length: MemoryLayout<matrix_float4x4>.stride,index: 11)

在这里,您将创建一个单位矩阵和一个要发送到 GPU 的渲染命令。

➤ 找到第二个红色三角形的position代码并更改: 

position = simd_float3(0.3, -0.4, 0)
renderEncoder.setVertexBytes(&position,length: MemoryLayout<SIMD3<Float>>.stride,index: 11)

为:

let position = simd_float3(0.3, -0.4, 0)
translation.columns.3.x = position.x
translation.columns.3.y = position.y
translation.columns.3.z = position.z
matrix = translation
renderEncoder.setVertexBytes(&matrix,length: MemoryLayout<matrix_float4x4>.stride,index: 11)

您将使用此矩阵来平移顶点着色器中的position。

➤ 打开 Shaders.metal,然后更改: 

constant float3 &position [[buffer(11)]])

为:

   constant float4x4 &matrix [[buffer(11)]])

您将接收传入着色器中的矩阵。

➤ 在 vertex 函数中,更改: 

   float3 translation = in.position.xyz + position;

为:

float3 translation = in.position.xyz + matrix.columns[3].xyz;

使用矩阵的第四列作为位移向量。

➤ 构建并运行。到目前为止,输出是相同的。 

请记住,此矩阵还将保存旋转和缩放信息。要计算position,您需要执行矩阵乘法,而不是添加平移位移向量。
➤ 将 vertex 函数的内容改为:

float4 translation = matrix * in.position;
VertexOut out {.position = translation
};
return out;

➤ 构建并运行应用程序,您会发现仍然没有变化。
现在,您可以在 Renderer 中向矩阵添加缩放和旋转,而无需每次更改着色器函数。

缩放

➤ 打开 Renderer.swift,然后在 draw(in:) 中找到第二个红色三角形中设置 matrix 的位置。
➤ 在 matrix = translation 之前,添加以下内容:

let scaleX: Float = 1.2
let scaleY: Float = 0.5
let scaleMatrix = float4x4([scaleX, 0,   0,   0],[0, scaleY,   0,   0],[0,      0,   1,   0],[0,      0,   0,   1])

您可以像这样初始化一个矩阵,将列定义为数组,而不是像在“平移”中所做的那样分配给列。scaleMatrix.columns.0 现在包含 [1.2, 0, 0, 0]。

无需过多地进行数学研究,就可以使用此代码来设置缩放矩阵。
➤ 将 matrix = translation更改为: matrix = scaleMatrix

将平移矩阵乘以缩放矩阵,而不是乘以平移矩阵。

➤ 构建并运行应用程序。

在 vertex 函数中,矩阵将三角形的每个顶点乘以 x 和 y 缩放因子。
➤ 将 matrix = scaleMatrix 更改为: matrix = translation * scaleMatrix

此代码平移缩放的三角形。

➤ 构建并运行应用程序。

旋转

执行旋转的方式与缩放类似。
➤ 将 matrix = translation * scaleMatrix,改为:

let angle = Float.pi / 2.0
let rotationMatrix = float4x4([cos(angle), -sin(angle), 0,    0],[sin(angle),  cos(angle), 0,    0],[0,           0,          1,    0],[0,           0,          0,    1])
matrix = rotationMatrix

在这里,您可以设置围绕 z 轴旋转的角度(以弧度为单位)。

注意:Float.pi / 2.0 与 90° 相同,均为 1.5708 弧度。弧度是计算机图形学中的标准单位。这是将度数转换为弧度的公式:度数 * pi / 180 = 弧度。

➤ 构建并运行,您将看到红色三角形的每个顶点如何围绕原点 [0, 0, 0] 旋转 90°。 

➤ 将 matrix = rotationMatrix 替换为:

matrix = translation * rotationMatrix * scaleMatrix

此代码首先缩放每个顶点,然后旋转,然后平移。

➤ 构建并运行。


矩阵运算的顺序很重要。尝试更改顺序,看看会发生什么。
缩放和旋转发生在原点 (坐标 [0, 0, 0])。但是,有时您可能希望围绕不同的点进行旋转。例如,当三角形处于其原始位置(即与灰色三角形相同的位置和旋转角度)时,让我们围绕三角形的最右边点旋转三角形。
要旋转三角形,您需要设置一个平移矩阵,其中向量位于原点和最右侧点之间,执行以下步骤:
1. 使用平移矩阵平移所有顶点。

2. 旋转。

3. 再次平移回来。

➤ 在对红色小三角设置矩阵之前,添加以下代码:

translation.columns.3.x = triangle.vertices[2].x
translation.columns.3.y = triangle.vertices[2].y
translation.columns.3.z = triangle.vertices[2].z

将变换矩阵设置为(从原点)移动到(灰色)三角形的第三个顶点,即最右侧的点。
记住这些步骤。第 1 步是按距原点的距离平移所有顶点。您可以通过将矩阵设置为顶点的向量值并使用平移矩阵的逆矩阵来实现此目的。

在执行以下每个步骤后,不要忘记构建并运行应用程序,以便您可以查看矩阵乘法的作用。
➤ 将matrix = translation * rotationMatrix * scaleMatrix 更改为: matrix = translation.inverse

此代码将最右侧的顶点放在原点处,以相同的距离平移所有其他顶点。

➤ 将您刚刚输入的代码更改为:

matrix = rotationMatrix * translation.inverse

 三角形绕原点旋转 90°。


➤ 将您刚刚输入的代码更改为:
matrix = translation * rotationMatrix * translation.inverse

匪夷所思!您正在执行按最右侧顶点与原点的距离平移每个顶点的所有步骤。之后,您将旋转每个顶点并再次将其平移回来,使三角形围绕其最右侧的点旋转。

参考

https://zhuanlan.zhihu.com/p/387152681

相关文章:

Metal 学习笔记五:3D变换

在上一章中&#xff0c;您通过在 vertex 函数中计算position&#xff0c;来平移顶点和在屏幕上移动对象。但是&#xff0c;在 3D 空间中&#xff0c;您还想执行更多操作&#xff0c;例如旋转和缩放对象。您还需要一个场景内摄像机&#xff0c;以便您可以在场景中移动。 要移动…...

unity学习56:旧版legacy和新版TMP文本输入框 InputField学习

目录 1 旧版文本输入框 legacy InputField 1.1 新建一个文本输入框 1.2 InputField 的子物体构成 1.3 input field的的component 1.4 input Field的属性 2 过渡 transition 3 控件导航 navigation 4 占位文本 placeholder 5 文本 text 5.1 文本内容&#xff0c;用户…...

32位,算Cache地址

32位&#xff0c;算Cache地址...

C++蓝桥杯基础篇(六)

片头 嗨~小伙伴们&#xff0c;大家好&#xff01;今天我们来一起学习蓝桥杯基础篇&#xff08;六&#xff09;&#xff0c;练习相关的数组习题&#xff0c;准备好了吗&#xff1f;咱们开始咯&#xff01; 第1题 数组的左方区域 这道题&#xff0c;实质上是找规律&#xff0c;…...

React 常见面试题及答案

记录面试过程 常见问题&#xff0c;如有错误&#xff0c;欢迎批评指正 1. 什么是虚拟DOM&#xff1f;为什么它提高了性能&#xff1f; 虚拟DOM是React创建的一个轻量级JavaScript对象&#xff0c;表示真实DOM的结构。当状态变化时&#xff0c;React会生成新的虚拟DOM&#xf…...

和鲸科技推出人工智能通识课程解决方案,助力AI人才培养

2025年2月&#xff0c;教育部副部长吴岩应港澳特区政府邀请&#xff0c;率团赴港澳宣讲《教育强国建设规划纲要 (2024—2035 年)》。在港澳期间&#xff0c;吴岩阐释了教育强国目标的任务&#xff0c;并与特区政府官员交流推进人工智能人才培养的办法。这一系列行动体现出人工智…...

免费使用 DeepSeek API 教程及资源汇总

免费使用 DeepSeek API 教程及资源汇总 一、DeepSeek API 资源汇总1.1 火山引擎1.2 百度千帆1.3 阿里百炼1.4 腾讯云 二、其他平台2.1 华为云2.2 硅基流动 三、总结 DeepSeek-R1 作为 2025 年初发布的推理大模型&#xff0c;凭借其卓越的逻辑推理能力和成本优势&#xff0c;迅速…...

网络安全-使用DeepSeek来获取sqlmap的攻击payload

文章目录 概述DeepSeek使用创建示例数据库创建API测试sqlmap部分日志参考 概述 今天来使用DeepSeek做安全测试&#xff0c;看看在有思路的情况下实现的快不快。 DeepSeek使用 我有一个思路&#xff0c;想要测试sqlmap工具如何dump数据库的&#xff1a; 连接mysql数据库&#…...

网络原理--TCP/IP(2)

我们在之前已经介绍到TCP协议的核心机制二,接下来我们将继续介绍其他的核心机制。 核心机制三:连接管理 即建立连接,断开连接,在正常情况下,TCP要经过三次握⼿建⽴连接,四次挥⼿断开连接。 建立连接:TCP是通过“三次握手” 在生活中的握手就是打招呼,,但握手操作没有…...

Ragflow与Dify之我见:AI应用开发领域的开源框架对比分析

本文详细介绍了两个在AI应用开发领域备受关注的开源框架&#xff1a;Ragflow和Dify。Ragflow专注于构建基于检索增强生成&#xff08;RAG&#xff09;的工作流&#xff0c;强调模块化和轻量化&#xff0c;适合处理复杂文档格式和需要高精度检索的场景。Dify则旨在降低大型语言模…...

文件上传漏洞绕过WAF

文件上传漏洞绕过WAF学习笔记 1. WAF检测原理 WAF&#xff08;Web应用防火墙&#xff09;通过以下方式拦截文件上传攻击&#xff1a; 关键字匹配&#xff1a;检测文件名、内容中的敏感词&#xff08;如<?php、eval&#xff09;。 扩展名黑名单&#xff1a;拦截.php、.jsp…...

[含文档+PPT+源码等]精品基于Python实现的vue3+Django计算机课程资源平台

基于Python实现的Vue3Django计算机课程资源平台的背景&#xff0c;可以从以下几个方面进行阐述&#xff1a; 一、教育行业发展背景 1. 教育资源数字化趋势 随着信息技术的快速发展&#xff0c;教育资源的数字化已成为不可逆转的趋势。计算机课程资源作为教育领域的重要组成部…...

Qt 开源音视频框架模块之QtAV播放器实践

Qt 开源音视频框架模块QtAV播放器实践 1 摘要 QtAV是一个基于Qt的多媒体框架&#xff0c;旨在简化音视频播放和处理。它是一个跨平台的库&#xff0c;支持多种音视频格式&#xff0c;并提供了一个简单易用的API来集成音视频功能。QtAV的设计目标是为Qt应用程序提供强大的音视…...

【前端】XML,XPATH,与HTML的关系

XML与HTML关系 XML&#xff08;可扩展标记语言&#xff09;和 HTML&#xff08;超文本标记语言&#xff09;是两种常见的标记语言&#xff0c;但它们有不同的目的和用途。它们都使用类似的标记结构&#xff08;标签&#xff09;&#xff0c;但在设计上存在一些关键的差异。 XML…...

ubuntu服务器安装VASP.6.4.3

ubuntu服务器安装VASP.6.4.3 1 安装Intel OneAPI Base Toolkit和Intel OneAPI HPC Toolkit1.1 更新并安装环境变量1.2 下载Intel OneAPI Base Toolkit和Intel OneAPI HPC Toolkit安装包1.3 安装 Intel OneAPI Base Toolkit1.4 安装 Intel OneAPI HPC Toolkit1.5 添加并激活环境…...

市场加速下跌,但监管「坚冰」正在消融

作者&#xff1a;Techub 热点速递 撰文&#xff1a;Yangz&#xff0c;Techub News 与近日气温逐步回暖不同&#xff0c;自 2 月 25 日比特币跌破 9 万美元以来&#xff0c;加密货币市场行情一路下滑。今日 10 时 50 分左右&#xff0c;比特币更是跌破 8 万美元大关&#xff0c…...

7.2 - 定时器之计算脉冲宽度实验

文章目录 1 实验任务2 系统框图3 软件设计 1 实验任务 本实验任务是通过CPU私有定时器来计算按键按下的时间长短。 2 系统框图 参见7.1。 3 软件设计 注意事项&#xff1a; 定时器是递减计数的&#xff0c;需要考虑StartCount&#xff1c;EndCount的情况。 /***********…...

Imagination DXTP GPU IP:加速游戏AI应用,全天候畅玩无阻

日前&#xff0c;Imagination 推出了最新产品——Imagination DXTP GPU IP&#xff0c;在智能手机和其他功耗受限设备上加速图形和AI工作负载时&#xff0c;保证全天候的电池续航。它是我们最新D系列GPU的最终产品&#xff0c;集成了自2022年发布以来引入的一系列功能&#xff…...

关于流水线的理解

还是不太理解&#xff0c;我之前一直以为&#xff0c;对axis总线&#xff0c;每一级的寄存器就像fifo一样&#xff0c;一级一级的分级存储最后一级需要的数据。&#xff08;现在看来&#xff0c;我这个理解应该也是没有问题的&#xff09; 如下图&#xff0c;一开始是在解析axi…...

采样算法二:去噪扩散隐式模型(DDIM)采样算法详解教程

参考 https://arxiv.org/pdf/2010.02502 一、背景与动机 去噪扩散隐式模型&#xff08;DDIM&#xff09; 是对DDPM的改进&#xff0c;旨在加速采样过程同时保持生成质量。DDPM虽然生成效果优异&#xff0c;但其采样需迭代数百至数千次&#xff0c;效率较低。DDIM通过以下关键…...

地震勘探——干扰波识别、井中地震时距曲线特点

目录 干扰波识别反射波地震勘探的干扰波 井中地震时距曲线特点 干扰波识别 有效波&#xff1a;可以用来解决所提出的地质任务的波&#xff1b;干扰波&#xff1a;所有妨碍辨认、追踪有效波的其他波。 地震勘探中&#xff0c;有效波和干扰波是相对的。例如&#xff0c;在反射波…...

iOS 26 携众系统重磅更新,但“苹果智能”仍与国行无缘

美国西海岸的夏天&#xff0c;再次被苹果点燃。一年一度的全球开发者大会 WWDC25 如期而至&#xff0c;这不仅是开发者的盛宴&#xff0c;更是全球数亿苹果用户翘首以盼的科技春晚。今年&#xff0c;苹果依旧为我们带来了全家桶式的系统更新&#xff0c;包括 iOS 26、iPadOS 26…...

1.3 VSCode安装与环境配置

进入网址Visual Studio Code - Code Editing. Redefined下载.deb文件&#xff0c;然后打开终端&#xff0c;进入下载文件夹&#xff0c;键入命令 sudo dpkg -i code_1.100.3-1748872405_amd64.deb 在终端键入命令code即启动vscode 需要安装插件列表 1.Chinese简化 2.ros …...

DIY|Mac 搭建 ESP-IDF 开发环境及编译小智 AI

前一阵子在百度 AI 开发者大会上&#xff0c;看到基于小智 AI DIY 玩具的演示&#xff0c;感觉有点意思&#xff0c;想着自己也来试试。 如果只是想烧录现成的固件&#xff0c;乐鑫官方除了提供了 Windows 版本的 Flash 下载工具 之外&#xff0c;还提供了基于网页版的 ESP LA…...

04-初识css

一、css样式引入 1.1.内部样式 <div style"width: 100px;"></div>1.2.外部样式 1.2.1.外部样式1 <style>.aa {width: 100px;} </style> <div class"aa"></div>1.2.2.外部样式2 <!-- rel内表面引入的是style样…...

【碎碎念】宝可梦 Mesh GO : 基于MESH网络的口袋妖怪 宝可梦GO游戏自组网系统

目录 游戏说明《宝可梦 Mesh GO》 —— 局域宝可梦探索Pokmon GO 类游戏核心理念应用场景Mesh 特性 宝可梦玩法融合设计游戏构想要素1. 地图探索&#xff08;基于物理空间 广播范围&#xff09;2. 野生宝可梦生成与广播3. 对战系统4. 道具与通信5. 延伸玩法 安全性设计 技术选…...

Linux --进程控制

本文从以下五个方面来初步认识进程控制&#xff1a; 目录 进程创建 进程终止 进程等待 进程替换 模拟实现一个微型shell 进程创建 在Linux系统中我们可以在一个进程使用系统调用fork()来创建子进程&#xff0c;创建出来的进程就是子进程&#xff0c;原来的进程为父进程。…...

【电力电子】基于STM32F103C8T6单片机双极性SPWM逆变(硬件篇)

本项目是基于 STM32F103C8T6 微控制器的 SPWM(正弦脉宽调制)电源模块,能够生成可调频率和幅值的正弦波交流电源输出。该项目适用于逆变器、UPS电源、变频器等应用场景。 供电电源 输入电压采集 上图为本设计的电源电路,图中 D1 为二极管, 其目的是防止正负极电源反接, …...

C++ 设计模式 《小明的奶茶加料风波》

&#x1f468;‍&#x1f393; 模式名称&#xff1a;装饰器模式&#xff08;Decorator Pattern&#xff09; &#x1f466; 小明最近上线了校园奶茶配送功能&#xff0c;业务火爆&#xff0c;大家都在加料&#xff1a; 有的同学要加波霸 &#x1f7e4;&#xff0c;有的要加椰果…...

MyBatis中关于缓存的理解

MyBatis缓存 MyBatis系统当中默认定义两级缓存&#xff1a;一级缓存、二级缓存 默认情况下&#xff0c;只有一级缓存开启&#xff08;sqlSession级别的缓存&#xff09;二级缓存需要手动开启配置&#xff0c;需要局域namespace级别的缓存 一级缓存&#xff08;本地缓存&#…...