本文效果图:
次表面散射(SSS)的概念自行百度,表现光进入物体,然后发生内部散射,最终从不同的位置射出,主要用于模拟玉石,翡翠,蜡烛,皮肤等有一点点透光的物体。常规的漫反射计算公式为:
float diffuse=max(0,dot(L,N));
《GPU GERM》中对使用环绕光照的方式模拟简单次表面散射,使原来为黑的地方有一点点光,公式为:
float wrap_diffuse=max(0,(dot(L,N)+wrap)/(1+wrap)); L 光照方向, N 表面法线,wrap为可变换参数
然后就是计算物体的半透明度,这里的透明只是模拟颜色的淡化,而非真的透明。物体半透的主要因素是光在物体中穿过的时候会逐渐丢失能量,穿过的物体越厚,越不透明,模拟半透主要问题在于获取光穿过物体距离。有两种计算方式:计算当前视角下场景的深度,然后以光的位置和方向做为摄像机位置和朝向渲染场景,获得光位置处场景的深度图,最后将光位置处深度信息映射到当前视角下,获得当前视角下光穿过物体的厚度;先渲染当前视角的深度信息,然后渲染当前视角背面的深度信息,获得物体当前视角下的厚度(这里面获取深度信息的原理,在另一篇说)。
首先采用前后摄像机的方式计算物体厚度,根据深度渲染模型的代码如下:
Shader "Custom/JadeRender2"
{
Properties{
_Base("Base", Color) = (1, 1, 1, 1)
_Diffuse("Diffuse", Color) = (1, 1, 1, 1)
_Specular("Specular",Color) = (1.0,1.0,1.0,1.0)
_Shinness("Shinness",Range(8,256)) = 128
_Wrap("Wrap",Range(0,1)) = 0.5
_ScatterWidth("_ScatterWidth",Vector) = (0,0,0,0)
_ScatterFactor("_ScatterFactor",Range(0,1)) = 0.75
}
SubShader{
Tags{ "RenderType" = "Opaque" "Queue" = "Geometry" }
Pass{
Tags{ "LightMode" = "ForwardBase" }
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile_fwdbase
#include "Lighting.cginc"
fixed4 _Diffuse;
float _Wrap;
float4 _ScatterWidth;
float4 _Base;
float _ScatterFactor;
float4 _Specular;
float _Shinness;
struct a2v {
float4 vertex : POSITION;
float3 normal : NORMAL;
float4 tangent:TANGENT;
float2 texcoord:TEXCOORD0;
};
struct v2f {
float4 pos : SV_POSITION;
float3 wNormal : TEXCOORD0;
float4 wPos:TEXCOORD1;
float4 uv:TEXCOORD2;
float4 scrPos:TEXCOORD3;
};
sampler2D _BackDepthTex;
float4x4 _WolrdtoLightMatrix;
float4x4 _LightTexMatrix;
float _CamNearPlane;
float _CamFarPlane;
v2f vert(a2v v) {
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.wNormal = mul(v.normal, (float3x3)unity_WorldToObject);
o.scrPos = ComputeScreenPos(o.pos);
o.wPos = mul(unity_ObjectToWorld,v.vertex);
return o;
}
fixed4 frag(v2f i) : SV_Target{
float d_o = mul(_WolrdtoLightMatrix,i.wPos).z;
d_o = 1-(-d_o - _CamNearPlane) / (_CamFarPlane - _CamNearPlane);
float4 tpos = mul(_LightTexMatrix,i.wPos);
float4 scrPos = tpos * 0.5f;
scrPos.xy = float2(scrPos.x, scrPos.y * 1) + scrPos.w;
scrPos.zw = tpos.zw;
float4 backDepthColor = tex2D(_BackDepthTex, i.scrPos.xy / i.scrPos.w); // SAMPLE_DEPTH_TEXTURE(_BackDepthTex, UNITY_PROJ_COORD(scrPos)); //tex2D(_BackDepthTex, i.scrPos.xy / i.scrPos.w); //
float d_i = -(backDepthColor*(_CamFarPlane - _CamNearPlane) + _CamNearPlane);
d_i = backDepthColor.r;
float depth = d_o - d_i;
float3 scattering = exp(-_ScatterWidth.xyz*depth*10);
float3 N = normalize(i.wNormal);
float4 texcol = float4(1.0,1.0,1.0,1.0);
fixed3 albedo = texcol.rgb*_Diffuse.rgb;
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz*albedo;
float3 L = normalize(UnityWorldSpaceLightDir(i.wPos));
float3 V = normalize(UnityWorldSpaceViewDir(i.wPos));
float3 H = normalize(L + V);
float wrap = (dot(L,N) + _Wrap) / (1 + _Wrap);
float wrap_diff = max(0,wrap);
fixed3 diffuse = _LightColor0.rgb * wrap_diff*albedo;
float s = pow(max(0,dot(N,H)),_Shinness);
float3 specular = _LightColor0.rgb *_Specular.rgb*s;
float4 finCol = float4(0,0,0,0);
finCol.rgb = _Base*(ambient + diffuse)*scattering+ specular;
finCol.a = texcol.a;
return finCol;
}
ENDCG
}
}
// FallBack "Diffuse"
}
源码地址:
https://download.csdn.net/download/wang371372/10775296
参考两位大佬的文章
https://blog.csdn.net/z18636930051/article/details/75105422
http://gad.qq.com/article/detail/19080