本文主要是介绍D3D12渲染技术之矩阵向量运算,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
向量,矩阵运算这个是老生常谈的话题了,作为程序员来说必须要掌握的,游戏开发逻辑编写中用的最多的也是矩阵向量相关的运算,向量矩阵运算它也是引擎的最基础部分,几乎每个引擎都会封装自己的向量运算类,矩阵运算类,Unity引擎它也封装了自己的矩阵向量类,虚幻也是一样的。学习D3D12,它们也是绕不过去的,大学的课本《线性代数》主讲的就是它们,如果大家没有学习过或者是跨行业的,可以学习一下。如果大家已经掌握了线性代数中的向量矩阵运算,接下来我们就要把向量矩阵运算转换成编程语言,将在D3D12中展示,先看向量的运算封装。
向量运算封装
向量有点乘,叉乘,加,减,标准化等等操作,在D3D12中提供的文件DirectXMath.h为我们提供了向量的定义为:XMVECTOR,所以我们要查看一下该文件都提供了哪些内容,这个跟我们使用引擎有点类似,先看看有没有相关的接口,如果没有再自己封装。
我们通过DirectXMath看到了如下代码:
#if defined(_XM_SSE_INTRINSICS_) && !defined(_XM_NO_INTRINSICS_)
typedef __m128 XMVECTOR;
#elif defined(_XM_ARM_NEON_INTRINSICS_) && !defined(_XM_NO_INTRINSICS_)
typedef float32x4_t XMVECTOR;
#else
typedef __vector4 XMVECTOR;
#endif
D3D12已经为我们定义了向量,该向量是四维的,是四个32位浮点分量在16字节上对齐,考虑了内存占用,代码很严谨。每个接下来做的事情就是赋值,然后我们做运算,看看有没有给向量的赋值函数,我们继续在D3D12中查找,在文件DirectXMathVector.inl中找到了给向量赋值的内联函数:
inline XMVECTOR XM_CALLCONV XMVectorSet
(float x, float y, float z, float w
)
{
#if defined(_XM_NO_INTRINSICS_)XMVECTORF32 vResult = { { { x, y, z, w } } };return vResult.v;
#elif defined(_XM_ARM_NEON_INTRINSICS_)float32x2_t V0 = vcreate_f32(((uint64_t)*(const uint32_t *)&x) | ((uint64_t)(*(const uint32_t *)&y) << 32));float32x2_t V1 = vcreate_f32(((uint64_t)*(const uint32_t *)&z) | ((uint64_t)(*(const uint32_t *)&w) << 32));return vcombine_f32(V0, V1);
#elif defined(_XM_SSE_INTRINSICS_)return _mm_set_ps( w, z, y, x );
#endif
}
我们可以自己调用接口XMVectorSet对向量进行赋值操作,比如下面代码所示:
XMVECTOR n = XMVectorSet(1.0f, 0.0f, 0.0f, 0.0f);XMVECTOR u = XMVectorSet(1.0f, 2.0f, 3.0f, 0.0f);XMVECTOR v = XMVectorSet(-2.0f, 1.0f, -3.0f, 0.0f);XMVECTOR w = XMVectorSet(0.707f, 0.707f, 0.0f, 0.0f);
下一步就是运算符重载函数了,D3D12应该已经为我们定义好了,这个还是在DirectXMath.h
文件中,代码如下所示:
#ifndef _XM_NO_XMVECTOR_OVERLOADS_
XMVECTOR XM_CALLCONV operator+ (FXMVECTOR V);
XMVECTOR XM_CALLCONV operator- (FXMVECTOR V);XMVECTOR& XM_CALLCONV operator+= (XMVECTOR& V1, FXMVECTOR V2);
XMVECTOR& XM_CALLCONV operator-= (XMVECTOR& V1, FXMVECTOR V2);
XMVECTOR& XM_CALLCONV operator*= (XMVECTOR& V1, FXMVECTOR V2);
XMVECTOR& XM_CALLCONV operator/= (XMVECTOR& V1, FXMVECTOR V2);XMVECTOR& operator*= (XMVECTOR& V, float S);
XMVECTOR& operator/= (XMVECTOR& V, float S);XMVECTOR XM_CALLCONV operator+ (FXMVECTOR V1, FXMVECTOR V2);
XMVECTOR XM_CALLCONV operator- (FXMVECTOR V1, FXMVECTOR V2);
XMVECTOR XM_CALLCONV operator* (FXMVECTOR V1, FXMVECTOR V2);
XMVECTOR XM_CALLCONV operator/ (FXMVECTOR V1, FXMVECTOR V2);
XMVECTOR XM_CALLCONV operator* (FXMVECTOR V, float S);
XMVECTOR XM_CALLCONV operator* (float S, FXMVECTOR V);
XMVECTOR XM_CALLCONV operator/ (FXMVECTOR V, float S);
#endif /* !_XM_NO_XMVECTOR_OVERLOADS_ */
最后就比较简单了,我们把向量的运算一并写出来,包括向量的角度,向量加减,点乘,叉乘等运算,代码如下所示:
// Vector addition: XMVECTOR operator + XMVECTOR a = u + v;// Vector subtraction: XMVECTOR operator - XMVECTOR b = u - v;// Scalar multiplication: XMVECTOR operator * XMVECTOR c = 10.0f*u;// ||u||XMVECTOR L = XMVector3Length(u);// d = u / ||u||XMVECTOR d = XMVector3Normalize(u);// s = u dot vXMVECTOR s = XMVector3Dot(u, v);// e = u x vXMVECTOR e = XMVector3Cross(u, v);// Find proj_n(w) and perp_n(w)XMVECTOR projW;XMVECTOR perpW;XMVector3ComponentsFromNormal(&projW, &perpW, w, n);// Does projW + perpW == w?bool equal = XMVector3Equal(projW + perpW, w) != 0;bool notEqual = XMVector3NotEqual(projW + perpW, w) != 0;// The angle between projW and perpW should be 90 degrees.XMVECTOR angleVec = XMVector3AngleBetweenVectors(projW, perpW);float angleRadians = XMVectorGetX(angleVec);float angleDegrees = XMConvertToDegrees(angleRadians);
最终实现的效果截图:
矩阵运算
矩阵有运算有矩阵相加,相减,相乘,单位矩阵,转置矩阵等等。矩阵的定义以及函数在DirectXMath.h文件中都有定义,给读者展示如下所示:
#if ( defined(_M_ARM64) || defined(_M_HYBRID_X86_ARM64) || _XM_VECTORCALL_ ) && !defined(_XM_NO_INTRINSICS_)
typedef const XMMATRIX FXMMATRIX;
#else
typedef const XMMATRIX& FXMMATRIX;
#endif// Fix-up for (2nd+) XMMATRIX parameters to pass by reference
typedef const XMMATRIX& CXMMATRIX;#ifdef _XM_NO_INTRINSICS_
struct XMMATRIX
#else
__declspec(align(16)) struct XMMATRIX
#endif
{
#ifdef _XM_NO_INTRINSICS_union{XMVECTOR r[4];struct{float _11, _12, _13, _14;float _21, _22, _23, _24;float _31, _32, _33, _34;float _41, _42, _43, _44;};float m[4][4];};
#elseXMVECTOR r[4];
#endifXMMATRIX() XM_CTOR_DEFAULT
#if defined(_MSC_VER) && _MSC_VER >= 1900constexpr XMMATRIX(FXMVECTOR R0, FXMVECTOR R1, FXMVECTOR R2, CXMVECTOR R3) : r{ R0,R1,R2,R3 } {}
#elseXMMATRIX(FXMVECTOR R0, FXMVECTOR R1, FXMVECTOR R2, CXMVECTOR R3) { r[0] = R0; r[1] = R1; r[2] = R2; r[3] = R3; }
#endifXMMATRIX(float m00, float m01, float m02, float m03,float m10, float m11, float m12, float m13,float m20, float m21, float m22, float m23,float m30, float m31, float m32, float m33);explicit XMMATRIX(_In_reads_(16) const float *pArray);#ifdef _XM_NO_INTRINSICS_float operator() (size_t Row, size_t Column) const { return m[Row][Column]; }float& operator() (size_t Row, size_t Column) { return m[Row][Column]; }
#endifXMMATRIX& operator= (const XMMATRIX& M) { r[0] = M.r[0]; r[1] = M.r[1]; r[2] = M.r[2]; r[3] = M.r[3]; return *this; }XMMATRIX operator+ () const { return *this; }XMMATRIX operator- () const;XMMATRIX& XM_CALLCONV operator+= (FXMMATRIX M);XMMATRIX& XM_CALLCONV operator-= (FXMMATRIX M);XMMATRIX& XM_CALLCONV operator*= (FXMMATRIX M);XMMATRIX& operator*= (float S);XMMATRIX& operator/= (float S);XMMATRIX XM_CALLCONV operator+ (FXMMATRIX M) const;XMMATRIX XM_CALLCONV operator- (FXMMATRIX M) const;XMMATRIX XM_CALLCONV operator* (FXMMATRIX M) const;XMMATRIX operator* (float S) const;XMMATRIX operator/ (float S) const;friend XMMATRIX XM_CALLCONV operator* (float S, FXMMATRIX M);
};
同样,在文件DirectXMathVector.inl中也定义了矩阵的单位化,转置等操作函数:
inline XMMATRIX XM_CALLCONV XMMatrixIdentity()
{XMMATRIX M;M.r[0] = g_XMIdentityR0.v;M.r[1] = g_XMIdentityR1.v;M.r[2] = g_XMIdentityR2.v;M.r[3] = g_XMIdentityR3.v;return M;
}
在此就不一一列举了,我们用代码实现矩阵运算的示例如下所示:
XMMATRIX A(1.0f, 0.0f, 0.0f, 0.0f,0.0f, 2.0f, 0.0f, 0.0f,0.0f, 0.0f, 4.0f, 0.0f,1.0f, 2.0f, 3.0f, 1.0f);XMMATRIX B = XMMatrixIdentity();XMMATRIX C = A * B;XMMATRIX D = XMMatrixTranspose(A);XMVECTOR det = XMMatrixDeterminant(A);XMMATRIX E = XMMatrixInverse(&det, A);XMMATRIX F = A * E;
代码运行效果如下所示:
小结:
编程实现矩阵,向量运算没有那么复杂,但是我们要考虑到问题很多,就像D3D12定义的向量运算一样,我们还要考虑到位对齐,内存的优化,主要是告诉读者,D3D12是如何封装接口的,参考D3D12的我们也可以自己封装。引擎的封装跟这个类似。
代码下载地址:链接:https://pan.baidu.com/s/1X0Vikf6qGYGPKU-Nwf-wYA 密码:h79q
这篇关于D3D12渲染技术之矩阵向量运算的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!