4

在URP中的BRDF计算公式问题

 3 years ago
source link: https://blog.uwa4d.com/archives/TechSharing_248.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

1)在URP中的BRDF计算公式问题
​2)Job System占用主线程时间问题
3)在Profiler中定位TempBuffer问题
4)Unity 2019使用HDRP的摄像机的GL画线问题
5)Unity WebCamTexture获取到的相机图片被旋转


这是第248篇UWA技术知识分享的推送。今天我们继续为大家精选了若干和开发、优化相关的问题,建议阅读时间10分钟,认真读完必有收获。

UWA 问答社区:answer.uwa4d.com
UWA QQ群2:793972859(原群已满员)

Shader

Q:最近在看URP Shader的时候发现个问题,不知道是不是我理解的不对。首先贴代码:

先是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 是粗糙度的平方;
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

代码里的roughness实际上是图里roughness的平方,为了防止混淆,下面用roughness©和roughness(n)来表示:
roughness©=roughness(n)^2

看到这里我产生了疑问,论文里roughness(n)+0.5这一步,对应到代码里,应该是roughness©^0.5+0.5才对,而URP代码里直接用了roughness©+0.5。我想问下是我理解的不对,还是URP的Shader写错了?

A:讲一下不太成熟的看法。我这边的结论是:

  • 用平方(roughness(n)^2)加0.5是合理的
  • 用一次方或者是平方差别不大

SIGGRAPH报告中最初V*F函数如下:

2.png

实际使用的V*F函数是对上述函数的一个近似,使用图像相近的函数进行优化:

3.png

笔者对取一次方和取平方的函数图像进行了绘制如下:

4.png

5.png

两个图像差别不大,而从走势上看,取平方似乎更最接近最初的VF(Modified KSK and Schlick Fresnel depend on LH)。

6.png

笔者修改了Lighting.hlsl的代码,使用一次方:
outBRDFData.normalizationTerm=outBRDFData.perceptualRoughness*4.0h+2.0h;
将使用roughness(n)+0.5和使用roughness(n)^2+0.5的材质绘制在同一个屏幕上如下,两者几乎没有差别:

7.png

根据图形学第一定律:如果它看起来是对的,那么它就是对的,所以理解成两个都对应该也可以。那么,是不是在差距不大的情况下就不必深究了。

感谢Prin@UWA问答社区提供了回答,欢迎大家转至社区交流:
https://answer.uwa4d.com/question/607fdebf6bb31032f97913ba


Script

Q:如图,为什么放到Job System去执行还会占用主线程的时间?

8.png

A:等待子线程执行完毕。

感谢Walker-523103@UWA问答社区提供了回答,欢迎大家转至社区交流:
https://answer.uwa4d.com/question/607ebad56bb31032f97913b0


Rendering

Q:如图所示,在一个场景里两个TempBuffer,不是从C#脚本上创建产生的。场景没有使用后效,没有使用Grab Pass的Shader,场景摄像机都关掉了MSAA和HDR,仍然有这两个TempBuffer。请问如何完全去掉?

9.png

好像和这两个东西有关:

10.png

A1:设置一下这个试试:Camera.forceIntoRenderTexture = false;
这个选项会强制将相机渲染到TempBuffer上。

在不使用后效时手动设置为false,开启后效会自动设为true。所以如果使用了后效,则TempBuffer是不可避免的了。

感谢范世青@UWA问答社区提供了回答

A2:屏蔽掉所有MonoBehavior的OnRenderImage(RenderTexture source, RenderTexture destination)函数,一般来说每个OnRenderImage(RenderTexture source, RenderTexture destination)函数都会产生一个TempBuffer。

感谢李星@UWA问答社区提供了回答

A3:Grab RenderTexture一般是Depth最小的相机的Clear Flags设置为Depth Only的时候会出现。

感谢Xuan@UWA问答社区提供了回答,欢迎大家转至社区交流:
https://answer.uwa4d.com/question/6079c4c26bb31032f9791362


Rendering

Q:在Unity 2019.3.0f6使用GL画线在Game视图下摄像机找不到,项目是属于HDRP工程。

A1:把你GL绘制的放到GLDraws()中,我在Unity 2020.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();
    }
}

感谢pp@UWA问答社区提供了回答,欢迎大家转至社区交流:
https://answer.uwa4d.com/question/5f9296c15d9aea5aa97197d1


Script

Q:从Unity WebCamTexture获取到的相机图片被旋转了,我看微信的扫一扫,画面都是对的,而且手机旋转的时候,画面也一直是正的,没有看到图片有旋转的痕迹。手机竖屏的时候是正常的,旋转到横屏,画面就不对了。

A:可以在屏幕旋转的时候按下面这样的方式调整一下用于显示的RawImage的旋转:

11.png

感谢羽飞@UWA问答社区提供了回答,欢迎大家转至社区交流:
https://answer.uwa4d.com/question/608268846bb31032f979141c

封面图来源:Unity Lightcookie Caustic
https://lab.uwa4d.com/lab/5dcdc2858bab6aaf023555a2


今天的分享就到这里。当然,生有涯而知无涯。在漫漫的开发周期中,您看到的这些问题也许都只是冰山一角,我们早已在UWA问答网站上准备了更多的技术话题等你一起来探索和分享。欢迎热爱进步的你加入,也许你的方法恰能解别人的燃眉之急;而他山之“石”,也能攻你之“玉”。

官网:www.uwa4d.com
官方技术博客:blog.uwa4d.com
官方问答社区:answer.uwa4d.com
UWA学堂:edu.uwa4d.com
官方技术QQ群:793972859(原群已满员)


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK