次表面散射之BTDF實(shí)現(xiàn)

本文主要用Translucent Shadow Maps 來(lái)實(shí)現(xiàn)投射效果
主要參考:GPU Gems
BRTF閱讀推薦:角色渲染技術(shù)blog
1.Transucent Shadow Maps 的應(yīng)用原理;
2.unity中實(shí)現(xiàn)的平行光投射
3.c#代碼/depthShader/objShader
1.投射的實(shí)現(xiàn)原理
當(dāng)一個(gè)物體為半透明時(shí),在物體較薄的地方,也會(huì)有光線穿過(guò)物體,這也就是所說(shuō)了BTDF部分。

為了描述投射光線的大小,我們應(yīng)考慮光線穿過(guò)物體的距離。
①用render texture 在光源空間的原點(diǎn)繪制一個(gè)攝像機(jī),使其記錄半透明物體的在光源空間的深度;
②在繪制物體的時(shí)候,計(jì)算片元的世界坐標(biāo)轉(zhuǎn)換到Light空間中,求出到原點(diǎn)的距離。再通過(guò)裁剪等變換取出對(duì)應(yīng)的Translucent Shadow Map中的深度值。將這兩個(gè)值進(jìn)行相減求出:光在物體中穿過(guò)的距離。
并用此值控制投射光的強(qiáng)度(以上不考慮光的折射)

2.unity中的實(shí)現(xiàn)
①render texture 中的攝像機(jī)調(diào)整
攝像機(jī)的rotator與平行光的方向一致;
確保裁剪空間完全包含要渲染的半透明物體;
將攝像機(jī) culling mask 與目標(biāo)物體的layer保持一致;
綜上寫個(gè)腳本方便調(diào)節(jié)

攝像機(jī)的渲染深度不適宜調(diào)整過(guò)大,會(huì)影響TSM中物體深度精準(zhǔn)度;
為了適宜大范圍多目標(biāo)的半透明物體渲染的項(xiàng)目
(1)分區(qū)域多增加Render Texture
(2)增加TSM貼圖的精度;
這里我使用unity 內(nèi)置的函數(shù)將 深度信息 編碼到32bit的RGBA中
//腳本中RenderTexture的聲明
depthTexture=new RenderTexture(depthCamera.pixelWidth,depthCamera.pixelHeight,8,RenderTextureFormat.ARGB32);
//shader中的編碼與解碼 在unityCG.cginc中定義
float4 depthRGBA = EncodeFloatRGBA(distance0_1);
float d_i=DecodeFloatRGBA(distanceColor);
②shader 中的算法(具體細(xì)節(jié)解釋請(qǐng)看GPU Gems)
(1)將算法改為平行光
因?yàn)闀r(shí)平行光,為了方便計(jì)算我們將near裁剪平面 設(shè)置為0;
將distance轉(zhuǎn)為depth,再 /far 的值 將距離轉(zhuǎn)為【0,1】;

之后就可以調(diào)用EncodeFloatRGBA(float)編碼了,編碼后顯示的圖片
當(dāng)你看見(jiàn)從你最近的地方圖像開始畫圈圈,說(shuō)明此步正確

(2)在繪制物體時(shí)對(duì)render texture進(jìn)行采樣
用腳本將lightCamera的viewMatrix和VPMatrix矩陣傳給 繪物體的shader
比較繞的地方:將用世界坐標(biāo)點(diǎn)求出 depthTexture 對(duì)應(yīng)的uv
裁剪空間的齊次變換后,xy分量的范圍是【-1,1】
可以通過(guò) /2.后再+0.5
也可以 +1.后再/2. 哈哈?。?br>
得到(0,1)之間
float4 texCoord =mul(_LightTexMatrix,i.worldPos);
float4 distanceColor=tex2D(_DistanceTex,((texCoord.xy/texCoord.w)/2)+0.5);
3.代碼
C#
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[ExecuteInEditMode]
public class btdfScript : MonoBehaviour
{
public Transform objTransform;
public Transform dirLightTransform;
private Transform depthCameraTransform;
public float armLength;
private Camera depthCamera;
private RenderTexture depthTexture;
public Shader drawDepthShader;
public Material mt;
void Start()
{
depthCameraTransform= GetComponent<Transform>();
depthCamera=GetComponent<Camera>();
depthCamera.enabled=false;
//depthCamera.clearFlags=CameraClearFlags.Nothing;
depthTexture=new RenderTexture(depthCamera.pixelWidth,depthCamera.pixelHeight,8,RenderTextureFormat.ARGB32);
//depthTexture.hideFlags = HideFlags.DontSave;
}
void Update()
{
depthCameraTransform.position = objTransform.position - dirLightTransform.forward * armLength ;
depthCameraTransform.LookAt(objTransform.position);
if(drawDepthShader){
//depthCamera.CopyFrom
depthTexture.Release();
depthCamera.targetTexture=depthTexture;
depthCamera.RenderWithShader(drawDepthShader,"");
//depthTexture.apply()
}
mt.SetTexture("_DistanceTex",depthTexture);
mt.SetMatrix("_LightMatrix" , depthCamera.worldToCameraMatrix);
mt.SetMatrix("_LightTexMatrix" , depthCamera.projectionMatrix * depthCamera.worldToCameraMatrix);
mt.SetFloat("_LightFarCP",depthCamera.farClipPlane);
}
}
depthShader
Shader "Unlit/DistanceShader"
{
SubShader
{
Tags { "RenderType"="Opaque" }
Pass
{
Cull back
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct a2v
{
float4 vertex : POSITION;
float3 normal: NORMAL;
};
struct v2f
{
float4 pos : SV_POSITION;
float3 viewPos:TEXCOORD1;
};
v2f vert (a2v v)
{
v2f o;
v.vertex.xyz += v.normal * 0.01;
o.pos = UnityObjectToClipPos(v.vertex);
o.viewPos= UnityObjectToViewPos(v.vertex);
return o;
}
float4 frag (v2f i) : SV_Target
{
float distance = length(i.viewPos);
float distance1 = distance *dot(normalize(-i.viewPos) , float3(0,0,1));
float distance0_1 = distance1/_ProjectionParams.z;
float4 depthRGBA = EncodeFloatRGBA(distance0_1);
//return fixed4(i.distance,i.distance,i.distance,1.0);
return depthRGBA;
}
ENDCG
}
}
}
objShader
// Upgrade NOTE: replaced '_Object2World' with 'unity_ObjectToWorld'
Shader "Unlit/BRTFshader"
{
Properties
{
_MainTex("MainTex",2D)="white"{}
_DiffuseColor("DiffuseColor",Color)=(1,1,1,1)
_SpecularColor("SpecularColor",Color)=(1,1,1,1)
_Shinness("Shinness",Range(0,300))=150
_Wrap("Wrap",Range(0,1))=0.5
_ScatterFactor("ScatterFactor",Range(0,1))=0.5
_DistanceTex ("DistanceTex", 2D) = "white" {}
_ssDistanceScale("ssDistanceScale",float)=1
_ssPow("ssPow",float)=1
}
SubShader
{
Tags { "RenderType"="Opaque" }
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
#include"UnityPBSLighting.cginc"
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
float3 normal:NORMAL;
};
struct v2f
{
float2 uv : TEXCOORD0;
float4 pos : SV_POSITION;
float4 worldPos:TEXCOORD1;
float3 worldNormal:TEXCOORD2;
};
//s
sampler2D _MainTex;
float4 _MainTex_ST;
fixed4 _DiffuseColor;
fixed4 _SpecularColor;
float _Shinness;
float _Wrap;
sampler2D _DistanceTex;
float4x4 _LightMatrix;
float4x4 _LightTexMatrix;
float _ssDistanceScale;
float _ssPow;
float _LightFarCP;
float _ScatterFactor;
v2f vert (appdata v)
{
v2f o;
o.pos= UnityObjectToClipPos(v.vertex);
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
o.worldPos=mul(unity_ObjectToWorld,v.vertex);
o.worldNormal=UnityObjectToWorldNormal(v.normal);
return o;
}
float trace(v2f i){
float4 texCoord =mul(_LightTexMatrix,i.worldPos);
float4 distanceColor=tex2D(_DistanceTex,((texCoord.xy/texCoord.w)/2)+0.5);
float d_i=DecodeFloatRGBA(distanceColor);
d_i = d_i *_LightFarCP;
float3 InLightPos=mul(_LightMatrix,i.worldPos).xyz;
float d_o = distance(InLightPos , float3(0,0,0));
d_o = d_o * dot(normalize(-InLightPos) , float3(0,0,1));
return d_o-d_i;
}
fixed4 frag (v2f i) : SV_Target
{
float traceDistance=trace(i);
// sample the texture
fixed3 scattering = pow(exp(-traceDistance*_ssDistanceScale) ,_ssPow) * _LightColor0.xyz ;
//data
float3 worldNormal=normalize(i.worldNormal);
float3 worldViewDir=normalize(UnityWorldSpaceViewDir(i.worldPos));
float3 worldLightDir=normalize(UnityWorldSpaceLightDir(i.worldPos));
//albedo
fixed3 albedo = tex2D(_MainTex, i.uv).xyz * _DiffuseColor.xyz;
//ambient
fixed3 ambient=UNITY_LIGHTMODEL_AMBIENT.xyz * albedo;
//specular
float3 halfDir=normalize(worldLightDir+worldViewDir);
//wrap
float wrap=(dot(worldLightDir,worldNormal) + _Wrap) / (1 + _Wrap);
wrap = max(0,wrap);
float wrapDiffuse=_LightColor0.xyz * wrap * albedo;
//specualr
float3 specualr = _LightColor0.xyz * _SpecularColor.xyz * pow(max(0,dot(worldNormal,worldLightDir)),_Shinness);
fixed3 color= lerp(ambient + wrapDiffuse , scattering , _ScatterFactor) + specualr;
return fixed4(color,1.0);
}
ENDCG
}
}
}