URPでのBRDF計算式の問題

1)URPでのBRDF計算式の問題

2)Job Systemがメインスレッドの時間を占めるという問題

3)ProfilerでTempBufferの問題を特定する

4)Unity2019でHDRPのカメラのGL線画の問題

5)UnityWebCamTextureによって取得されたカメラ画像が回転される


 

Shader

 

Q:最近URP Shaderを見ているときに問題が見つかりました。それが間違っているかどうかわかりませんが、最初にコードを貼り付けます。

 

1つ目は、URPのLighting.hlslでBRDFを初期化する部分です。

inline void InitializeBRDFData(half3 albedo, half metallic, half3 specular, half smoothness, half alpha, out BRDFData outBRDFData)
{
#ifdef _SPECULAR_SETUP
    half reflectivity = ReflectivitySpecular(specular);
    half oneMinusReflectivity = 1.0 - reflectivity;

    outBRDFData.diffuse = albedo * (half3(1.0h, 1.0h, 1.0h) - specular);
    outBRDFData.specular = specular;
#else

    half oneMinusReflectivity = OneMinusReflectivityMetallic(metallic);
    half reflectivity = 1.0 - oneMinusReflectivity;

    outBRDFData.diffuse = albedo * oneMinusReflectivity;
    outBRDFData.specular = lerp(kDieletricSpec.rgb, albedo, metallic);
#endif

    outBRDFData.grazingTerm = saturate(smoothness + reflectivity);
    outBRDFData.perceptualRoughness = PerceptualSmoothnessToPerceptualRoughness(smoothness);
    outBRDFData.roughness = max(PerceptualRoughnessToRoughness(outBRDFData.perceptualRoughness), HALF_MIN);
    outBRDFData.roughness2 = outBRDFData.roughness * outBRDFData.roughness;

    outBRDFData.normalizationTerm = outBRDFData.roughness * 4.0h + 2.0h;
    outBRDFData.roughness2MinusOne = outBRDFData.roughness2 - 1.0h;

#ifdef _ALPHAPREMULTIPLY_ON
    outBRDFData.diffuse *= alpha;
    alpha = alpha * oneMinusReflectivity + reflectivity;
#endif
}

私の理解では:

 

outBRDFData.perceptualRoughnessは、粗さで、ラフネスと呼ばれるものです。

outBRDFData.roughnessは、粗さの2乗です。

outBRDFData.roughness2は、粗さの4乗です。

 

URPのLighting.hlslでBRDFを計算する部分を見てください。

// Based on Minimalist CookTorrance BRDF
// Implementation is slightly different from original derivation: http://www.thetenthplanet.de/archives/255
//
// * NDF [Modified] GGX
// * Modified Kelemen and Szirmay-Kalos for Visibility term
// * Fresnel approximated with 1/LdotH
half3 DirectBDRF(BRDFData brdfData, half3 normalWS, half3 lightDirectionWS, half3 viewDirectionWS)
{
#ifndef _SPECULARHIGHLIGHTS_OFF
    float3 halfDir = SafeNormalize(float3(lightDirectionWS) + float3(viewDirectionWS));

    float NoH = saturate(dot(normalWS, halfDir));
    half LoH = saturate(dot(lightDirectionWS, halfDir));

    // GGX Distribution multiplied by combined approximation of Visibility and Fresnel
    // BRDFspec = (D * V * F) / 4.0
    // D = roughness^2 / ( NoH^2 * (roughness^2 - 1) + 1 )^2
    // V * F = 1.0 / ( LoH^2 * (roughness + 0.5) )
    // See "Optimizing PBR for Mobile" from Siggraph 2015 moving mobile graphics course
    // https://community.arm.com/events/1155

    // Final BRDFspec = roughness^2 / ( NoH^2 * (roughness^2 - 1) + 1 )^2 * (LoH^2 * (roughness + 0.5) * 4.0)
    // We further optimize a few light invariant terms
    // brdfData.normalizationTerm = (roughness + 0.5) * 4.0 rewritten as roughness * 4.0 + 2.0 to a fit a MAD.
    float d = NoH * NoH * brdfData.roughness2MinusOne + 1.00001f;

    half LoH2 = LoH * LoH;
    half specularTerm = brdfData.roughness2 / ((d * d) * max(0.1h, LoH2) * brdfData.normalizationTerm);

    // On platforms where half actually means something, the denominator has a risk of overflow
    // clamp below was added specifically to "fix" that, but dx compiler (we convert bytecode to metal/gles)
    // sees that specularTerm have only non-negative terms, so it skips max(0,..) in clamp (leaving only min(100,...))
#if defined (SHADER_API_MOBILE) || defined (SHADER_API_SWITCH)
    specularTerm = specularTerm - HALF_MIN;
    specularTerm = clamp(specularTerm, 0.0, 100.0); // Prevent FP16 overflow on mobiles
#endif

    half3 color = specularTerm * brdfData.specular + brdfData.diffuse;
    return color;
#else
    return brdfData.diffuse;
#endif
}

ここでの最終的な式は次のとおりです。

BRDFspec=roughness^2/( NoH^2*(roughness^2-1)+1)^2*(LoH^2*(roughness+0.5)*4.0)

 

次に、コードに記述されているアドレスであるUnityの2015年の論文を見てください。

https://community.arm.com/events/1155

コードにあるroughnessは、実際には画像のroughnessの2乗です。混乱を防ぐために、以下はroughness©和roughness(n)で表します。

roughness©=roughness(n)^2

 

これを見て、私は疑問を持っています。論文のroughness(n)+0.5

ステップはコードに対応したら、roughness©^0.5+0.5

はずだが、URPコードでroughness©+0.5を直接使用します。これは私の理解が間違っているか、それともURPのShaderが間違っているか。

 

A:未熟な見方ですが、私の結論は次のとおりです。

2乗roughness(n)^2プラス5は妥当です

1乗と2乗のどちらを使用してもほとんど違いはありません

SIGGRAPHレポートの元のV*F関数は次のとおりです。

使用される実際のV*F関数は、上記の関数の近似値であり、画像に近い関数を使用して最適化します。

次のように作者は1乗と2乗をとる関数を画像化します。

2つの画像はそれほど違いがなく、傾向から見れば、2乗を取るのは元のVFに近いように見えます(Modified KSK and Schlick Fresnel depend on LH)。

作成者は、Lighting.hlslのコードを変更して、最初の1乗を使用します。

outBRDFData.normalizationTerm=outBRDFData.perceptualRoughness*4

同じ画面でroughness(n)+0.5とroughness(n)^2+0.5

を使用してマテリアルを描画すると、ほとんど違いはありません。

グラフィックスの最初の法則によると、見た目が正しければ正しいので、両方が正しいことを理解するのは問題ありません。そうすれば、差が大きくなければ、掘り下げる必要がないということでしょうか。

 

Script

 

Q:下図に示すように、Job Systemで実行したのにメインスレッドの時間を占めるのはなぜですか?

A:子スレッドの実行が終了するのを待ちます。

 

Rendering

 

Q:写真に示されているように、シーンにある2つのTempBufferはC#スクリプトから作成したわけではありません。シーンはAfterEffectsを使用せず、Grab PassのShaderも使用せずに、シーンカメラはMSAAとHDRをオフにしており、これら2つのTempBufferがまだあります。それを完全に取り除く方法は?

これは、次の2つのことと関わります。

A1:次のように設定してみてください:Camera.forceIntoRenderTexture = false;

このオプションは、カメラを強制的にTempBufferにレンダリングします。

AfterEffectsを使用しない場合は手動でfalseに設定し、AfterEffectsを有効にすると自動的にtrueに設定します。したがって、After Effectsを使用する場合、TempBufferは避けられません。

 

A2:すべてのMonoBehaviorのOnRenderImage(RenderTexture source、RenderTexture destination)関数をブロックします。一般的に、各OnRenderImage(RenderTexture source, RenderTexture destination)

関数は一つのTempBufferを生成します。

 

A3:Grab RenderTextureは通常、Depthが最小のカメラのClear FlagsがDepth Onlyに設定されている場合に表示されます。

 

Rendering

 

Q:Unity 2019.3.0f6で、GL線画を使うと、Gameビューにカメラが見つかりません。プロジェクトはHDRPプロジェクトに属しています。

 

A1:GL描画をGLDraws()に配置してください。Unity2020.2.2f1c1で、HDRP10.2.2環境で効果を正常に描画しました。

protected void OnEnable()

{

if (GraphicsSettings.renderPipelineAsset != null)

RenderPipelineManager.endFrameRendering += OnCameraRender;

}

 

protected void OnDisable()
{
    if (GraphicsSettings.renderPipelineAsset != null)
        RenderPipelineManager.endFrameRendering -= OnCameraRender;
}

protected void OnCameraRender(ScriptableRenderContext context, Camera[] cameraList)
{
    foreach(var camera in cameraList)
    {
        if (CheckFilter(camera))
            GLDraws();
    }
}

Script

Q:Unity WebCamTextureから取得したカメラ画像が回転されました。WeChatをスワイプしたところ、画像は正しいです。スマホを回転させると、画像は常に正しくて、回転の痕跡はありません。スマホが縦向きの場合は正常ですが、横向きに回転させると画像が正しくなくなります。

 

A:画面を回転させたときに、表示に使用するRawImageの回転を次のように調整できます。


UWA公式サイト:https://jp.uwa4d.com

UWA公式ブログ:https://blog.jp.uwa4d.com

UWA公式Q&Aコミュニティ(中国語注意):https://answer.uwa4d.com