玉石透射效果

作者: 万里_aa3f | 来源:发表于2019-01-25 18:03 被阅读4次

    次表面散射之BTDF实现


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

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

    1.投射的实现原理

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



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


    2.unity中的实现

    ①render texture 中的摄像机调整

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


    摄像机的渲染深度不适宜调整过大,会影响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】;



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


    (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<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
            }
        }
    }
    
    

    相关文章

      网友评论

        本文标题:玉石透射效果

        本文链接:https://www.haomeiwen.com/subject/safvjqtx.html