unity shader——体积光

(本文纯属自创,难免有疏漏之处,还望各位大侠不吝指正。)

看了各种体积光的教程,尝试了做了一下,发现了很多BUG,其中有一个教材是添加了顶点位移功能,以节省GPU。但是移动之后整个模型全部散架了,而且其中的平方运算就执行了4次,而且原理作者也讲的很勉强。看完之后心情很糟糕。就自己造一个调节一下情绪。

********************

首先观察现实中的体积光:

1.固定的形状;

2.半透明

3.存在渐变特性,

4.观察距离足够近后完全透明;

**************

两条路:

1是几何着色器;2是多边形加色彩。

unity内部不支持几何着色器,所以这条路不可取了;只能采用多边形+色彩了。

可以使用3dmax建立几个常用的模型:cube、cylinder等等。也可以在脚本中,使用mesh类建立模型。省事的方法肯定还是用3dmax了,哈哈^_^

**************

一、要实现这些特性,先考虑数据结构吧:

    float _FadeOutDistNear;    // 淡出的最近距离,小于该值则透明度为最小,不再变化
    float _FadeOutDistFar;    // 淡出最大距离,大于该值则透明度为最大,不再变化
    float _MaxAlpha;        // 最大透明度
    float _MinAlpha;        // 最小透明度
    
    float _DampX;            // X轴衰减系数
    float _DampY;            // Y轴衰减系数
    float _DampZ;            // Z轴衰减系数

我添加了注释,不难理解含义的。
二、核心功能部分

首先实现透明度渐变的效果,这个一般都是“手动”控制的,所以有了数据结构中的 _DampX、DampY、DampZ。(数据结构部分应该放在算法设计的过程中,这样看起来才合理。)

这部分我是在物体局部坐标系进行运算的。在顶点转换到世界坐标系前,以物体中心为原点。这样就大大减小了阻力了,,,只要模型形状大致规则,那不需要到世界坐标系中来回运算了。(所以建模的时候一定要小心了,中心尽量在一端面中心

数学公式也非常简单,我只用了一次方运算,如果想要实现更圆滑的效果,可以多平方几次。代码如下:

 1 float DampParam(float3 pos, float3 len, float minA, float maxA)
 2     {
 3         // 光线的基本衰减函数
 4         float att;
 5         att = 1 - pos.z*len.z;    // 以平方计衰减
 6         
 7         att *= (1 - abs(pos.x * len.x * len.x));
 8         att *= (1 - abs(pos.y * len.y * len.y));
 9         
10         return lerp( minA, maxA, att);
11     }

然后是根据观察点位置调节顶点透明度了,这个难度更小,直接上代码吧:

1 float DampViewParam(float dist, float nFade, float fFade, float minA, float maxA)
2     {
3         // 由于观察点位置引起的衰减
4         return ( lerp(nFade, fFade, dist) - nFade ) * (maxA-minA) / (fFade - nFade);
5     }


程序的最终运行结果:

技术分享

原谅我只截这么小的图,哈哈,最终作品出来之前,其他细节不能过多透露。^_^

完整版代码:

  1 Shader "Custom/godRay" {
  2 
  3 Properties {
  4     _MainTex ("Base texture", 2D) = "white" {}
  5     _MainColor ("Color", color) = (1,1,1,1)
  6     _FadeOutDistNear ("Near fadeout dist", float) = 3    
  7     _FadeOutDistFar ("Far fadeout dist", float) = 10000
  8     _MaxAlpha ("MaxAlpha", range(0, 1)) = 1
  9     _MinAlpha ("MinAlpha", range(0.001, 1)) = 0.01
 10     
 11     
 12     _DampX ("DampX", range(0, 6) ) = 0.3    // 体积光的衰减系数
 13     _DampY ("DampY", range(0, 6) ) = 0.3    // 
 14     _DampZ ("DampZ", float ) = 0.3    //
 15     
 16 }
 17 
 18     
 19 SubShader {
 20     Tags { "Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Opaque" }
 21     
 22     Blend One OneMinusSrcColor
 23     Cull Off Lighting Off ZWrite Off Fog { Color (0,0,0,0) }
 24     
 25     LOD 100
 26     
 27     
 28     CGINCLUDE
 29     #include "UnityCG.cginc"
 30     sampler2D _MainTex;
 31     fixed4 _MainColor;
 32     float _FadeOutDistNear;    // 淡出的最近距离,小于该值则透明度为最小,不再变化
 33     float _FadeOutDistFar;    // 淡出最大距离,大于该值则透明度为最大,不再变化
 34     float _MaxAlpha;        // 最大透明度
 35     float _MinAlpha;        // 最小透明度
 36     
 37     float _DampX;            // X轴衰减系数
 38     float _DampY;            // Y轴衰减系数
 39     float _DampZ;            // Z轴衰减系数
 40     
 41     struct v2f {
 42         float4    pos    : SV_POSITION;
 43         float2    uv        : TEXCOORD0;
 44         fixed4    color    : TEXCOORD1;
 45     };
 46     
 47     
 48     float DampParam(float3 pos, float3 len, float minA, float maxA)
 49     {
 50         // 光线的基本衰减函数
 51         float att;
 52         att = 1 - pos.z*len.z;    // 以平方计衰减
 53         
 54         att *= (1 - abs(pos.x * len.x * len.x));
 55         att *= (1 - abs(pos.y * len.y * len.y));
 56         
 57         return lerp( minA, maxA, att);
 58     }
 59     
 60     float DampViewParam(float dist, float nFade, float fFade, float minA, float maxA)
 61     {
 62         // 由于观察点位置引起的衰减
 63         return ( lerp(nFade, fFade, dist) - nFade ) * (maxA-minA) / (fFade - nFade);
 64     }
 65     
 66     
 67     v2f vert (appdata_full v)
 68     {
 69         v2f o;
 70         
 71         // 模型的局部空间
 72         float3 mPos = v.vertex.xyz;
 73         
 74         // gl坐标中,摄像机坐标始终是(0,0,0)
 75         // 所以顶点世界坐标的长度就是与摄像机的距离
 76         float3 viewPos    = mul(UNITY_MATRIX_MV,v.vertex).xyz;
 77         float dist        = length(viewPos);
 78         
 79         float3 len = float3(_DampX, _DampY, _DampZ);
 80         float att = DampParam(mPos, len, _MinAlpha, _MaxAlpha);
 81         
 82         att *= DampViewParam(dist, _FadeOutDistNear, _FadeOutDistFar, _MinAlpha, _MaxAlpha);
 83         o.color.a = att;
 84         
 85         o.uv        = v.texcoord.xy;
 86         o.pos    = mul(UNITY_MATRIX_MVP, v.vertex);
 87         
 88     //    o.color.rgb    = _MainColor;
 89         
 90         return o;
 91         
 92     }
 93     ENDCG
 94 
 95 
 96     Pass {
 97         CGPROGRAM
 98         #pragma vertex vert
 99         #pragma fragment frag
100         #pragma fragmentoption ARB_precision_hint_fastest
101         fixed4 frag (v2f i) : COLOR
102         {
103             fixed4 finalColor;
104             finalColor = tex2D (_MainTex, i.uv)* (i.color.a);
105             
106             return finalColor;
107         }
108         ENDCG 
109     }    
110 }
111 }

 


最后得比几句:

困死了,睡。

 

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。