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