zoukankan      html  css  js  c++  java
  • Unity Water Shader

    上图是一个物体浸入水中的效果

    原理

      我们使用相机渲染的整个场景的深度图减去需要忽略的模型的深度,这里忽略的是图中蓝色部分,就保留了其他的深度值。

      用到Main Camera渲染的深度贴图: sampler2D _CameraDepthTexture; //在shader中声明,需要设置相机开启渲染深度图;深度图中记录的深度值:深度纹理中每个像素所记录的深度值是从0 到1 非线性分布的。精度通常是 24 位或16 位,这主要取决于所使用的深度缓冲区。当读取深度纹理时,我们可以得到一个0-1范围内的高精度值。如果你需要获取到达相机的距离或者其他线性关系的值,那么你需要手动计算它。

      在顶点着色器中获取顶点在屏幕空间的位置,用做采样深度图的uv坐标:

                    o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
                    o.scrPos = ComputeScreenPos(o.pos);//将返回片段着色器的屏幕位置
                    COMPUTE_EYEDEPTH(o.scrPos.z);//计算顶点摄像机空间的深度:距离裁剪平面的距离,线性变化;

      在像素着色器中采样Main Camera渲染的深度贴图(摄像机能看到的整个场景的深度图):

      

              //计算当前像素深度
                    float  depth= tex2Dproj(_CameraDepthTexture, UNITY_PROJ_COORD(i.scrPos)).r;//深度值 [0,1]
                    depth = LinearEyeDepth(depth);//深度根据相机的裁剪范围的值[0.3,1000],是将经过透视投影变换的深度值还原了

      将此图的值构建出灰度图,输出查看深度图效果: return float4(depth-999,depth-999,depth-999,1); 可以看出深度图中,没有被遮挡的部分并没有表现出线性渐变,说明区域的值被默认填充为最大的深度,我并没有找到相关的文件说明,有朋友找到,麻烦私信下。

      现在我们测试下顶点在屏幕空间的深度值(仅仅是计算需要计算的模型顶点的深度,与摄像机的深度图有区别),该值被顶点着色器记录在o.srcPos.z中,将值在像素着色器中输出,查看效果: return float4(i.scrPos.z,i.scrPos.z,i.scrPos.z,1); 

      (上图)想象沿着摄像机的方向,该值呈现出线性分布,说明是该点到近裁减面(以近裁剪面)的距离,假如摄像机裁剪空间是[0.3,10],那么该值在大于等于1的时候呈现出白色,在[0,1]区间内是灰度图。

       我们将顶点的深度值作为通道输出: return float4(i.scrPos.z,i.scrPos.z,i.scrPos.z,i.scrPos.z); 

    Tags {"Queue" = "Transparent"}
    Blend SrcAlpha OneMinusSrcAlpha 

    这个样子,我们就实现根据摄像机与顶点之间的深度值变化,实现与视角有关的透明效果,深度值在[0,1]范围内,则会透明,大于1则会呈现出材质颜色;

    现在我们将我们转化过的深度图深度值(线性分布)与顶点深度相减: depth -= i.scrPos.z; 

    为什么其他地方白色相减不等于黑色? 

      个人认为:在深度图中,没有被遮挡的地方被默认填充为最大深度1,转化为距离相机的线性距离时,也被转化为最大距离,例1000。这个值再减去模型的深度时,结果远大于模型深度,用颜色显示出来,即为白色。

    现在将深度值输出到像素着色器的rgba通道:查看效果: return float4(depth,depth,depth, depth); 与模型相交部分变成了透明。

    接下来,我们使用变量ColorDepth控制深度值,并增加2个颜色,来表现深度的范围:

        Properties
        {    _Color0("Water Color",Color) = (1,1,1,1)//水的颜色
            _Color1("Water Depth",Color) = (0,0,0,0)//水的深度的颜色
            _Alpha("Alpha",Range( 0,1))= 1//水面的正题透明度
            _ColorDepth("ColorDepth",Range( 0,1))= 0//水的深度
        }
                    //计算水的透明度: 使用深度值
                    float alpha = saturate( _Alpha*depth);
    
                    //计算颜色深度:
                    float colDepth = saturate(_ColorDepth*depth);
                    colDepth = 1-colDepth;
                    colDepth = lerp(colDepth, colDepth*colDepth*colDepth, 0.5);//调整深度,看个人喜好
    
                    half3 col;
                    col.rgb = lerp(_Color0.rgb, _Color1.rgb, colDepth);
    
                    return float4(col.rgb, alpha );

     

    在物体遮挡处,蓝色越深,表示越深,天蓝色,是海水颜色。现在除了物体遮挡处的深度表现正确,其他地方审定其实不正确。我们在水下加一块地皮:

    是不是有感觉了,我们再调节下颜色等参数:

    源代码:

    Shader "JQM/DepthTest_1"
    {
        Properties
        {    _Color0("Water Color",Color) = (1,1,1,1)//水的颜色
            _Color1("Water Depth",Color) = (0,0,0,0)//水的深度的颜色
            _Alpha("Alpha",Range( 0,1))= 1//水面的正题透明度
            _ColorDepth("ColorDepth",Range( 0,1))= 0//水的深度
        }
        SubShader
        {
            Tags {"Queue" = "Transparent"}
    
            Pass
            {
                Blend SrcAlpha OneMinusSrcAlpha 
                CGPROGRAM
                #pragma vertex vert
                #pragma fragment frag
                
                #include "UnityCG.cginc"
    
                struct appdata
                {
                    float4 vertex : POSITION;
                    float2 uv : TEXCOORD0;
                };
    
                struct VertexOutput
                {
                    float2 uv : TEXCOORD0;
                    float4 pos : SV_POSITION;
                    float4 scrPos : TEXCOORD1;
                };
    
    
                float4 _Color0;
                float4 _Color1;
                float _Alpha;//水的透明度
                float _ColorDepth;
    
                sampler2D _CameraDepthTexture;
                
                VertexOutput vert (appdata v)
                {
                    VertexOutput o;
    
                    o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
                    o.scrPos = ComputeScreenPos(o.pos);//将返回片段着色器的屏幕位置
                    COMPUTE_EYEDEPTH(o.scrPos.z);//计算顶点摄像机空间的深度:距离裁剪平面的距离
                    return o;
                }
                
                fixed4 frag (VertexOutput i) : COLOR
                {
                    //计算当前像素深度
                    float  depth= tex2Dproj(_CameraDepthTexture, UNITY_PROJ_COORD(i.scrPos)).r;//UNITY_PROJ_COORD:深度值 [0,1]
                    depth = LinearEyeDepth(depth);//深度根据相机的裁剪范围的值[0.3,1000],是将经过透视投影变换的深度值还原了
                    depth -= i.scrPos.z;
                    //计算水的透明度: 使用深度值
                    float alpha = saturate( _Alpha*depth);
    
                    //计算颜色深度:
                    float colDepth = saturate(_ColorDepth*depth);
                    colDepth = 1-colDepth;
                    colDepth = lerp(colDepth, colDepth*colDepth*colDepth, 0.5);//调整深度,看个人喜好
    
                    half3 col;
                    col.rgb = lerp(_Color0.rgb, _Color1.rgb, colDepth);
    
                    return float4(col.rgb, alpha );  
                }
                ENDCG
            }
        }
    }

    现在基本完成了物体浸入水中的效果:

    接下来,我还需要什么效果呢?

    1. UV偏移的动态水面;

    2. 反射;

    3. 折射;

    关于相机透明度问题

    深度增加,透明度降低。

  • 相关阅读:
    hdu5014——构造打表找规律
    HDU5124,线段树加离散化
    hdu 3400-三分套三分
    三分法——凸函数求极值问题
    Zoj 3811并查集
    iOS更新之DFU模式和恢复模式
    获取安卓系统版本
    (转)25个增强iOS应用程序性能的提示和技巧--高级篇
    (转)25个增强iOS应用程序性能的提示和技巧--中级篇
    (转)25个增强iOS应用程序性能的提示和技巧--初级篇
  • 原文地址:https://www.cnblogs.com/jqm304775992/p/4939977.html
Copyright © 2011-2022 走看看