玉石透射效果

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,189评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,577评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,857评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,703评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,705评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,620评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,995评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,656评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,898评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,639评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,720评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,395评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,982评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,953评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,195评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,907评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,472评论 2 342

推荐阅读更多精彩内容

  • 转载自VR设计云课堂[https://www.jianshu.com/u/c7ffdc4b379e]Unity S...
    水月凡阅读 1,006评论 0 0
  • 111. [动画系统]如何将其他类型的动画转换成关键帧动画? 动画->点缓存->关键帧 112. [动画]Unit...
    胤醚貔貅阅读 12,948评论 3 90
  • 投射老公工作顺利,赚多多钱宝宝! 投射大宝喜欢学习,善于发现问题,自己动手动脑解决问题 投射二宝吃的香,睡得着,玩...
    廖飞凤吸引力法则实践者阅读 151评论 1 1
  • 纪录片|伪球迷与真球迷之间,只差一部《足球道路》的距离 原创:河西印象纪录昨天 2018俄罗斯世界杯已经结束了。除...
    印象纪录阅读 165评论 0 0
  • 奶萌奶萌的两个小生物,让心都融化了,主体突出,色调柔和,抓拍到位。 岩石占据了屏幕的前景,在大片的蓝...
    苑_50f5阅读 177评论 0 0