unity urp 实现丝绸渲染

首先看一下实际上真实的效果

unity urp 实现丝绸渲染_环境光


再来一张

unity urp 实现丝绸渲染_sed_02


这是专门去找的。

可以看到丝绸渲染使用了各向异性的GGX去实现,有点仿头发的感觉,接下来看一下怎么实现的。

首先,准备实现双向反射率分布函数(BRDF)的DVF项。
D项使用UE里面的各项异性GGX:

登录后复制

// [Burley 2012, "Physically-Based Shading at Disney"]float D_GGXaniso(float ax, float ay, float NoH, float XoH, float YoH){  float a2 = ax * ay;  float3 V = float3(ay * XoH, ax * YoH, a2 * NoH);  float S = dot(V, V);  return(1.0f / PI) * a2 * Square(a2 / S);}1.2.3.4.5.6.7.8.9.

V项使用配合D项的Vis_SmithJointAniso

登录后复制

// [Heitz 2014, "Understanding the Masking-Shadowing Function in Microfacet-Based BRDFs"]float Vis_SmithJointAniso(float ax, float ay, float NoV, float NoL, float XoV, float XoL, float YoV, float YoL){  float Vis_SmithV = NoL * length(float3(ax * XoV, ay * YoV, NoV));  float Vis_SmithL = NoV * length(float3(ax * XoL, ay * YoL, NoL));  return 0.5 * rcp(Vis_SmithV + Vis_SmithL);}这个函数里面的x就是tangent y就是bitangent 中间的o就是dot计算结果1.2.3.4.5.6.7.8.

F项使用UE里面的F项:

登录后复制

// [Schlick 1994, "An Inexpensive BRDF Model for Physically-Based Rendering"]float3 F_Schlick_UE4(float3 SpecularColor, float VoH){  float Fc = Pow5(1 - VoH);         // 1 sub, 3 mul  //return Fc + (1 - Fc) * SpecularColor;   // 1 add, 3 mad    // Anything less than 2% is physically impossible and is instead considered to be shadowing  return saturate(50.0 * SpecularColor.g) * Fc + (1 - Fc) * SpecularColor;}1.2.3.4.5.6.7.8.9.

使用DVF项实现双向反射率分布函数:

登录后复制

float3 SlikBRDF(float3 DiffuseColor, float3 SpecularColor, float Roughness, float Anisotropy,float3 N, float3 T, float3 B, float3 V, float3 L, float3 LightColor, float Shadow){  float Alpha = Roughness * Roughness;  float a2 = Alpha * Alpha;  // Anisotropic parameters: ax and ay are the Roughness along the tangent and bitangent  // Kulla 2017, "Revisiting Physically Based Shading at Imageworks"  float ax = max(Alpha * (1.0 + Anisotropy), 0.001f);  float ay = max(Alpha * (1.0 - Anisotropy), 0.001f);  float3 H = normalize(L + V);  float NoH = saturate(dot(N, H));  float NoV = saturate(abs(dot(N, V)) + 1e-5);  float NoL = saturate(dot(N, L));  float VoH = saturate(dot(V, H));  float XoV = dot(T, V);  float XoL = dot(T, L);  float XoH = dot(T, H);  float YoV = dot(B, V);  float YoL = dot(B, L);  float YoH = dot(B, H);  float3 Radiance = NoL * LightColor * Shadow * PI;    //直接光漫反射  float3 DiffuseTerm = Diffuse_Lambert(DiffuseColor) * Radiance;  //直接光镜面反射  float D = D_GGXaniso(ax, ay, NoH, XoH, YoH);  float Vis = Vis_SmithJointAniso(ax, ay, NoV, NoL, XoV, XoL, YoV, YoL);  float3 F = F_Schlick_UE4(SpecularColor, VoH);  float3 SpecularTerm = ((D * Vis) * F) * Radiance;  return DiffuseTerm + SpecularTerm;}1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.22.23.24.25.26.27.28.29.30.31.32.33.34.35.

其它代码,我们可以直接魔改unity的urp内置的lit函数,用于获取相关的参数,这里就不再聊了。
那么,你会发现一个问题,因为GGXaniso 主要使用的是tangent和bitangent的朝向做高光的,我们平时做高光的时候都是使用法向的, 法向贴图不会影响这两个朝向啊,那么怎么办,这么办:

登录后复制

//设置T和B也受法线贴图的影响    half3 tangentTS = normalize(surfaceData.normalTS.x * half3(0, 0, 1) * _NormalAniso + half3(1, 0, 0));    half3 T = TransformTangentToWorld(tangentTS, tangentToWorld);    T = NormalizeNormalPerPixel(T);    half3 bitangentTS = normalize(surfaceData.normalTS.y * half3(0, 0, 1) * _NormalAniso + half3(0, 1, 0));    half3 B = TransformTangentToWorld(bitangentTS, tangentToWorld);    B = NormalizeNormalPerPixel(B);1.2.3.4.5.6.7.8.

我们用法向的x轴的量去影响切线空间下的切线的向量值,然后再将其转换到世界空间即可。
直接光的函数运算如下:

登录后复制

float3 DirectLighting(float3 DiffuseColor, float3 SpecularColor, float Roughness, float3 WorldPos, float Anisotropy,   float3 N, float3 T, float3 B, float3 V, float4 shadowCoord, float4 shadowMask){  //主光源  half3 DirectLighting_MainLight = half3(0, 0, 0);  {    Light light = GetMainLight(shadowCoord, WorldPos, shadowMask);    half3 L = light.direction;    half3 LightColor = light.color;    half Shadow = light.shadowAttenuation;    DirectLighting_MainLight = SlikBRDF(DiffuseColor, SpecularColor, Roughness, Anisotropy, N, T, B, V, L, LightColor, Shadow);  }  //附加光源  half3 DirectLighting_AddLight = half3(0, 0, 0);  #ifdef _ADDITIONAL_LIGHTS    uint pixelLightCount = GetAdditionalLightsCount();    for (uint lightIndex = 0; lightIndex < pixelLightCount; ++lightIndex)    {      Light light = GetAdditionalLight(lightIndex, WorldPos, shadowMask);      half3 L = light.direction;      half3 LightColor = light.color;      half Shadow = light.shadowAttenuation * light.distanceAttenuation;      DirectLighting_AddLight += SlikBRDF(DiffuseColor, SpecularColor, Roughness, Anisotropy, N, T, B, V, L, LightColor, Shadow);    }  #endif  return DirectLighting_MainLight + DirectLighting_AddLight;}1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.22.23.24.25.26.27.28.

计算主光源和附加光源,即可实现直接光的漫反射和镜面反射。

unity urp 实现丝绸渲染_反射率_03


这是-0.9的Anisotropy的效果

unity urp 实现丝绸渲染_sed_04


这是Anisotropy的值为0.9的效果,当前的双向反射率分布函数,如果把Anisotropy设置为1或者-1,效果上表现会很差。

环境光

首先,如果在环境光镜面反射上面实现aniso的效果呢,这里有段代码:

登录后复制

//根据设置的各项异性的强度,对法向进行扭曲,实现各项异性的环境高光  float3 anisotropicDirection = Anisotropy >= 0.0 ? B : T;  float3 anisotropicTangent = cross(anisotropicDirection, V);  float3 anisotropicNormal = cross(anisotropicTangent, anisotropicDirection);  float3 bentNormal = normalize(lerp(N, anisotropicNormal, abs(Anisotropy)));1.2.3.4.5.

用这个最终生成的bentNormal去替换之前的计算反射角度的N,就可以实现环境光镜面反射,效果是这样的:

unity urp 实现丝绸渲染_环境光_05


为了看效果,我故意把光滑度调高了。

间接光的实现函数:

登录后复制

float3 IndirectLighting(float3 DiffuseColor, float3 SpecularColor, float Roughness, float3 WorldPos, float Anisotropy, float3 N, float3 T, float3 B, float3 V, float Occlusion, float EnvRotation){  float NoV = saturate(abs(dot(N, V)) + 1e-5);  //SH  float3 DiffuseAO = AOMultiBounce(DiffuseColor, Occlusion);  float3 RadianceSH = SampleSH(N);  float3 IndirectDiffuse = RadianceSH * DiffuseColor * DiffuseAO;    //根据设置的各项异性的强度,对法向进行扭曲,实现各项异性的环境高光  float3 anisotropicDirection = Anisotropy >= 0.0 ? B : T;  float3 anisotropicTangent = cross(anisotropicDirection, V);  float3 anisotropicNormal = cross(anisotropicTangent, anisotropicDirection);  float3 bentNormal = normalize(lerp(N, anisotropicNormal, abs(Anisotropy)));  //IBL  half3 R = reflect(-V, bentNormal);  R = RotateDirection(R, EnvRotation);  half3 SpeucularLD = GlossyEnvironmentReflection(R, WorldPos, Roughness, Occlusion);  half3 SpecularDFG = EnvBRDFApprox(SpecularColor, Roughness, NoV);  float SpecularOcclusion = GetSpecularOcclusion(NoV, Pow2(Roughness), Occlusion);  float3 SpecularAO = AOMultiBounce(SpecularColor, SpecularOcclusion);  float3 IndirectSpecular = SpeucularLD * SpecularDFG * SpecularAO;  return IndirectDiffuse + IndirectSpecular;}1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.22.23.24.25.26.

整体的效果:

unity urp 实现丝绸渲染_游戏引擎_06

               



免责声明:本文系网络转载或改编,未找到原创作者,版权归原作者所有。如涉及版权,请联系删

QR Code
微信扫一扫,欢迎咨询~

联系我们
武汉格发信息技术有限公司
湖北省武汉市经开区科技园西路6号103孵化器
电话:155-2731-8020 座机:027-59821821
邮件:tanzw@gofarlic.com
Copyright © 2023 Gofarsoft Co.,Ltd. 保留所有权利
遇到许可问题?该如何解决!?
评估许可证实际采购量? 
不清楚软件许可证使用数据? 
收到软件厂商律师函!?  
想要少购买点许可证,节省费用? 
收到软件厂商侵权通告!?  
有正版license,但许可证不够用,需要新购? 
联系方式 155-2731-8020
预留信息,一起解决您的问题
* 姓名:
* 手机:

* 公司名称:

姓名不为空

手机不正确

公司不为空