在Direct3D中向着色器传递世界、视图和投影矩阵

4
我正在学习DirectX的所有MS教程,我注意到它们总是将World、View和Projection矩阵传递给技术,然后在顶点着色器中对它们进行乘法运算。
有必要这样做吗?而不是在绘制之前乘以矩阵,然后将单个WVP矩阵传递给着色器?这样,每帧只需要一次矩阵乘法,而不是每个顶点都需要一次,对吗?
提前感谢您的回答。
编辑: 附上示例代码
MS教程:
matrix World;
matrix View;
matrix Projection;

struct VS_INPUT
{
    float4 Pos : POSITION;
    float4 Color : COLOR;
};

struct VS_OUTPUT
{
    float4 Pos : SV_POSITION;
    float4 Color : COLOR;
};

PS_INPUT VS( VS_INPUT input )
{
    VS_OUTPUT output;
    output.Pos = mul( input.Pos, World );
    output.Pos = mul( output.Pos, View );
    output.Pos = mul( output.Pos, Projection );
    output.Color = input.Color;

    return output;
}

为什么不这样做:
matrix WVP;

struct VS_INPUT
{
    float4 Pos : POSITION;
    float4 Color : COLOR;
};

struct VS_OUTPUT
{
    float4 Pos : SV_POSITION;
    float4 Color : COLOR;
};

PS_INPUT VS( VS_INPUT input )
{
    VS_OUTPUT output;
    output.Pos = mul( input.Pos, WVP );  
    output.Color = input.Color;

    return output;
}
4个回答

6
在这个例子中,您建议对世界、视图和投影矩阵进行预乘,这样更有效率。我不确定为什么教程在这个简单的例子中将它们分开。
有时将它们分开是有意义的(或者将世界矩阵保持独立,同时使用结合的视图-投影矩阵)。例如,有时您可能希望在世界空间而不是对象空间中进行光照处理,因此您需要在顶点着色器中访问世界空间顶点位置。如果您正在执行某种实例化,则可能还希望将世界空间矩阵保持分开,如DeadMG所指出的那样。但在许多情况下,像您建议的那样将它们组合起来会更好。

3
因为世界矩阵针对每个对象都会发生变化。如果您将WVP矩阵传递到着色器中,则每次重新绘制时都必须更改该矩阵-非常低效,并且您将在CPU上进行乘法运算,这也非常慢。另一方面,您可以一次性将所有世界矩阵发送到GPU中的硬件实例缓冲区中,然后在GPU上将它们全部乘出来,在现代GPU的规模上几乎不会产生任何影响,然后发出一次绘制调用。
简而言之,当您添加硬件实例化时,将其放在着色器中执行效率要高得多。

那么你的意思是说,在 CPU 上每帧将这三个矩阵相乘一次比在 GPU 上每顶点执行一次(因此可能每帧执行数千次)效率更低?世界矩阵每帧都在 CPU 上更新。 - jaho
什么?不对。你需要在GPU上每个实例传递世界矩阵,将VP矩阵在CPU上乘一次并传递,然后在GPU上再乘以世界和VP。 - Puppy

0

我认为每个矩阵在每一帧中都可以改变。除非你以相同的方式查看某些内容,否则如果你将相机视角定位到其他角度或坐标来观察世界/物体,则视图可能会发生变化。


0
这样,矩阵将会在每一帧中乘以一次,而不是在每一个顶点中乘以一次,对吗?
"每一帧"这个术语在这里没有意义。为什么我们要称其为顶点着色器?因为它是一个基于每个顶点的处理过程,所以无论您将三个矩阵乘以单个矩阵(在或外顶点着色器中),每个顶点在绘制之前都应该应用于此单个矩阵。GPU是这种任务的更好选择,如向量/矩阵计算和其他图形处理。

顶点始终只在第一种情况下被乘以三次,在第二种情况下只乘以一次。我已经在问题中添加了示例代码。 - jaho
嗯,我觉得我最初误解了你的意思,我以为你是在说顶点和矩阵相乘,实际上你只是指矩阵相乘。对此我感到抱歉。 - zdd

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接