[Unity] 一个简单的探照灯shader

查看:523 |回复:119 | 2024-10-12 21:26:55

您需要 登录 才可以下载或查看,没有账号?注册

x
本帖最后由 元素精灵 于 2018-9-27 17:54 编辑

原理
这个shader的原理很简单,就是用一个模型来模拟了一个体积光,当模型朝向摄像机时,增加亮度,并且让部分顶点做一点偏移来模拟bloom的感觉,这里我还加入了噪音让光线看起来变化更丰富些。


准备工作
首先需要准备一个用来模拟体积光的模型,网格如下图所示:


144942jnj0whlz66boet9w.jpg.thumb.jpg



其实就是一个圆形的平面,内圈用来模拟光源,外圈用来模拟体积光,我们刷入定点色来控制那些顶点是需要做变化的,如下图所示:


144958w1e1qt3uen0emuec.jpg.thumb.jpg


顶点法线要用来判断灯光朝向,全部垂直这个平面即可。


最后再准备一张灯光的贴图和噪音图

在unity中的实现
shader部分比较简单,直接上代码:

001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
091
092
093
094
095
096
097
098
099
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
Shader "Mya/GodRays"
{
        Properties
        {
                _Color("Color" , Color) = (1,1,1,1)
                _MainTex ("Texture", 2D) = "white" {}
                _Noise("Noise" , 2D) = "Black"{}
                _NoiseIntensity("Noise Intensity" , float) = 0.2
                _NoiseWave("Noise Wave" , vector) = (-1.0,2.0,2.0,-1.0)
                _MaxDistance("Distance Max" , float) = 20.0
                _MinDistance("Distance Min" , float) = 1.0
                _ViewAngleWeight("View Angle weight" , float) = 2
                _MaxBrightness("Max Brightness" , Range(0,8)) = 2
                _ViewClipRange("View SoftClip Range" , Range(0,1)) = 0.5
                _ViewClipNear("Clip Near" , float) = 0.3
        }
        SubShader
        {

                Tags { "Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent" }
                LOD 100
                Blend One One
                //Blend One OneMinusSrcColor
                   Cull Off
                Lighting Off
                ZWrite Off
                Fog { Color (0,0,0,0) }

                Pass
                {
                        CGPROGRAM
                        #pragma vertex vert
                        #pragma fragment frag

                        #include "UnityCG.cginc"

                        struct appdata
                        {
                                float4 vertex        : POSITION;
                                float2 uv                : TEXCOORD0;
                                fixed4 color        : COLOR;
                                half3 normal        :NORMAL;
                        };

                        struct v2f
                        {
                                float4 pos                : SV_POSITION;
                                float2 uv                : TEXCOORD0;
                                float4 uv_noise : TEXCOORD1;
                                float4 viewPos        : TEXCOORD2;
                                fixed4 color        : COLOR;
                        };

                        fixed4                _Color;

                        sampler2D        _MainTex;
                        float4                _MainTex_ST;

                        sampler2D        _Noise;
                        float4                _Noise_ST;
                        half4                _NoiseWave;
                        half                _NoiseIntensity;

                        half                _MaxDistance;
                        half                _MinDistance;
                        half                _ViewAngleWeight;
                        half                _MaxBrightness;
                        half                _ViewClipRange;
                        half                _ViewClipNear;



                        v2f vert (appdata v)
                        {
                                v2f o;


                                //摄像机空间体中心点的向量
                                float3 viewSpacecenterDir = -normalize(float3(UNITY_MATRIX_MV[0].w, UNITY_MATRIX_MV[1].w, UNITY_MATRIX_MV[2].w));
                                //摄像机空间法线
                                half3 viewSpaceNormalDir =  normalize(mul((float3x3)UNITY_MATRIX_MV,v.normal));
                                //法线与摄像机的夹角 VdotL
                                half viewAngle = viewSpaceNormalDir.z *0.5 + 0.5;
                                half powAngle = pow(viewAngle , _ViewAngleWeight);

                                //把顶点转换到摄像机空间
                                o.viewPos = mul(UNITY_MATRIX_MV , float4(v.vertex.xyz , 1));

                                //顶点偏移
                                o.viewPos.xyz += normalize(lerp( viewSpaceNormalDir , viewSpacecenterDir ,viewAngle ))* (_MinDistance + powAngle * _MaxDistance)  * v.color.a;

                                //转换到裁切空间
                                o.pos = mul(UNITY_MATRIX_P , o.viewPos );

                                //主纹理uv
                                o.uv = TRANSFORM_TEX(v.uv, _MainTex);

                                //噪音图uv
                                float2 noiseUV = TRANSFORM_TEX(v.uv, _Noise);
                                o.uv_noise.xy = noiseUV + _Time.x * _NoiseWave.xy;
                                o.uv_noise.zw = noiseUV * 0.7 + _Time.x * _NoiseWave.zw;

                                //用观察角度来控制亮度
                                o.color = powAngle * _MaxBrightness ;
                                o.color.a = 1-v.color.a;
                                return o;
                        }

                        fixed4 frag (v2f i) : SV_Target
                        {
                                //当前像素到摄像机的距离
                                half viewDistance = length(i.viewPos.xyz);
                                //摄像机软裁剪值
                                half viewSoftClip = saturate(viewDistance * _ViewClipRange - _ViewClipNear);

                                //采样噪音图
                                half noise = (tex2D(_Noise, i.uv_noise.xy) + tex2D(_Noise, i.uv_noise.zw) )* _NoiseIntensity;

                                //采样主纹理
                                fixed4 col = tex2D(_MainTex, i.uv) * _Color;

                                //对亮度进行扰动
                                col.rgb =  saturate((col.rgb - noise)  * i.color.rgb * i.color.a) ;

                                return col * viewSoftClip;
                        }
                        ENDCG
                }
        }
}




我这里是放在摄像机空间来计算,最初是单纯的按照法线方向来作顶点偏移的,后来是感觉不是很想光的感觉,所以加入了摄像机的方向来作插值,这样会有一点光线照向摄像机的感觉。



最后用顶点和摄像机的距离来做了一点过渡,防止模型和摄像机出现明显的穿插。


在unity中预览一下效果:
这里面很多计算都是全凭感觉...大家理解思路就好...

知乎@喵喵Mya

timg (2).jpg

评分

参与人数 3元素币 +14 活跃度 +8 展开 理由
lhcppy + 7 + 4 还让不让人休息了
monkeyhaha + 5 + 3 打开格局
介炮家龙 + 2 + 1 留下了没有技术含量的口水

查看全部评分

2024-10-12 21:26:55  
 赞 赞 5

使用道具 登录

119个回答,把该问题分享到群,邀请大神一起回答。
2#
回复 收起回复
2018-9-25 18:43:18   回复
 赞 赞 5

使用道具 登录

3#

资源甚好,发帖艰辛,且阅且珍惜!
回复 收起回复
2018-10-15 10:02:51   回复
 赞 赞 4

使用道具 登录

4#
这个光看着不错啊
回复 收起回复
2018-10-15 10:34:30   回复
 赞 赞 5

使用道具 登录

5#
这个光看着不错啊
回复 收起回复
2018-10-15 10:34:33   回复
 赞 赞 5

使用道具 登录

6#
       
这个光看着不错啊
回复 收起回复
2018-10-15 10:38:08   回复
 赞 赞 5

使用道具 登录

7#
不错的资源,谢谢分享
回复 收起回复
2018-10-15 10:42:08   回复
 赞 赞 5

使用道具 登录

8#
会代码真好!!!!
回复 收起回复
2018-10-15 11:01:28   回复
 赞 赞 4

使用道具 登录

9#
会代码真好!!!!
回复 收起回复
2018-10-15 11:01:30   回复
 赞 赞 5

使用道具 登录

10#
233333
回复 收起回复
2018-10-15 11:18:59   回复
 赞 赞 5

使用道具 登录

11#
个简单的探照灯sha
回复 收起回复
2018-10-15 11:30:58   回复
 赞 赞 4

使用道具 登录

12#
感谢分享
回复 收起回复
2018-10-15 11:39:11   回复
 赞 赞 3

使用道具 登录

13#
哇,不会代码,看着觉得高大上
回复 收起回复
2018-10-15 11:45:04   回复
 赞 赞 3

使用道具 登录

14#
强烈支持楼主ing……
回复 收起回复
2018-10-15 11:47:03   回复
 赞 赞 3

使用道具 登录

15#
从业不识微元素,做遍项目也枉然
回复 收起回复
2018-10-15 13:36:17   回复
 赞 赞 2

使用道具 登录

17#
好东西,收藏
回复 收起回复
2018-10-15 16:56:50   回复
 赞 赞 2

使用道具 登录

18#
感觉不错
回复 收起回复
2018-10-15 22:03:04   回复
 赞 赞 2

使用道具 登录

19#
好教程!感谢楼主分享!
回复 收起回复
2018-10-15 22:06:35   回复
 赞 赞 2

使用道具 登录

20#
从业不识微元素,做遍项目也枉然
回复 收起回复
2018-10-16 09:55:12   回复
 赞 赞 2

使用道具 登录

CG 游戏行业专业问题

Unity3D技术手机游戏引擎手游引擎
您需要登录后才可以回帖 登录 | 注册

本版积分规则

快速回复 返回顶部 返回列表