为什么要将左手和右手矩阵的入口点分开而不是用手性标志或转换功能?

时间:2016-07-06 01:53:43

标签: c++ math graphics directx directx-11

用于矩阵计算的DirectX Math API包含用于生成左手与右手矩阵的单独函数(例如XMMatrixLookAtLHXMMatrixLootAtRH并排XMMatrixPerspectiveLH与{{1} }})。

我并不完全理解两个坐标系之间的完全差异(特别是它们适用于传统的DirectX和OpenGL),但为什么API结构如此,而不是结合入口点和提供,比如说,XMMatrixPerspectiveRH表示手性或额外的通用功能,将用于右手系统的矩阵转换为左手系统(反之亦然)?难道只是两个操作都需要快速(即你可以提供这些选项,但它们对于任何实际目的来说太慢而不值得支持)或者是否存在需要LH和RH变化的这些矩阵函数的基础是完全独立的端点?

编辑:澄清一下:虽然我非常感谢解释为什么做出API设计决策的答案,但我的主要好奇心是参数化或事后转换函数是否可以< / em>在考虑数学和实现时正确或有效地实现(即如果两半不能真正共享代码,那将是低效的。)

2 个答案:

答案 0 :(得分:3)

DirectXMath项目历史悠久。我在2008年开始研究它,当时它是&#34; xboxmath&#34; Xbox 360专注于VMX128,没有SSE / SSE2优化。从那时起,我已经保留了大部分初始API表面区域,因为我已经尝试维持对现有客户端的支持,因为我从xboxmath转移到xnamath然后xnamath转移到DirectXMath,包括这个&#34; LH&#34; vs.&#34; RH&#34;作为两个不同的功能。

这种设计有一个实际的原因:单个应用程序只会使用其中一个,而不是两个。对参数进行潜在的运行时检查以选择固定和已知的东西并不是那么有用。

  

另一个实际原因是最小化代码中的分支。大多数DirectXMath函数都是直线代码,避免了所有分支,而是使用元素选择。最初的原因是Xbox 360是一个非常快速的有序处理器,但没有特别先进的分支预测器。

一般来说,观看坐标系的选择是一个历史性的舒适问题:OpenGL长期以来都是首选的专业右手观察系统。 DirectX历来使用行主左手观察坐标。 XNA游戏工作室选择使用行主右侧查看坐标。

使用现代可编程GPU管道,只要您保持一致,实际上就不需要使用其中一个:DirectX API可以支持LH或RH。大多数DirectX样本(包括使用DXUT编写的任何内容)都使用左手查看坐标。大多数Windows应用商店示例和基于.NET的系统通常都遵循XNA Game Studio惯例。

由于这一切,DirectXMath支持行主矩阵,并让开发人员使用Right-Handed vs. Left-Handed。 DirectXMath的SimpleMath包装对于那些来自C#的XNA Game Studio数学库的人来说是很自然的,所以它采用右手。

回应@ galpo1n:&#34; DirectXMath现在也很古老,不是C ++的一个很好的例子,它对它的作用是好的,但是大多数项目都会重写它们的数学库。&#34; < / p>

xboxmath的传统是使用C可调用函数,因为在Xbox 360的早期,仍然有很多开发人员更喜欢C over C ++。随着时间的推移,随着C ++编译器的成熟和开发人员的口味发生变化,这一点变得越来越不重要了。在从XNAMath到DirectXMath的过渡中,我只创建了C ++库(即没有C),并利用了stdint.h,C ++命名空间之类的东西,并且我利用模板和专门化来改进permutes和shuffle的实现SSE / SSE2指令集的操作。

DirectXMath的C ++语言使用也跟踪了Visual C ++编译器支持。 DirectXMath 3.08使用=default,即将发布的3.09版本使用constexpr。在它的核心,它仍然是一个基本的C接口设计。真正想到它的最好方法是每个DirectXMath函数都是一个元内在函数&#39;。它们都是内联的,你真的希望编译器将它们中的一堆拼接成一个代码路径以获得最大效率。虽然最近的编译器在优化C ++代码模式方面做得更好,但即使是旧编译器(想想Visual C ++ .NET 2002时代)也能很好地使用C代码。

  

原始API实现了VXM128和&#34; no-intrinsics&#34;。 XNAMath实现了VMX128,no-intrinsics和SSE / SSE2 for x86&amp; 64。 DirectXMath不再支持VXM128,但添加了ARM-NEON和我自从为SSE3,SSE4.1,AVX和AVX2添加了可选的代码路径。因此,这种C风格的API已经证明可以很好地映射到各种处理器系列的SIMD内在操作。

SimpleMath是我决定将C ++类型转换行为隐藏在加载和放大器周围的详细程度的地方。存储数据。这样效率较低,因为程序员可能没有意识到他们实际上正在做一些昂贵的事情,这就是我将它保留在基础库之外的原因。如果您避免使用SimpleMath并坚持使用DirectXMath,那么您将编写更详细的代码,但作为回报,您知道什么时候您正在做一些可能会影响C ++隐式转换和构造函数的内容,您最终可能会溢出到内存中当你没有期待的时候。如果我把它放在基础库中,性能敏感的程序员就不能轻易选择退出。这完全取决于权衡。

更新:如果你真的需要参数化版本,你可以做一些简单的事情,让编译器负责优化它:

inline XMMATRIX XM_CALLCONV XMMatrixPerspectiveFov(float FovAngleY, float AspectRatio, float NearZ, float FarZ, bool rhcoords )
{
    if (rhcoords)
    {
        return XMMatrixPerspectiveFovRH(FovAngleY, AspectRatio, NearZ, FarZ);
    }
    else
    {
        return XMMatrixPerspectiveFovLH(FovAngleY, AspectRatio, NearZ, FarZ);
    }
}

库是全内联的,所以你基本上都有源代码。每种情况都略有不同,因此您可以单独优化每个参数化版本。有些函数有完整的代码路径扩展,有些只有一个。在大多数情况下,它只是归结为Z.

答案 1 :(得分:0)

这是基于设计API的意见。在引擎中,基于轻便性计算具有有效差异的矩阵所花费的时间可以忽略不计。他们可以使用一组标志来驱动而不是名称和代码重复,而没有任何真正的重大问题。

DirectXMath现在也很古老,不是C ++的一个很好的例子,它对它的作用已经足够好了,但是大多数项目都会重写它们的数学库。

使用现代GPU和着色器,只要您的管道是一致的,或者在需要时执行转换,从建模工具到渲染引擎,便捷性是一种纯粹的时尚选择。值得注意的是,除了便利之外,您还经常需要处理Y或Z惯例。

理解轻便的简单方法是用手指形成一个框架(拇指是X,索引是Y,中间是Z)。如果你用双手做这个并尝试对齐两个手指,差异很明显,第三个轴是倒置的。这就是全部:))