DirectX11筆記10:NDC空間與轉換


在書中的5.6章節有對於坐標變換的內容,里面涉及了NDC空間。

開始閱讀的時候不是特別明白,在開始學習拾取的時候,對坐標變換有了一個新的認識。

首先看一個老朋友:

inline XMMATRIX XM_CALLCONV XMMatrixPerspectiveFovLH
(
    float FovAngleY, float AspectHByW, float NearZ, float FarZ )

XMMatrixPerspectiveFovLH這個函數生成了一個從    局部坐標————》NDC空間坐標   的變幻矩陣

(博客里直接添加矩陣好不方便啊。。。。)

我們可以看一下它的實現原理:

不用全部看完,只需要去看XM_NO_INTRINSICS部分,這個部分最好理解!

inline XMMATRIX XM_CALLCONV XMMatrixPerspectiveFovLH
(
    float FovAngleY, float AspectHByW, float NearZ, float FarZ ) { assert(!XMScalarNearEqual(FovAngleY, 0.0f, 0.00001f * 2.0f)); assert(!XMScalarNearEqual(AspectHByW, 0.0f, 0.00001f)); assert(!XMScalarNearEqual(FarZ, NearZ, 0.00001f)); #if defined(_XM_NO_INTRINSICS_) float SinFov; float CosFov; XMScalarSinCos(&SinFov, &CosFov, 0.5f * FovAngleY); float Height = CosFov / SinFov; float Width = Height / AspectHByW; float fRange = FarZ / (FarZ-NearZ); XMMATRIX M; M.m[0][0] = Width; M.m[0][1] = 0.0f; M.m[0][2] = 0.0f; M.m[0][3] = 0.0f; M.m[1][0] = 0.0f; M.m[1][1] = Height; M.m[1][2] = 0.0f; M.m[1][3] = 0.0f; M.m[2][0] = 0.0f; M.m[2][1] = 0.0f; M.m[2][2] = fRange; M.m[2][3] = 1.0f; M.m[3][0] = 0.0f; M.m[3][1] = 0.0f; M.m[3][2] = -fRange * NearZ; M.m[3][3] = 0.0f; return M; #elif defined(_XM_ARM_NEON_INTRINSICS_) float SinFov; float CosFov; XMScalarSinCos(&SinFov, &CosFov, 0.5f * FovAngleY); float fRange = FarZ / (FarZ-NearZ); float Height = CosFov / SinFov; float Width = Height / AspectHByW; const XMVECTOR Zero = vdupq_n_f32(0); XMMATRIX M; M.r[0] = vsetq_lane_f32( Width, Zero, 0 ); M.r[1] = vsetq_lane_f32( Height, Zero, 1 ); M.r[2] = vsetq_lane_f32( fRange, g_XMIdentityR3.v, 2 ); M.r[3] = vsetq_lane_f32( -fRange * NearZ, Zero, 2 ); return M; #elif defined(_XM_SSE_INTRINSICS_) float SinFov; float CosFov; XMScalarSinCos(&SinFov, &CosFov, 0.5f * FovAngleY); float fRange = FarZ / (FarZ-NearZ); // Note: This is recorded on the stack float Height = CosFov / SinFov; XMVECTOR rMem = { Height / AspectHByW, Height, fRange, -fRange * NearZ }; // Copy from memory to SSE register XMVECTOR vValues = rMem; XMVECTOR vTemp = _mm_setzero_ps(); // Copy x only vTemp = _mm_move_ss(vTemp,vValues); // CosFov / SinFov,0,0,0  XMMATRIX M; M.r[0] = vTemp; // 0,Height / AspectHByW,0,0 vTemp = vValues; vTemp = _mm_and_ps(vTemp,g_XMMaskY); M.r[1] = vTemp; // x=fRange,y=-fRange * NearZ,0,1.0f vTemp = _mm_setzero_ps(); vValues = _mm_shuffle_ps(vValues,g_XMIdentityR3,_MM_SHUFFLE(3,2,3,2)); // 0,0,fRange,1.0f vTemp = _mm_shuffle_ps(vTemp,vValues,_MM_SHUFFLE(3,0,0,0)); M.r[2] = vTemp; // 0,0,-fRange * NearZ,0.0f vTemp = _mm_shuffle_ps(vTemp,vValues,_MM_SHUFFLE(2,1,0,0)); M.r[3] = vTemp; return M; #else // _XM_VMX128_INTRINSICS_ #endif // _XM_VMX128_INTRINSICS_ }

 

 

關於函數里面的預處理指令時判斷是否支持SSE浮點指令集 ARM指令集的,以此來優化性能。

當然,這里有一個奇怪的地方:TM的SSE的注釋寫錯了吧!!!

 

最主要的: 

    float    SinFov;
    float    CosFov;
    XMScalarSinCos(&SinFov, &CosFov, 0.5f * FovAngleY);

    float Height = CosFov / SinFov;
    float Width = Height / AspectHByW;
    float fRange = FarZ / (FarZ-NearZ);

    XMMATRIX M;
    M.m[0][0] = Width;
    M.m[0][1] = 0.0f;
    M.m[0][2] = 0.0f;
    M.m[0][3] = 0.0f;

    M.m[1][0] = 0.0f;
    M.m[1][1] = Height;
    M.m[1][2] = 0.0f;
    M.m[1][3] = 0.0f;

    M.m[2][0] = 0.0f;
    M.m[2][1] = 0.0f;
    M.m[2][2] = fRange;
    M.m[2][3] = 1.0f;

    M.m[3][0] = 0.0f;
    M.m[3][1] = 0.0f;
    M.m[3][2] = -fRange * NearZ;
    M.m[3][3] = 0.0f;

換句話說,我們獲得了以下公式:

x *=  1/tan(a/2) *  (Y:X)       //這里Y:X指輸入的橫縱比

y *= 1/tan(a/2)

這樣,X與Y 獲得的值去除以Z之后,就可以規范化到[-1,1]的NDC坐標了,也就是相對於屏幕的坐標。

 

Z代表了深度值

z = far*z/(far - near)   -  far*near/(far-near)

 

書中有一個曲線約束了深度值。。。。我這個也不懂為什么用這個公式。

 

還有一個沒有解決的問題,xyz經過矩陣變幻之后都要除以局部坐標中z的大小,但是代碼中沒有這一部分的實現機制。。。。估計GPU自己實現了?

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM