首页 > 技术文章 > DirectX11笔记10:NDC空间与转换

Windogs 2015-12-24 19:58 原文

在书中的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自己实现了?

 

推荐阅读