Jade ( Translucency / Subsurface Scattering ) Shader——玉石效果——SSS深度图实现

试验效果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,私信我哦!!!

  • 2
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值