shoeisha-books / hlsl-grimoire-sample

『HLSL シェーダーの魔導書』(ISBN978-4-7981-6428-1)のサンプルファイル
MIT License
154 stars 92 forks source link

TBDR章で行っているライトカリングのフラスタムとの当たり判定について #4

Open keny30827 opened 2 months ago

keny30827 commented 2 months ago

お世話になっております。 Issueというよりはただの質問になってしまうのですが…

ComputeShaderで行っているライトカリング用ライト配列計算の中で、 タイルごとに計算したフラスタムの各面法線とライト位置との内積(平面の方程式による内外判定)を計算していると思うのですが、 ビュー空間で計算しているため、平面位置を加味しないと正しく計算できないように思えてしまいます。

つまり、a(x - x0) + b(y - y0) + c(z - z0) = 0 として、x0 ~ z0までを各フラスタムの面ごとに指定しないと正しい距離計算ができないように感じるのですが、 書籍や元の研究内容( https://www.intel.com/content/www/us/en/developer/articles/technical/deferred-rendering-for-current-and-future-rendering-pipelines.html )では、 a(x - 0) + b(y - 0) + c(z - 0) = 0 として扱ってしまっているように思えてしまい、 それだとフラスタム内かどうかの計算が正しくできないのではないかと… これが、各タイル用フラスタムの空間に変換された座標系での比較なら理解できるのですが、そういった計算も見受けられず…

また、ax + by + cz + d = 0 として、dに面位置と面法線の内積が入っているならそれでも問題ないと思うのですが、 nearとfar以外にそれらしき数値が入っていないように見受けられまして…

ただ、一通りそれっぽい内容でweb検索をかけても同じ疑問が見受けられなかったため、 おそらく私の知識不足で行間が読めてないのだろうと思っています。 大変お手数だとは思いますが、こちらに関してご教示いただくことは可能でしょうか?

追記:

念のため手元でd相当の計算として、下記のような計算を加えてみたところ、 正しい結果は出たのですが、加えようが加えまいが正しい結果にはなるようで、 やはり混乱しております…

// スクリーン座標で面位置のXYを決めたうえで、ビュー座標に変換してからZ方向に押し出す.
float2 baseScreenPos = float2(groupId.x * LIGHT_TILE_WIDTH, groupId.y * LIGHT_TILE_HEIGHT);
float z = (maxTileZ + minTileZ) * 0.5f;
float3 ofsZ = float3(0.0f, 0.0f, minTileZ);
// 右.
{
    float2 screenPos = baseScreenPos + float2(LIGHT_TILE_WIDTH, float(LIGHT_TILE_HEIGHT) * 0.5f);
    float3 viewFrontPos = ComputePositionInCamera(screenPos, true); // zを0.0fにしたクリップ -> ビュー変換.
    frustumPlanes[0].w = dot(frustumPlanes[0].xyz, viewFrontPos + ofsZ);
}
// 左.
{
    float2 screenPos = baseScreenPos + float2(0.0f, float(LIGHT_TILE_HEIGHT) * 0.5f);
    float3 viewFrontPos = ComputePositionInCamera(screenPos, true); // zを0.0fにしたクリップ -> ビュー変換.
    frustumPlanes[1].w = -dot(frustumPlanes[1].xyz, viewFrontPos + ofsZ);
}
// 下.
{
    float2 screenPos = baseScreenPos + float2(float(LIGHT_TILE_WIDTH) * 0.5f, LIGHT_TILE_HEIGHT);
    float3 viewFrontPos = ComputePositionInCamera(screenPos, true); // zを0.0fにしたクリップ -> ビュー変換.
    frustumPlanes[2].w = -dot(frustumPlanes[2].xyz, viewFrontPos + ofsZ);
}
// 上.
{
    float2 screenPos = baseScreenPos + float2(float(LIGHT_TILE_WIDTH) * 0.5f, 0.0f);
    float3 viewFrontPos = ComputePositionInCamera(screenPos, true); // zを0.0fにしたクリップ -> ビュー変換.
    frustumPlanes[3].w = dot(frustumPlanes[3].xyz, viewFrontPos + ofsZ);
}

追々記: 上記、押し出し方向が正しくなく、正しい計算ではありませんでした…申し訳なりません。 ただ、言いたいことはなんとなく伝わるかなと思いますので、記載はそのままにさせていただきます。