您需要 登录 才可以下载或查看,没有账号?注册
x
本帖最后由 元素精灵 于 2018-9-27 17:54 编辑
原理这个shader的原理很简单,就是用一个模型来模拟了一个体积光,当模型朝向摄像机时,增加亮度,并且让部分顶点做一点偏移来模拟bloom的感觉,这里我还加入了噪音让光线看起来变化更丰富些。
准备工作首先需要准备一个用来模拟体积光的模型,网格如下图所示:
其实就是一个圆形的平面,内圈用来模拟光源,外圈用来模拟体积光,我们刷入定点色来控制那些顶点是需要做变化的,如下图所示:
顶点法线要用来判断灯光朝向,全部垂直这个平面即可。
最后再准备一张灯光的贴图和噪音图
在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
|