玉石透射效果

次表面散射之BTDF实现

玉石透射效果_第1张图片

本文主要用Translucent Shadow Maps 来实现投射效果
主要参考: GPU Gems
BRTF阅读推荐: 角色渲染技术blog

1.Transucent Shadow Maps 的应用原理;
2.unity中实现的平行光投射
3.c#代码/depthShader/objShader

1.投射的实现原理

当一个物体为半透明时,在物体较薄的地方,也会有光线穿过物体,这也就是所说了BTDF部分。


玉石透射效果_第2张图片

为了描述投射光线的大小,我们应考虑光线穿过物体的距离。
①用render texture 在光源空间的原点绘制一个摄像机,使其记录半透明物体的在光源空间的深度;
②在绘制物体的时候,计算片元的世界坐标转换到Light空间中,求出到原点的距离。再通过裁剪等变换取出对应的Translucent Shadow Map中的深度值。将这两个值进行相减求出:光在物体中穿过的距离。
并用此值控制投射光的强度(以上不考虑光的折射)


玉石透射效果_第3张图片

2.unity中的实现

①render texture 中的摄像机调整

摄像机的rotator与平行光的方向一致;
确保裁剪空间完全包含要渲染的半透明物体;
将摄像机 culling mask 与目标物体的layer保持一致;
综上写个脚本方便调节


玉石透射效果_第4张图片

摄像机的渲染深度不适宜调整过大,会影响TSM中物体深度精准度;
为了适宜大范围多目标的半透明物体渲染的项目
(1)分区域多增加Render Texture
(2)增加TSM贴图的精度;
这里我使用unity 内置的函数将 深度信息 编码到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 中的算法(具体细节解释请看GPU Gems)

(1)将算法改为平行光
因为时平行光,为了方便计算我们将near裁剪平面 设置为0;
将distance转为depth,再 /far 的值 将距离转为【0,1】;


玉石透射效果_第5张图片

之后就可以调用EncodeFloatRGBA(float)编码了,编码后显示的图片
当你看见从你最近的地方图像开始画圈圈,说明此步正确


玉石透射效果_第6张图片

(2)在绘制物体时对render texture进行采样
用脚本将lightCamera的viewMatrix和VPMatrix矩阵传给 绘物体的shader
比较绕的地方:将用世界坐标点求出 depthTexture 对应的uv
裁剪空间的齐次变换后,xy分量的范围是【-1,1】
可以通过 /2.后再+0.5
也可以 +1.后再/2. 哈哈!!
得到(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();
        
        depthCamera=GetComponent();
        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
        }
    }
}

你可能感兴趣的:(玉石透射效果)