半径平方和反平方半径在照明计算中的用途是什么?


16

在“《战地风云3》中的DirectX 11渲染”幻灯片中的一张幻灯片中,我注意到了以下代码:

struct Light {
    float3 pos; float sqrRadius;
    float3 color; float invSqrRadius;
}

我不明白为什么他们要存储半径的平方,甚至是平方的倒数(我相信只是平方的半径),而不是仅仅存储半径?他们如何在计算中使用这些数据?此外,锥形和线形灯又如何呢?该结构只能用于点光源,我看不到它可用于其他类型-没有足够的数据。我仍然很想知道他们如何使用该平方和invSquare。

更新:好的,我终于明白了。

这是经典的光衰减方程式,很容易在网上找到:

float3 lightVector = lightPosition - surfacePosition;

float attenuation = saturate(1 - length(lightVector)/lightRadius);

length(lightVector)这样做实际上是相对昂贵的:

length(lightVector) = sqrt(dot(lightVector, lightVector);

此外,分割操作(/lightRadius)也相当昂贵。

不用这种方法来计算光衰减,您可以通过以下方法进行计算,这将更快。

attenuation = saturate(1 - dot(lightVector, lightVector)*invRadiusSqr);

其中invRadiusSqr可以在CPU级别上预先计算并作为着色器常量传递。

而且,结果是得到了二次光衰减(而不是前一种情况下的线性光衰减),这更好,因为IRL光已显示出二次衰减。

谢谢大家的帮助!


13
填充。着色器对齐16字节。而且,如果您看一下代码的格式,您会发现它会打包成两个float4。当您从缓存中免费获取有用的东西时,为什么不存储它呢?
Tordin 2014年

Answers:


23

这是一种简单的优化,因为,invSqrRadius = 1/SqrRadius与其在每次简单地缓存光时都没有为每个光计算平方根的倒数,而是因为至少与乘法相比,除法通常是“慢”的操作。

此优化尤其重要:

  • 当每个光源完成大量操作时,因此缓存该值将释放额外的CPU / GPU周期
  • 并且假设用于读取该值的内存访问时间实际上比重新计算它要快。

关于如何使用,我不确定它们的具体实现方式,但是关于1/sqrRadius,它仅用于光衰减,衰减和剔除。它也与方向和聚光灯相关,在聚光灯情况下的唯一区别是,您需要 应用衰减计算聚光灯系数。对于像太阳这样的定向光,它通常没有衰减或衰减,因此我想它会被忽略。

[编辑]只是为了详细说明,它不是无关紧要的数据。可以使用以下公式计算光辐照度:

E =披/ 4 * pi * rSqr;

哪里

E是焊剂的面积密度。

是辐射通量。

4 * pi * rSqr是球体的表面积。

该方程式解释了为什么接收到的能量随距离的平方而下降。

另一点是您需要计算顶点与光线之间的距离,以计算特定顶点上的光线贡献(此值不太可能被缓存),该顶点可以在光照范围之内或之外,从而使我们到达下一个点在哪里Radius Square有用。

如果您想要一个用于计算光衰减和剔除的实用示例,那么这在基于平铺的延迟渲染器中特别有用,这里是一个示例


诅咒,你把我击败了7秒!;)(还有更全面的答案!)
Trevor Powell

感谢您的详细评论,尤其是链接!据我从后一个链接了解到,《战地风云3》不是存储半径,而是存储光源和光接收器之间的实际距离,对吗?那就是他们在文章中使用的“ d”值。
cubrman 2014年

@cubrman,如果不看代码就很难推测。我的猜测是它是倒数radiusSqr。而且他们使用的方程可能与本文有很大不同。
concept3d

但是告诉我,您如何在照明计算中使用平方成平方的裸露的倒置光半径?我在网上找到的每个光源都告诉我,我需要找到接收表面和光源之间的距离,将其除以原始光半径,然后将结果平方。您将在哪里使用平方半径或invSqrRadius?对我来说,这似乎是完全无关的数据。
cubrman 2014年

1
@cubrman更新了答案。
concept3d


6

这里的其他答案是平方半径的倒数,但我将改为看平方半径(concept3d涉及到该平方,但我认为值得进一步讨论)。

平方有用的是距离比较。我们知道计算两点之间的距离涉及平方根,平方根的计算成本很高,但是如果我们要做的就是比较距离(以发现哪个更大或更小,然后根据结果)我们可以丢弃平方根。

如果sqrt(x)> sqrt(y),则x> y也是如此。

对于光源,平方半径等于光源中心和最大范围之间的距离-当然是平方。

对于照明计算,可以将其用于较早的情况。如果您要照明的点与光源的中心(平方)之间的距离大于半径的平方,则该点不会接收到任何光线,因此您无需运行其余的计算。因此,这只是一种优化(一种相当普遍的优化)-我们可以使用平方半径来进行距离比较,而无需花费昂贵的平方根,而只需减去和减去乘积即可。

我当然不知道这是否正是 BF3的用途,但是我希望我离这个目标不太远。


因此,如果我对您的理解正确,则代码将是:if(dot((lightPos-surfacePos),(lightPos-surfacePos))> lightRadiusSqr)不进行照明,对吗?
cubrman 2014年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.