D3DXMatrixPerspectiveFovLH

本文详细解析了DirectX中D3DXMatrixPerspectiveFovLH函数的工作原理,阐述了如何通过该函数实现3D物体到特定盒子的投影变换,并讨论了视角、高宽比及裁面等参数对变换的影响。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

DirectX中投影变换D3DXMatrixPerspectiveFovLH()其实产生的变换矩阵不是将3D物体转换为2D平面画面的变换。实际上是把3D世界的物体变换到(1,1,0) (-1,1,0) (-1,-1,0) (1,-1,0) (1,1,1) (-1,1,1) (-1,-1,1) (1,-1,1)这个小盒子中。在Jim Adams 的著作<Role playing games with DriectX 8.0>和Frank Luna 的著作中<Introduction to 3D Game Programming with DirectX9.0>都把这个变换矩阵讲成从3D到2D的变换。实际上在DirectX中3D到2D的变换是由SDK自己完成的。而我们要做的是把3D世界变换到上面那个小盒子中。

这个变换矩阵是投影变换的预变换。

      在DirectX中,把视口的高作为3D世界里面的长度:2.0f来使用的,一个视点观察世界的视野范围在Y轴上无论什么情况下都是 -1.0f到1.0f(这一点在后面来证明)。把视口的高作为y轴,顶端坐标为(x,1),底端坐标为(x,-1)。

      视口:在窗体中显示3D的矩形范围,可以通过D3DDevice的SetViewPort()来设置大小,默认的大小为窗体的大小。

      变换的过程就是把点的向量乘以变换矩阵,可以得到这个点在新坐标系的对应向量,在投影变换矩阵中Z轴的值落在0~1之内,也就是说3D世界里所有的点都会被投影到以上描述的小盒子中。

我们用函数D3DXMatrixPerspectiveFovLH来获得该“投影变换”矩阵。

D3DXMATRIX * D3DXMatrixPerspectiveFovLH(

D3DXMATRIX *pOut,

FLOAT fovy,

FLOAT Aspect,

FLOAT zn,

FLOAT zf

);

其中参数fovy为y轴上的视角,Aspect为高宽比,zn为近裁面,zf为远裁面。

Y轴的视角:在DirectX的帮助文档中描述fovy为filed of view in y direction。

高宽比:一般用于全屏显示,如果我们要让3D世界充满整个窗口,就必须让高宽比等于窗口的高宽比,例如让我们的窗口正常显示”标准视野”[2X2 的一个面,顶点坐标为(1,1),(-1,1),(-1,-1),(1,-1)],我们必须把窗口的高宽比设置成1:1,这样,标准视野就可以充满整个窗口,否则 会在左右留出空白,这个空白是无法用VIEW,WORLD变换所能填充的,相当于word文档采用“文档”视图里面的两边的灰色区域,是无法编辑的,在一般的3D程序里面是不允许出现这种情况的,但是我们的显示器的高宽比不是1:1,而且大部分的用户也不习惯用1:1的窗口来使用程序,我们需要调整这个标准视野,来适应我们的窗口大小,Aspect就是这个作用,有一点需要说明的Aspect不等于1,不代表标准视野就不是(1,1),(-1,1),(-1,-1),(1,-1),如果Aspect = 0.5 标准视野也还是(1,1),(-1,1),(-1,-1),(1,-1),只不过x轴上的单位长度的像素值 是 y轴上单位长度的像素值的2倍。

裁面的作用: 在Z轴方向上,不显示z坐标小于zn的或者大于 zf的点。

 

 

 

      在上图中,紫红色的线为我们要显示的面,红色的弧就是上面函数fovy的夹角,当这个夹角为90°的时候,我们窗口的视野为标准视野,当夹角缩小时,可视范围就变小,如图,从90°变为60°的时候,可视范围变小了,要显示的面,相对于可以范围变大了,由于我们的窗口是不变的,也就是说实际显示的像素值不变,要显示的面就相对于变大,fovy的值变小的结果就是把要显示的面变大。
      当fovy为0的时候呢,视野就为0,就是说什么都看不到,窗口中显示的就是底色。

      当fovy变大至180°的时候呢?视野的范围为视线夹角上的垂直于Z轴的且过点(0,0,1)的连线。当视线夹角变大视野范围也变大,夹角趋近于180°的时候视野范围趋近于无穷大,要显示的面相对于视野范围就是0,也就是说缩小到最小就是0.所以我们在设定fovy的值为180°的时候也是什么都看不见,为底色。

      那大于180°的情况呢?在我们的视野理论上是不存在这种情况的,当视角大于180°时,视角于Z轴垂直的连线是不会过点(0,0,1)的。但是函数放在我们面前,我们是可以输入大于180°的数值,结果会如何呢?

非常神奇的事情发生了,我们的图以原点为中心旋转了180°!这是无法用我们的视野理论来解释的,因为180°的视野范围就已经是个极限了,“超越极限”是只能在广告中出现的词汇(如果能超越,那就不叫极限了:P)。

      在MSDN的文档中有这样一个数学公式:y-scale = cot(fovy/2)

      这下就明白了,当我们输入大于180°的时候cot的值为负数。而x-scale = y-scale/ Aspect。.

也就是说,一个3D世界的点,在视野大于180°的作用下,它的放大/缩小倍数不变,但是需要对X轴做一次对称变换,还要对Y轴做一次对称变换。相当于对原点做一次对称变换。

拿到这个公式,我们就可以对前面的标准做一个证明:

当fovy为90° Aspect = 1的时候,就是说y-scale = cot(45°) = 1

x-scale = 1,这时y和x的缩放比都为1,没有缩放。我们显示这样一个面

              {-1.0f, -1.0f, 0.0f},

              {-1.0f, 1.0f, 0.0f},

              { 1.0f, 1.0f, 0.0f},

              {-1.0f, -1.0f, 0.0f},

              { 1.0f, 1.0f, 0.0f},

              { 1.0f,-1.0f, 0.0f}

我们会发现无论怎么改变窗口的高度,这个画面的Y轴方向上都会充满整个视口。(如果我们把视口的高度和宽度设置成一样的,在X轴上也会充满整个视口)所以DirectX会以视口的高度的二分之一作为Y轴的单位长度。

在D3D11中,初始化过程通常包括以下几个关键步骤[^1]: 1. **创建Device**:这是D3D11引擎的核心组件,用于执行图形和计算操作。通过`ID3D11CreateDevice`接口创建,可以指定硬件支持级别、多线程模型以及特性水平。 ```cpp IDXGIAdapter* adapter; D3D_FEATURE_LEVEL featureLevel; HRESULT hr = D3D11CreateDevice(adapter, D3D_DRIVER_TYPE_HARDWARE, NULL, D3D11_CREATE_DEVICE_BGRA_SUPPORT | D3D11_CREATE_DEVICE_DEBUG, &featureLevel, 1, D3D11_SDK_VERSION, &device, &featureLevel, &context); ``` 2. **创建Context**:与Device一起工作的一个会话上下文,用于提交命令到GPU执行。创建时要关联到Device。 ```cpp ID3D11DeviceContext* context; hr = device->CreateContext(D3D11_CONTEXT_ALL, &context); ``` 3. **深度/模板缓冲和渲染目标视图**:设置深度缓冲以存储Z值,渲染目标视图用于最终的图像呈现。这涉及到创建合适的`ID3D11DepthStencilView`和`ID3D11RenderTargetView`。 ```cpp D3D11_TEXTURE2D_DESC depthDesc; // 初始化深度描述符... HR(device->CreateTexture2D(&depthDesc, NULL, &depthBuffer)); HR(context->CreateDepthStencilView(depthBuffer, ...)); HR(device->CreateRenderTargetView(..., &renderTargetView)); ``` 4. **视口和变换矩阵设置**:定义屏幕上的绘制区域和投影变换。 ```cpp D3D11_VIEWPORT viewport; viewport.Width = backBufferWidth; viewport.Height = backBufferHeight; // 初始化视口... context->RSSetViewports(1, &viewport); D3DXMATRIX projectionMatrix; D3DXMatrixPerspectiveFovLH(&projectionMatrix, D3DXToRadian(fov), aspectRatio, nearClip, farClip); context->OMSetRenderTargets(1, &renderTargetView, depthBuffer); context->IASetTransformFeedbackLayout(...); ``` 5. **着色器和资源管理**:加载并编译着色器程序,配置顶点输入布局和几何描述符集,最后绑定资源。 ```cpp ID3DBlob* vertexShaderBlob, *pixelShaderBlob; // 编译着色器... HR(device->CreateVertexShader(vertexShaderBlob, sizeof(vertexShaderBlob), NULL, &vertexShader)); HR(device->CreatePixelShader(pixelShaderBlob, sizeof(pixelShaderBlob), NULL, &pixelShader)); // 绑定资源... context->PSSetShaderResources(0, 1, &shaderResourceView); ``` 完成上述步骤后,D3D11就准备好了进行渲染操作。然而,实际项目可能还需要考虑错误处理、资源清理和其他细节。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值