Unity translucent SSS 次表面散射 皮肤材质研究

https://www.zhihu.com/question/265769844/answer/299073168
https://www.jianshu.com/p/a1de93d2dcb7
https://zhuanlan.zhihu.com/p/337384739
记录一下吧
准确的说应该是透射
http://iryoku.com/translucency/
在这里插入图片描述
大概原理是当光线从后面照进来的时候,中间是黑的,外周围是亮的,很像边缘光,但是明暗过渡又很微妙,所以硬要模拟的话,美术可以手绘一张贴图去控制皮肤的强弱,通透度。否者只能模拟一下这种效果,很奇怪,😂

在这里插入图片描述
这里用了Unity的插件paint in 3D 其实可以自己手绘的,这哥插件貌似有bug

在这里插入图片描述
还是要加mask

Shader "Unlit/UnityBRDFSkin"
{
    Properties
    {
        _SSSColor("SSSColor",Color)=(1,1,1,1)
        _MainTex ("Texture", 2D) = "white" {}
        _NormalTex ("Normal Map", 2D) = "bump" {}
        _TranslucentMaskTex ("TranslucentMaskTex", 2D) = "white" {}
        _Metallic("_Metallic",Range(0,1))=0
        _Smoothness("Smoothness",Range(0,1))=0
        _NormalMapIntensity("Normal intensity", Range(0,3)) = 1 
        _Distortion("_Distortion", Range(0,2)) = 1 
        _Power("_Power", Range(0,10)) = 1 
        _Scale("_Scale", Range(0,10)) = 1 
    }
    SubShader
    {
        CGINCLUDE
        #include "UnityStandardUtils.cginc"
        #include "AutoLight.cginc"
        #include "UnityCG.cginc"
        #include "UnityPBSLighting.cginc"
        struct a2v
        {
            float4 vertex : POSITION;
            float2 uv : TEXCOORD0;
            float3 normal:NORMAL;
            float4 tangent:TANGENT;
        };

        struct v2f
        {
            float4 pos : SV_POSITION;
            float3 normal:TEXCOORD1;
            float3 tangent:TEXCOORD3;
            float2 uv : TEXCOORD0;
            float3 worldPos:TEXCOORD2;
            SHADOW_COORDS(5)
        };

        sampler2D _MainTex;
        float4 _MainTex_ST;
        float _Metallic;
        float _Smoothness;
        fixed4 _SSSColor;

        sampler2D _NormalTex;
        float _NormalMapIntensity;

        sampler2D _TranslucentMaskTex;

        float _Distortion;
        float _Power;
        float _Scale;

        v2f vert (a2v v)
        {
            v2f o;
            o.pos = UnityObjectToClipPos(v.vertex);
            o.uv = TRANSFORM_TEX(v.uv, _MainTex);
            o.worldPos = mul(unity_ObjectToWorld,v.vertex);
            o.normal = UnityObjectToWorldNormal(v.normal);
            o.tangent = UnityObjectToWorldNormal(v.tangent);
            return o;
        }

        fixed4 frag (v2f i) : SV_Target
        {
            //data
            float3 worldViewDir = normalize(UnityWorldSpaceViewDir(i.worldPos)) ;
            float3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos)) ;
            float3 worldNormal = normalize(i.normal);
            float3 worldTangent = normalize(i.tangent);

            fixed3 albedo = tex2D(_MainTex,i.uv).xyz;

            half3 specColor;
            half oneMinusReflectivity;
            albedo = DiffuseAndSpecularFromMetallic(albedo, _Metallic, specColor, oneMinusReflectivity);

            UnityLight DirectLight;
            DirectLight.dir = worldLightDir;
            DirectLight.color = _LightColor0.xyz;
            DirectLight.ndotl = DotClamped(worldNormal,worldLightDir);
            
            UnityIndirect InDirectLight;
            InDirectLight.diffuse = 0;
            InDirectLight.specular = 0;
            
            // 法线贴图
            float3 normalMap = UnpackNormal(tex2D(_NormalTex, 
            i.uv)); 
            normalMap.x *= _NormalMapIntensity; 
            normalMap.y *= _NormalMapIntensity; 
            float3 TNormal = normalMap;
            float3 binormal = cross(worldNormal, worldTangent);
            float3x3 T2W = float3x3(
                            worldTangent.xyz,
                            binormal.xyz,
                            worldNormal.xyz);
            T2W = transpose(T2W);
            TNormal = mul(T2W, TNormal);
            worldNormal = normalize(TNormal);

            // Translucency.
            float translucencyMask = tex2D(_TranslucentMaskTex, i.uv).b;
            half3 transLightDir = worldLightDir + worldNormal * _Distortion;
            float transDot = pow(max(0, dot(worldViewDir, -transLightDir)), _Power ) * _Scale;

            UNITY_LIGHT_ATTENUATION(atten, i, i.worldPos);

            fixed3 transLight = atten * transDot * _SSSColor.a * _SSSColor.rgb;
            fixed3 transAlbedo = albedo * _LightColor0.rgb * transLight;
            transAlbedo = saturate(transAlbedo);
            transAlbedo = transAlbedo * translucencyMask;


            albedo = 0;
            specColor = 0;
            fixed4 col = UNITY_BRDF_PBS(albedo,specColor,oneMinusReflectivity,
            _Smoothness,worldNormal,worldViewDir,
            DirectLight,InDirectLight);
            col.xyz = col.xyz + transAlbedo;
            return col;
        }
        ENDCG

        Pass
        {
            Tags{"LightMode"="ForwardBase"}
            CGPROGRAM
            #pragma target 3.0
            #pragma vertex vert
            #pragma fragment frag
            #pragma multi_compile_fwdbase     

            ENDCG
        }

        Pass
        {
            Tags{"LightMode"="ForwardAdd"}
            Blend One One
            CGPROGRAM
            #pragma target 3.0
            #pragma vertex vert
            #pragma fragment frag
            #pragma multi_compile_fwdadd

            ENDCG
        }

        
    }
}

类似于这张贴图吧
在这里插入图片描述

曲率

上面的这张图其实有点像SSS中的曲率

现在再来看这篇文章,感觉是错漏百出 🤣 改天重写一篇

预积分的次表面

// SSSS
half nl = saturate(dot(normalWS, mainLight.direction)) * 0.5 + 0.5;
float curvature = clamp(_Curvature, 0.01, 0.99);

float3 blurNormalDirection = normalWS2;
half curve = length(fwidth(blurNormalDirection)) / (length(fwidth(positionWS)) * curvature * 20);
half NDotL = dot(blurNormalDirection, mainLight.direction);
// NDotL = saturate(NDotL);
half3 sssColor = SAMPLE_TEXTURE2D(_LutMap, sampler_LutMap, float2(NDotL * 0.5 + 0.5, curve)) * mainLight
    .color;

// return half4(curve.xxx, 1);

// Mix diffuse GI with environment reflections.
half3 color = GlobalIllumination(brdfData, bakedGI, surfaceData.occlusion, normalWS, viewDirectionWS);


// SSS
brdfData.diffuse *= sssColor * 1.5;

双lobe specular

    BRDFData brdfData2 = (BRDFData)0;
    brdfData2.diffuse = brdfData.diffuse;
    brdfData2.reflectivity = brdfData.reflectivity;
    brdfData2.specular = brdfData.specular;
    
    brdfData2.perceptualRoughness = PerceptualSmoothnessToPerceptualRoughness(smoothness2);;
    brdfData2.roughness = max(PerceptualRoughnessToRoughness(brdfData2.perceptualRoughness), HALF_MIN_SQRT);;
    brdfData2.roughness2 = max(brdfData2.roughness * brdfData2.roughness, HALF_MIN);;
    brdfData2.grazingTerm = saturate(smoothness2 + brdfData2.reflectivity);;
    brdfData2.normalizationTerm = brdfData2.roughness * 4.0h + 2.0h;;
    brdfData2.roughness2MinusOne = brdfData2.roughness2 - 1.0h;

    #ifndef _SPECULARHIGHLIGHTS_OFF
    [branch] if (!specularHighlightsOff)
    {
        // brdf += brdfData.specular * DirectBRDFSpecular(brdfData, normalWS, lightDirectionWS, viewDirectionWS);
        // ================================
        float specular = brdfData.specular * (
            DirectBRDFSpecular(brdfData, normalWS, lightDirectionWS, viewDirectionWS) / 2 +
            DirectBRDFSpecular(brdfData2, normalWS, lightDirectionWS, viewDirectionWS) * 0.1
            );
        // return specular;
        brdf += specular;
		// =======================================
        #if defined(_CLEARCOAT) || defined(_CLEARCOATMAP)
        // Clear coat evaluates the specular a second timw and has some common terms with the base specular.
        // We rely on the compiler to merge these and compute them only once.
        half brdfCoat = kDielectricSpec.r * DirectBRDFSpecular(brdfDataClearCoat, normalWS, lightDirectionWS, viewDirectionWS);

        // Mix clear coat and base layer using khronos glTF recommended formula
        // https://github.com/KhronosGroup/glTF/blob/master/extensions/2.0/Khronos/KHR_materials_clearcoat/README.md
        // Use NoV for direct too instead of LoH as an optimization (NoV is light invariant).
        half NoV = saturate(dot(normalWS, viewDirectionWS));
        // Use slightly simpler fresnelTerm (Pow4 vs Pow5) as a small optimization.
        // It is matching fresnel used in the GI/Env, so should produce a consistent clear coat blend (env vs. direct)
        half coatFresnel = kDielectricSpec.x + kDielectricSpec.a * Pow4(1.0 - NoV);

        brdf = brdf * (1.0 - clearCoatMask * coatFresnel) + brdfCoat * clearCoatMask;
        #endif // _CLEARCOAT
    }
    #endif // _SPECULARHIGHLIGHTS_OFF
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值