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)を見てください。**
![1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653855/d0c46d62-11c7-ba1b-15a5-89129a0611d2.png)
**コードにある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プラス５は妥当です
１乗と2乗のどちらを使用してもほとんど違いはありません
SIGGRAPHレポートの元のV*F関数は次のとおりです。
![2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653855/966cd789-1c6e-4d2e-7747-190d578b01fd.png)
使用される実際のV*F関数は、上記の関数の近似値であり、画像に近い関数を使用して最適化します。
![3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653855/ba974a56-e1b4-1f30-5e13-e8852fa1d426.png)
次のように作者は１乗と2乗をとる関数を画像化します。
![4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653855/0a7c472e-9206-ef58-46f2-c770f01a1075.png)
![5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653855/455bd997-36a9-d1d2-de52-596c809853c0.png)
2つの画像はそれほど違いがなく、傾向から見れば、2乗を取るのは元のVFに近いように見えます（Modified KSK and Schlick Fresnel depend on LH）。
>![6.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653855/f2aecc62-7b68-4b01-55ae-cc53155f4660.png)
作者は、Lighting.hlslのコードを変更して、最初の１乗を使用します。
outBRDFData.normalizationTerm=outBRDFData.perceptualRoughness*4
同じ画面でroughness(n)+0.5とroughness(n)^2+0.5
を使用してマテリアルを描画すると、ほとんど違いはありません。
![7.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653855/b94e7e73-ea8e-03df-8efb-51e3881990e6.png)
グラフィックスの最初の法則によると、見た目が正しければ正しいので、両方が正しいことを理解するのは問題ありません。そうすれば、差が大きくなければ、掘り下げる必要がないということでしょうか。


## Script

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

![8.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653855/1f117cdd-a915-0a52-0832-88a3dc0195af.png)
>A：子スレッドの実行が終了するのを待ちます。

## Rendering
 
**Q：写真に示されているように、シーンにある2つのTempBufferはC＃スクリプトから作成したわけではありません。シーンはAfterEffectsを使用せず、Grab PassのShaderも使用せずに、シーンカメラはMSAAとHDRをオフにしており、これら2つのTempBufferがまだあります。それを完全に取り除く方法は？**
![9.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653855/bdba0d0f-2994-842f-ca7e-814bf663af14.png)
**これは、次の2つのことと関わりそうです。**
![10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653855/8905752e-9cac-9fdb-7fe3-b135d4f05b4b.png)
>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の回転を次のように調整できます。
![11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653855/85ed09ee-6034-2b42-f104-941bff631207.png)
---
UWA Technologyは、モバイル/VRなど様々なゲーム開発者向け、**パフォーマンス分析**と**最適化ソリューション**及び**コンサルティングサービス**を提供している会社でございます。

今なら、UWA GOTローカルツールが**15日間に無償試用**できます！！
よければ、ぜひ！

UWA公式サイト：https://jp.uwa4d.com
UWA GOT OnlineレポートDemo：https://jp.uwa4d.com/u/got/demo.html
UWA公式ブログ：https://blog.jp.uwa4d.com