[Unity] Unity 布料渲染实现的方式都有哪些

查看:730 |回复:5 | 2021-2-23 16:18:11

您需要 登录 才可以下载或查看,没有账号?注册

x
本帖最后由 源数之力 于 2021-3-2 14:38 编辑

Unity 布料渲染实现的方式都有哪些
2021-2-23 16:18:11  
 赞 赞 0

使用道具 登录

5个回答,把该问题分享到群,邀请大神一起回答。
2#


half4 Cuntom_BRDF_PBS(half3 diffColor, half3 specColor, half oneMinusReflectivity, half smoothness,
            float3 normal, float3 viewDir,
            UnityLight light, UnityIndirect gi)
            {
                float perceptualRoughness = SmoothnessToPerceptualRoughness (smoothness);
                float3 halfDir = Unity_SafeNormalize (float3(light.dir) + viewDir);

               
                #define UNITY_HANDLE_CORRECTLY_NEGATIVE_NDOTV 0

                #if UNITY_HANDLE_CORRECTLY_NEGATIVE_NDOTV
                    // The amount we shift the normal toward the view vector is defined by the dot product.
                    half shiftAmount = dot(normal, viewDir);
                    normal = shiftAmount < 0.0f ? normal + viewDir * (-shiftAmount + 1e-5f) : normal;
                    // A re-normalization should be applied here but as the shift is small we don't do it to save ALU.
                    //normal = normalize(normal);

                    half nv = saturate(dot(normal, viewDir)); // TODO: this saturate should no be necessary here
                #else
                    half nv = abs(dot(normal, viewDir));    // This abs allow to limit artifact
                #endif

                    half nl = saturate(dot(normal, light.dir));
                    float nh = saturate(dot(normal, halfDir));

                    half lv = saturate(dot(light.dir, viewDir));
                    half lh = saturate(dot(light.dir, halfDir));
                 //diffuse term
                half diffuseTerm = DisneyDiffuse(nv, nl, lh, perceptualRoughness);
               
                //specular term
                float roughness = PerceptualRoughnessToRoughness(perceptualRoughness);
                roughness = max(roughness, 0.002);
                half V = VisibilityCloth (nl, nv, roughness);
                half D = DistributionCloth(roughness, nh);
                #ifdef USE_LUMINANCE
                    half luminance = dot(diffColor,half3(0.299,0.587,0.114));
                    half3 F = luminance;
                #else
                    half3 F = specColor;//0.04 should be  default
                #endif

                half specularTerm = V * D * _SpecularScale;
                specularTerm = max(0, specularTerm * nl);


                half surfaceReduction;
                surfaceReduction = 1.0 / (roughness*roughness + 1.0);

                 specularTerm *= any(specColor) ? 1.0 : 0.0;

               
               
               
                 
                //half grazingTerm = saturate(smoothness + (1-oneMinusReflectivity));

                #ifdef  SUBSURFACE_COLOR_ON
                    diffuseTerm *= Wrap(nl,_FabricScatterSale);
                    half3 Fd = diffColor * (gi.diffuse + light.color * diffuseTerm
                     * saturate(_FabricScatterColor + nl));
                    half3 Fr = specularTerm * light.color;
                    half3 color = Fd + Fr;
                    
                #else
                    
                    half3 Fd = diffColor * (gi.diffuse + light.color * diffuseTerm
                     * nl);
                    half3 Fr = specularTerm * light.color;
                    half3 color = Fd + Fr;
                    

                #endif        

                return half4(color, 1);

            }
回复 收起回复
2021-2-23 16:28:46   回复
 赞 赞 1

使用道具 登录

3#
楼上的回答太专业了吧
回复 收起回复
2021-8-28 07:59:21   回复
 赞 赞 2

使用道具 登录

4#
大家好,我是Shawn。在知乎中对于布料这块的渲染好像并不多,网上能搜到的也是寥寥,因为目前大多数的基于物理的材质都是用BRDF进行渲染的,但是传统的BRDF是建立在微表面理论,但是对于微表面不能当成完美镜面的,传统的BRDF并不能很好的模拟,所以我们需要找到一个能“模拟”布料的BRDF公式。

在下面实现的代码都是基于以下的公式:

diffuse项是DisneyDiffuse 项:

[公式]

其中, [公式]

在UnityStandardBRDF中已经为我们实现好了,我们直接拿来用:

half diffuseTerm = DisneyDiffuse(nv, nl, lh, perceptualRoughness);
如果我们要为其添加次表面散射,则需要用到如下公式:

[公式]

其中w是一个0-1之间的值,用来控制散射的柔和程度,实际上就是WrapLighting,

代码:

inline float Wrap(float nl, float w) {
                return saturate((nl + w) / (1.0 + w)*(1.0 + w));
            }
diffuseTerm *= Wrap(nl,_FabricScatterSale);
half3 Fd = diffColor * diffuseTerm * saturate(_FabricScatterColor + nl);
这样Fd就算好了,接着就是specular项:

这里的公式和Cook-Torrance公式有点不一样,首先是D项:

在Physically Based Rendering in Filament 种他提到了两种模型,一种是Ashikhmin模型,他们认为在衣物的渲染中distribution占了很大的比重并且建立在这样的假设上:遮罩带来的影响为0。模型本身是一个逆高斯函数,并且当添加偏移后能很好的灯光对毛绒的高光的模拟,这里偏移为1:

[公式]

第二个模型为Charlie模型,这也是建立在遮罩影响为0的情况下,并且相对于上面的cot运算,Charlie模型的运算量更小。

[公式]



两种代码:

inline float D_Ashikhmin(float roughness,float nh){
                float a2 = roughness * roughness;
                float cos2h = nh * nh ;
                float sin2h = max(1.0 - cos2h, 0.0078125); // 2^(-14/2), so sin2h^2 > 0 in fp16
                    float sin4h = sin2h * sin2h;
                float cot2 = -cos2h / (a2 * sin2h);
                    return 1.0 / (PI * (4.0 * a2 + 1.0) * sin4h) * (4.0 * exp(cot2) + sin4h);

            }
inline float D_Charlie(float roughness, float nh) {
               
                float invAlpha  = 1.0 / roughness;
                float cos2h = nh * nh;
                float sin2h = max(1.0 - cos2h, 0.0078125); // 2^(-14/2), so sin2h^2 > 0 in fp16
                return (2.0 + invAlpha) * pow(sin2h, invAlpha * 0.5) / (2.0 * PI);
            }
对于specular中的G项(Unity中的Visibility)

[公式]

inline float VisibilityCloth(float nl, float nv, float roughness){
                return  1/(4 * ( nl + nv - nl*nv) + 1e-5f);
            }
最后整合得到Fr。

注意我们没有菲尼尔项,取而代之的是用Sheen来控制高光。在Physically Based Rendering in Filament 一文中,Sheen的缺省值为0.04 以实现天鹅绒的效果,如果考虑实现其他布料材质,我们可以考虑使用luminance来作为sheen值。

高光项的效果如下:(注意黑丝)


Charlie模型下的高光结果
加上漫反射项:


没有使用sss
加上sss:


加上sss后感觉更透了,像超能陆战队的大白一样。

不同参数下的结果:




注意:我们在使用sss时,不应该乘 [公式]

最后我们的代码应该看起来像这样的:

half4 Cuntom_BRDF_PBS(half3 diffColor, half3 specColor, half oneMinusReflectivity, half smoothness,
            float3 normal, float3 viewDir,
            UnityLight light, UnityIndirect gi)
            {
                float perceptualRoughness = SmoothnessToPerceptualRoughness (smoothness);
                float3 halfDir = Unity_SafeNormalize (float3(light.dir) + viewDir);

               
                #define UNITY_HANDLE_CORRECTLY_NEGATIVE_NDOTV 0

                #if UNITY_HANDLE_CORRECTLY_NEGATIVE_NDOTV
                    // The amount we shift the normal toward the view vector is defined by the dot product.
                    half shiftAmount = dot(normal, viewDir);
                    normal = shiftAmount < 0.0f ? normal + viewDir * (-shiftAmount + 1e-5f) : normal;
                    // A re-normalization should be applied here but as the shift is small we don't do it to save ALU.
                    //normal = normalize(normal);

                    half nv = saturate(dot(normal, viewDir)); // TODO: this saturate should no be necessary here
                #else
                    half nv = abs(dot(normal, viewDir));    // This abs allow to limit artifact
                #endif

                    half nl = saturate(dot(normal, light.dir));
                    float nh = saturate(dot(normal, halfDir));

                    half lv = saturate(dot(light.dir, viewDir));
                    half lh = saturate(dot(light.dir, halfDir));
                 //diffuse term
                half diffuseTerm = DisneyDiffuse(nv, nl, lh, perceptualRoughness);
               
                //specular term
                float roughness = PerceptualRoughnessToRoughness(perceptualRoughness);
                roughness = max(roughness, 0.002);
                half V = VisibilityCloth (nl, nv, roughness);
                half D = DistributionCloth(roughness, nh);
                #ifdef USE_LUMINANCE
                    half luminance = dot(diffColor,half3(0.299,0.587,0.114));
                    half3 F = luminance;
                #else
                    half3 F = specColor;//0.04 should be  default
                #endif

                half specularTerm = V * D * _SpecularScale;
                specularTerm = max(0, specularTerm * nl);


                half surfaceReduction;
                surfaceReduction = 1.0 / (roughness*roughness + 1.0);

                 specularTerm *= any(specColor) ? 1.0 : 0.0;

               
               
               
                 
                //half grazingTerm = saturate(smoothness + (1-oneMinusReflectivity));

                #ifdef  SUBSURFACE_COLOR_ON
                    diffuseTerm *= Wrap(nl,_FabricScatterSale);
                    half3 Fd = diffColor * (gi.diffuse + light.color * diffuseTerm
                     * saturate(_FabricScatterColor + nl));
                    half3 Fr = specularTerm * light.color;
                    half3 color = Fd + Fr;
                    
                #else
                    
                    half3 Fd = diffColor * (gi.diffuse + light.color * diffuseTerm
                     * nl);
                    half3 Fr = specularTerm * light.color;
                    half3 color = Fd + Fr;
                    

                #endif        

                return half4(color, 1);

            }
另外网上还有一种Custom Fabric的文章,并且知乎上有人实现了效果,传送门。但我觉得并不是很真实,他使用了比较强的菲尼尔系数,导致材质看起来蒙蒙的。

最后还有一篇关于快速模拟sss的文章,与本篇内容无关,但挺有意思的,传送门。

Shawn发布于 2020-08-10
回复 收起回复
2021-8-28 12:49:07   回复
 赞 赞 1

使用道具 登录

5#
用骨骼去模拟  我印象中有插件可以结合骨骼实现引擎实时摆动的规律
回复 收起回复
2021-12-25 12:09:19   回复
 赞 赞 1

使用道具 登录

6#
回复 收起回复
2023-2-26 12:48:41   回复
 赞 赞 0

使用道具 登录

CG 游戏行业专业问题

您需要登录后才可以回帖 登录 | 注册

本版积分规则

快速回复 返回顶部 返回列表