高い鏡面反射力を持つ鏡面バンディング


8

DirectXでのレイトレーシングにいくつかの問題があり、特にスペキュラに関する深刻なバンディングの問題があります。高い鏡面反射力(8以上)でバンディングが始まります。これがHDR / LDRの問題なのか、それとも法線や他のベクトルなど、他の何かに関連している可能性があるのでしょうか。

更新

更新については以下をご覧ください。

これは、球体上のブリンフォンに関連するシェーダーコードです。

float3 hitPoint = thisRay.origin + thisRay.direction * bestHit.hitT;
float3 normal = normalize(hitPoint - spheres[bestHit.hitID].center);
float3 toLight = pointLights[0].position.xyz - hitPoint;
float d = length(toLight);
toLight = normalize(toLight);

float diffuse = max(dot(normal, toLight), 0.0f);

float3 v = normalize(thisRay.origin - hitPoint);
float3 h = normalize(v + toLight);

float spec = 0;
if (diffuse > 0)
    spec = pow(max(dot(normal, h), 0.0f), specPower) * diffuse;

output[threadID.xy] = spheres[bestHit.hitID].colour * diffuse + spheres[bestHit.hitID].specColour * spec;

この画像では、specPowerは8です。 この画像では、specPowerは8です。

この画像では、specPowerは9です。 この画像では、specPowerは9です。

これは、HDR / LDRの問題と同じくらい簡単ですか、それとも通常の精度に関連していますか?法線が低精度で不適切にパック/アンパックされた遅延レンダラーでこの問題が以前に発生したと思いますが、この場合、法線はオンザフライで生成され、すべてが直接バックバッファーにレンダリングされます。

アップデート1

上記に加えて、三角形には同じアーティファクトがあり、それらの法線は現在次のように生成されていることを追加したいと思います。

float3 normal = normalize(cross(triangles[bestHit.hitID].vertices[1] - triangles[bestHit.hitID].vertices[0],
                                    triangles[bestHit.hitID].vertices[2] - triangles[bestHit.hitID].vertices[0]));

これにより、表面の法線が問題である可能性はさらに低くなります。次の画像は、specPowerが2048に達するとどうなるかを示しています。

ここに画像の説明を入力してください


1
推測にすぎません。試してみてくださいfloat3 h = normalize( reflect(toLight,normal) );。そしてspec = pow(dot(v, h) * 0.5 + 0.5, specPower) * diffuse;
Raxvan 14

これは同じアーティファクトを生成したと言って申し訳ありません。
Bentebent 2014

4
オーバーフローの問題のようです。値はゼロになるはずですが、代わりに1に戻ります。の値をspec直接出力してみてくださいmax(dot(normal, h), 0.0f)。どちらの計算でも、この戻り値が1の値を探します。

この画像は、スペックが0.9fを上回っているポイントを示しているため、原因である可能性があります。imgur.com/YbTS8m5
Bentebent

一方、0.99fを超える値を出力する場合、max(dot(normal、h)、0.0f)は次のようになります(imgur.com/YV22egw)。
Bentebent 14

回答:


1

この特定のGPUでのpow関数の実装に問題があるようです。

数学関数のバグが疑われる場合は、数学関数を同等の関数に置き換えます。たとえば、次のように置き換えます。

spec = pow(max(dot(normal, h), 0.0f), specPower) * diffuse;

specPower = 9.0にバグが表示されたら、ハードコードして

float temp = max(dot(normal, h), 0.0f);
spec = pow(temp, 8.0) * temp * diffuse;

または

float temp = max(dot(normal, h), 0.0f);
spec = temp * temp * temp * temp * temp * temp * temp * temp * temp * diffuse;

問題が解消された場合、これはおそらく、このGPUまたはドライバーの指数に関するpow関数にアンダーフローのバグがあることを意味します。

回避策としてできること フラグメントシェーダーの計算が16ビットの浮動小数点で内部的に行われると仮定して。この権利を計算したら

pow(max(temp, (Nth root of (1.0/16384.0))), N);

32768または8192の可能性があります。1ビットずれているか、GPUが多かれ少なかれ精度を使用している可能性があります。pow(x、9.0)の場合、クランプはpow(max(temp、0.3401975)、9.0)になります。これを下回るXの値は、IEEE 16ビット浮動小数点の指数範囲(+15から-14)をアンダーフローします(ここでも、これがGPUが使用するものであると仮定します。)

ロゼッタコードから(http://rosettacode.org/wiki/Nth_root#C.2B.2B

double NthRoot(double value, double degree)
{
    return pow(value, (double)(1 / degree));
};

my_shader->SetUniform("specular_minimum", NthRoot((1.0/16384.0), specPower));

これをCPUで計算し、値をユニフォームでフィードする必要があります。シェーダーでこれを正しく計算すると、GPUでおそらく同じ精度の問題が発生します。

これを確認できる場合は、問題を再現するサンプルシェーダーソースコードをGPUドライバーチームに連絡して、正確なドライバーバージョン、OS、およびGPUモデルリビジョンを提供してください。実行可能ファイルは役立つかもしれませんが、電子メールで送信しないので送信しないでください。ウイルス/スパムチェッカーに閉じ込められます。ビルドして、ビルド済みのサンプル実行可能ファイルを要求された場合に備えてコピーを保持するだけです。礼儀として、公開する前に修正する機会を与えます。特定のシェーダーでのみ発生するシェーダーコンパイラーの最適化バグである可能性があります。彼らと連絡を取るのにしばらく(数ヶ月)かかるかもしれません、これらの人はしばしば人手不足です。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.