玉石透射效果

次表面散射之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
        }
    }
}

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 轉(zhuǎn)載自VR設(shè)計(jì)云課堂[http://www.itdecent.cn/u/c7ffdc4b379e]Unity S...
    水月凡閱讀 1,166評(píng)論 0 0
  • 111. [動(dòng)畫系統(tǒng)]如何將其他類型的動(dòng)畫轉(zhuǎn)換成關(guān)鍵幀動(dòng)畫? 動(dòng)畫->點(diǎn)緩存->關(guān)鍵幀 112. [動(dòng)畫]Unit...
    胤醚貔貅閱讀 13,516評(píng)論 3 88
  • 投射老公工作順利,賺多多錢寶寶! 投射大寶喜歡學(xué)習(xí),善于發(fā)現(xiàn)問(wèn)題,自己動(dòng)手動(dòng)腦解決問(wèn)題 投射二寶吃的香,睡得著,玩...
    廖飛鳳吸引力法則實(shí)踐者閱讀 164評(píng)論 1 1
  • 紀(jì)錄片|偽球迷與真球迷之間,只差一部《足球道路》的距離 原創(chuàng):河西印象紀(jì)錄昨天 2018俄羅斯世界杯已經(jīng)結(jié)束了。除...
    印象紀(jì)錄閱讀 228評(píng)論 0 0
  • 奶萌奶萌的兩個(gè)小生物,讓心都融化了,主體突出,色調(diào)柔和,抓拍到位。 巖石占據(jù)了屏幕的前景,在大片的藍(lán)...
    苑_50f5閱讀 198評(píng)論 0 0

友情鏈接更多精彩內(nèi)容