试验效果1:
下面是最简化的shader:
Shader "RE/Jade" {
Properties {
_MainColor( "MainColor", Color ) = ( 1,1,1,1 )
_MinDist("MinDist", float ) = 0.01
_MaxDist( "MaxDist", float ) = 0.9
}
SubShader
{
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma target 3.0
#pragma multi_compile_fwdbase
#include "UnityCG.cginc"
#include "Autolight.cginc"
#include "UnityShadowLibrary.cginc"
struct VS_IN
{
float4 vertex : POSITION;
float4 color : COLOR;
float4 texcoord0 : TEXCOORD0;
float3 normal : NORMAL;
};
struct VS_OUT
{
float4 pos:SV_POSITION;
float4 Color : COLOR;
float2 Texcoord : TEXCOORD0;
float3 WorldPosition : TEXCOORD1;
};
uniform samplerCUBE _AmbientCube;
uniform float4x4 _LightViewProj;
uniform float4x4 _LightViewProjINV;
uniform float _MinDist;
uniform float _MaxDist;
uniform float4 _MainColor;
VS_OUT vert(VS_IN In)
{
VS_OUT Out;
float3 LocalPos = In.vertex.xyz;
float2 Texcoord = float2( In.texcoord0.x, In.texcoord0.y );
float3 WorldPosition = mul( unity_ObjectToWorld, float4(LocalPos,1) );
Out.pos = mul (UNITY_MATRIX_VP, float4( WorldPosition, 1) );
Out.Texcoord = In.texcoord0;
Out.Color = In.color;
Out.WorldPosition = WorldPosition;
return Out;
}
uniform sampler2D _CameraDepthTexture;
fixed4 frag(VS_OUT In) : SV_Target
{
float4 ProjectedLightPos = mul( _LightViewProj, float4( In.WorldPosition, 1.0 ) );
ProjectedLightPos.xyz /= ProjectedLightPos.w;
float4 ProjectedLightPos2 = ProjectedLightPos;
ProjectedLightPos.xy = ProjectedLightPos.xy * float2(0.5,0.5) + float2( 0.5, 0.5 );
float2 SampleDepthPos = ProjectedLightPos.xy;
float LightDepth = tex2D( _CameraDepthTexture, SampleDepthPos.xy ).x;
float4 LightEnterPos = mul( _LightViewProjINV, float4( ProjectedLightPos2.xy, LightDepth, 1.0 ) );
LightEnterPos.xyz /= LightEnterPos.w;
float3 PenetrationVector = LightEnterPos.xyz - In.WorldPosition;
float TravelDist = length( PenetrationVector );
float PenetrationWeight = (TravelDist - _MinDist) / (_MaxDist - _MinDist);
PenetrationWeight = 1.0 - PenetrationWeight;
float3 FinalColor = PenetrationWeight * _MainColor;
return fixed4( FinalColor.rgb, 1);
}
ENDCG
}
}
FallBack "Diffuse"
}
C#代码:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class RE_Translucency : MonoBehaviour
{
public void OnPreRender()
{
Camera C = this.GetComponent<Camera>();
if (C != null)
{
C.depthTextureMode = DepthTextureMode.Depth;
Matrix4x4 M = Matrix4x4.identity;
Matrix4x4 V = C.worldToCameraMatrix;
Matrix4x4 P = C.projectionMatrix;
Matrix4x4 MVP = P * V * M;
Matrix4x4 VP = P * V;
Shader.SetGlobalMatrix("_LightViewProj", MVP );
Shader.SetGlobalMatrix("_LightViewProjINV", MVP.inverse);
}
}
}
原项目中可以调节处这个效果:主要是加了一个环境贴图反射。详细可见原项目:
代码解释:
1、输入参数
struct VS_IN
{
float4 vertex : POSITION;
float4 color : COLOR;
float4 texcoord0 : TEXCOORD0;
float3 normal : NORMAL;
};
2、输出参数
struct VS_OUT
{
float4 pos:SV_POSITION;
float4 Color : COLOR;
float2 Texcoord : TEXCOORD0;
float3 WorldPosition : TEXCOORD1;
};
3、顶点着色器
VS_OUT vert(VS_IN In)
{
VS_OUT Out;
float3 LocalPos = In.vertex.xyz;
float2 Texcoord = float2( In.texcoord0.x, In.texcoord0.y );
float3 WorldPosition = mul( unity_ObjectToWorld, float4(LocalPos,1) );
Out.pos = mul (UNITY_MATRIX_VP, float4( WorldPosition, 1) );
Out.Texcoord = In.texcoord0;
Out.Color = In.color;
Out.WorldPosition = WorldPosition;
return Out;
}
4、片段着色器
分段解释1:
fixed4 frag(VS_OUT In) : SV_Target
{
float4 ProjectedLightPos = mul( _LightViewProj, float4( In.WorldPosition, 1.0 ) );
ProjectedLightPos.xyz /= ProjectedLightPos.w;
float4 ProjectedLightPos2 = ProjectedLightPos;
ProjectedLightPos.xy = ProjectedLightPos.xy * float2(0.5,0.5) + float2( 0.5, 0.5 );
float2 SampleDepthPos = ProjectedLightPos.xy;
float LightDepth = tex2D( _CameraDepthTexture, SampleDepthPos.xy ).x;
float4 LightEnterPos = mul( _LightViewProjINV, float4( ProjectedLightPos2.xy, LightDepth, 1.0 ) );
LightEnterPos.xyz /= LightEnterPos.w;
float3 PenetrationVector = LightEnterPos.xyz - In.WorldPosition;
float TravelDist = length( PenetrationVector );
float PenetrationWeight = (TravelDist - _MinDist) / (_MaxDist - _MinDist);
PenetrationWeight = 1.0 - PenetrationWeight;
float3 FinalColor = PenetrationWeight * _MainColor;
return fixed4( FinalColor.rgb, 1);
}
float4 ProjectedLightPos = mul( _LightViewProj, float4( In.WorldPosition, 1.0 ) );
将世界坐标位置,转换到光源位置的MVP矩阵中去。
ProjectedLightPos.xyz /= ProjectedLightPos.w;
除以第四维,得到NDC坐标
ProjectedLightPos.xy = ProjectedLightPos.xy * float2(0.5,0.5) + float2( 0.5, 0.5 );
除以2,加上0.5,转换到(0,1)区间
uv相同,使用采样到的深度,构建一个点,然后乘以_LightViewProjINV,得到当前uv相同的,离光源最近的点的世界坐标。
然后两个点的相减,得到一个差向量,最后求长度,得到TravelDist,这值是个非零的正值。
C#传入变量脚本没啥好解释的:
public void OnPreRender()
{
Camera C = this.GetComponent<Camera>();
if (C != null)
{
C.depthTextureMode = DepthTextureMode.Depth;
Matrix4x4 M = Matrix4x4.identity;
Matrix4x4 V = C.worldToCameraMatrix;
Matrix4x4 P = C.projectionMatrix;
Matrix4x4 MVP = P * V * M;
Matrix4x4 VP = P * V;
Shader.SetGlobalMatrix("_LightViewProj", MVP );
Shader.SetGlobalMatrix("_LightViewProjINV", MVP.inverse);
}
}
总结:
1、 C.depthTextureMode = DepthTextureMode.Depth;
开启使用摄像机的深度图。
2、原理,计算物体的厚度,厚度越薄就越透明。如何计算深度,首先是将从背后渲染一个深度图,本例子是直接使用背后的深度图,开启深度图模式得到,然后当从正面渲染的时候,将当前渲染的点,转换到背后的摄像机,得到其在背后相机中的位置;然后用这个位置的xy采样深度图,还原一个深度,两个深度相减即得到物体的厚度。用这个厚度作为权重,缩放主颜色,即得到上面简单的效果,如果还要加一个环境反射效果,可以采样一个环境cube贴图,两者叠加即可得到图二的带有镜面反射的效果。
3、这个例子的难点是,一个是深度图的使用;二个是坐标的转换;三个是最远和最近距离的调节,使其得到理想的效果。
4、扩展,关于如何得到背面的深度图,还可以用一个单独的shader,使用rt的方法,将其传入到shader中,这个也是一个方法,本节是最简单的使用untiy默认提供的设置DepthTextureMode即可获得深度图。
5、必须有FallBack “Diffuse” 才能有深度图,暂且这么解释。
总上,本节用了物体的厚度模拟了sss的效果,当然其他的方法我们将会逐步的深入学习,待补充。
链接:https://pan.baidu.com/s/1BSKWQm7hcVEe3ExAPOMQaw
想要知道提取码:请关注me,私信我哦!!!