ShaderLab新手宝典 时间:2021年11月30日09:12:27
本笔记仅作为《Shader入门精要》的部分要点回顾和补充,重点放在表面着色器和ASE的使用上
Ch7.透明效果 P125 模板测试
不管通过还是未通过测试,在测试结束之后都可以对缓存中的模板值做操作
语法:
1 2 3 4 5 6 7 8 9 10 Stencil { Ref referenceValue ReadMask readMask WriteMask writeMask Comp comparisonFunction Pass stencilOperation Fail stencilOperation ZFail stencilOperation }
Ch8.表面着色器的基础概念 P134 组织结构 表面着色器编译指令的语法结构为:
#pragma surface surfaceFunction lightModel [optionalparams]
1)surface:声明所使用的Shader是表面着色器
2)surfaceFunction:声明表面着色器的函数名称,被称为表面函数,一般使用surf作为表面函数的名称
3)lightModel:声明所使用的的光照模型。Unity内置四种光照模型,分别为:
非物理光照模型:Lambert和BlinnPhong
物理光照模型:Standard和StandardSpecular
4)[optionalparams]:其他可选参数
P134 编译指令中的可选参数 自行查询书籍了解
P137 表面函数的语法结构 1 2 3 4 void surf (Input IN, inout SurfaceOutput o) { }
1.表面函数输入结构体 使用者可以通过下表中的变量直接获取到相关数据,然后传入表面函数中进行计算
变量
说明
float2 uv_texName、float2 uv2_texName
uv关键词后接纹理名称,获取模型某套uv坐标
float3 viewDir
摄像机视角方向(世界空间),没有被标准化
使用COLOR语义定义的float4变量
插值后的逐顶点颜色
float4 screenPos
屏幕空间坐标,可用于反射或屏幕空间特效,但不适用于GrabPass,需要使用ComputeGrabScreenPos函数单独计算uv
float3 worldPos
世界空间坐标
float3 worldRefl
世界空间反射向量,前提是没有修改表面法线 o.Normal
float3 worldNormal
世界空间法线线路,前提是没有修改表面法线 o.Normal
float3 worldRefl; INTERNAL_DATA
如果表面法线 o.Normal 进行了修改,在表面函数中通过WorldReflectionVector(IN, o.Normal)得到基于法线贴图的世界空间反射向量
float3 worldNormal; INTERNAL_DATA
如果表面法线 o.Normal 进行了修改,在表面函数中通过 WorldNormalVector(IN, o.Normal)得到基于法线贴图的世界空间法线向量
2.表面函数输出结构体 Lambert和BlinnPhong使用SurfaceOutput结构体输出
1 2 3 4 5 6 7 8 9 struct SurfaceOutput { fixed3 Albedo; fixed3 Normal; fixed3 Emission; half Specular; fixed Gloss; fixed Alpha; };
金属工作流输出结构体
1 2 3 4 5 6 7 8 9 10 struct SurfaceOutputStandard { fixed3 Albedo; fixed3 Normal; fixed3 Emission; half Metallic; half Smoothness; half Occlusion; fixed Alpha; };
高光工作流输出结构体
1 2 3 4 5 6 7 8 9 10 struct SurfaceOutputStandard { fixed3 Albedo; fixed3 Normal; fixed3 Emission; half Smoothness; half Occlusion; fixed Alpha; };
Ch9.编写表面着色器 P141 使用法线贴图 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 Shader "Surface Shader/Normal Map" { Properties { _MainTex ("MainTex" , 2 D) = "white" {} _Color ("Color" , Color) = (1 ,1 ,1 ,1 ) _Normal ("Normal Map" , 2 D) = (1 ,1 ,1 ,1 ) _Bumpiness ("Bumpiness" , Range(0 , 1 )) = 0 } SubShader { CGPROGRAM #pragma surface surf Lambert struct Input { float2 uv_MainTex; float2 uv_Normal; }; sampler2D _MainTex; fixed4 _Color; sampler2D _Normal; fixed _Bumpiness; void surf (Input IN, inout SurfaceOutput o) { fixed4 c = tex2D(_MainTex, IN.uv_MainTex) * _Color; o.Albedo = c.rgb; fixed3 n = UnpackNormal(tex2D(_Normal, IN.uv_Normal)); n *= float3(_Bumpiness, _Bumpiness, 1 ); o.Normal = n; } ENDCG } FallBack "Diffuse" }
P146 使用顶点修改函数 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 Shader "Surface Shader/Vertex Modify" { Properties { _MainTex ("MainTex" , 2 D) = "white" {} _Expansion ("Expansion" , Range(0 , 0.1 )) = 0 } SubShader { CGPROGRAM #pragma surface surf Lambert vertex:vert struct Input { float2 uv_MainTex; }; sampler2D _MainTex; fixed _Expansion; void vert (inout appdata_full v) { v.vertex.xyz += v.normal * _Expansion; } void surf (Input IN, inout SurfaceOutput o) { fixed4 c = tex2D(_MainTex, IN.uv_MainTex) * _Color; o.Albedo = c.rgb; } ENDCG } FallBack "Diffuse" }
P148 自定义光照函数 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 Shader "Surface Shader/Custom Lambert" { Properties { _MainTex ("MainTex" , 2 D) = "white" {} } SubShader { CGPROGRAM #pragma surface surf CustomLambert struct Input { float2 uv_MainTex; }; sampler2D _MainTex; void surf (Input IN, inout SurfaceOutput o) { fixed4 c = tex2D(_MainTex, IN.uv_MainTex) * _Color; o.Albedo = c.rgb; } half4 LightingCustomLambert (SurfaceOutput s, half3 lightDir, half atten) { fixed NdotL = saturate(dot(s.Normal, lightDir)); half4 c; c.rgb = s.Albedo * _LightColor0 * NdotL * atten; c.a = s.Alpha; return c; } ENDCG } FallBack "Diffuse" }
P149 最终颜色修改函数 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 Shader "Surface Shader/Final Color Modify" { Properties { _MainTex ("MainTex" , 2 D) = "white" {} _ColorTint ("Color Tint" , Color) = (1 ,1 ,1 ,1 ) } SubShader { CGPROGRAM #pragma surface surf Lambert finalcolor:ModifyColor struct Input { float2 uv_MainTex; }; sampler2D _MainTex; fixed4 _ColorTint; void surf (Input IN, inout SurfaceOutput o) { fixed4 c = tex2D(_MainTex, IN.uv_MainTex) * _Color; o.Albedo = c.rgb; } void ModifyColor (Input IN, SurfaceOutput o, inout fixed4 color) { color *= _ColorTint; } ENDCG } FallBack "Diffuse" }
P151 使用曲面细分函数 曲线细分语法:
添加编译指令:tessellate:FunctionName
1 2 3 4 float4 FunctionName () { return tess; }
固定数量的曲面细分 1 2 3 4 5 6 7 8 9 #pragma surface surf Lambert tesellate:tessellation vertex:height addshadow half _Tessellation; float4 tessellation () { return _Tessellation; }
基于边长的曲面细分 会根据不同长度的边分别对应不同的曲面细分等级,边越长细分等级越高,从而使模型的边长在屏幕上看起来一致
1 2 3 4 5 6 7 8 9 10 #pragma surface surf Lambert tesellate:tessellateEdge vertex:height addshadow half _EdgeLength; float4 tessellateEdge (appdata_full v0, appdata_full v1, appdata_full v2) { return UnityEdgeLengthBasedTess(v0.vertex, v1.vertex, v2.vertex, _EdgeLength); }
视锥剔除曲面细分 在基于边长的基础上,额外判断顶点是否在摄像机的视锥体内,超出视锥体范围的顶点将会被剔除
1 2 3 4 5 6 7 8 9 10 11 #pragma surface surf Lambert tesellate:tessellateCull vertex:height addshadow half _EdgeLength; float _MaxHeight; float4 tessellateCull (appdata_full v0, appdata_full v1, appdata_full v2) { return UnityEdgeLengthBasedTessCull(v0.vertex, v1.vertex, v2.vertex, _EdgeLength, _MaxHeight); }
基于距离的曲面细分
d < minDist,细分等级为tess
minDist < d < maxDist:逐渐降低
d > maxDist:细分等级保持为1
1 2 3 4 5 6 7 8 9 10 11 12 #pragma surface surf Lambert tesellate:tessellateDistance vertex:height addshadow half _MinDistance; half _MaxDistance; half _Tessellation; float4 tessellateDistance (appdata_full v0, appdata_full v1, appdata_full v2) { return UnityDistanceBasedTess(v0.vertex, v1.vertex, v2.vertex, _MinDistance, _MaxDistance, _Tessellation); }
Phong 曲面细分 Phong细分会使新生成的顶点沿着原始顶点的平均法线方向偏移一段距离,使模型表面变得更加光滑
Phong曲面细分单独使用是没有效果的,需要结合曲面细分函数才会生效
1 2 3 4 5 6 7 8 9 10 #pragma surface surf Lambert tesellate:tessellation tessphong:_Phong half _Tessellation; fixed _Phong; float4 tessellation () { return _Tessellation; }
P164 透明度混合 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 Shader "Surface Shader/Transparent" { Properties { _MainTex ("MainTex" , 2 D) = "white" {} _Color ("Color(RGB-A)" , Color) = (1 ,1 ,1 ,1 ) } SubShader { Tags {"Queue" = "Transparent" } CGPROGRAM #pragma surface surf Lambert alpha struct Input { float2 uv_MainTex; }; sampler2D _MainTex; fixed4 Color; void surf (Input IN, inout SurfaceOutput o) { fixed4 c = tex2D(_MainTex, IN.uv_MainTex) * _Color; o.Albedo = c.rgb; o.Alpha = c.a * _Color.a; } ENDCG } FallBack "Diffuse" }
P166 透明度测试 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 Shader "Surface Shader/Transparent" { Properties { _MainTex ("MainTex" , 2 D) = "white" {} _AlphaTest ("Alpha Test" , Range(0 ,1 )) = 0 } SubShader { Tags {"Queue" = "AlphaTest" } CGPROGRAM #pragma surface surf Lambert alphatest:_AlphaTest addshadow struct Input { float2 uv_MainTex; }; sampler2D _MainTex; void surf (Input IN, inout SurfaceOutput o) { fixed4 c = tex2D(_MainTex, IN.uv_MainTex) * _Color; o.Albedo = c.rgb; o.Alpha = c.a * _Color.a; } ENDCG } FallBack "Diffuse" }
Ch10.Image Effect P168 GrabPass GrabPass可以获取模型对应的屏幕纹理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 Shader "Custom/GrabPass" { Properties { _GrayScale ("Gray Scale" , Range(0 , 1 )) = 0 } SubShader { Tags {"Queue" = "Transparent" } GrabPass {"_ScreenTex" } Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag #include "UnityCG.cginc" struct v2f { float4 pos : SV_POSITION; float4 grabPos : TEXCOORD0; }; v2f vert (float4 vertex : POSITION) { v2f o; o.pos = UnityObjectToClipPos(vertex); o.grabPos = ComputeGrabScreenPos(o.pos); return o; } fixed _GrayScale; sampler2D _ScreenTex; half4 frag (v2f i) : SV_TARGET { half4 src = tex2Dproj(_ScreenTex, i.grabPos); half grayscale = Luminance(src.rgb); half4 dst = half4(grayscale, grayscale, grayscale, 1 ); return lerp(src, dst, _GrayScale); } } } }
P187 自定义后处理堆栈 Shader 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 Shader "Hidden/BSC - HLSL" { HLSLINCLUDE #include "Packages/com.unity.postprocessing/PostProcessing/Shaders/StdLib.hlsl" TEXTURE2D_SAMPLER2D(_MainTex, sampler_MainTex); half _Brightness; half _Saturation; half _Contrast; float4 Frag (VaryingsDefault i) : SV_TARGET { float4 color = SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, i.texcoord); color.rgb *= _Brightness; float luminance = dot(color.rgb, float3(0.2126729 , 0.7151522 , 0.0721750 )); color.rgb = lerp(luminance, color.rgb, _Saturation); half3 grayColor = half3(0.5 , 0.5 , 0.5 ); color.rgb = lerp(grayColor, color.rgb, _Contrast); return color; } ENDHLSL SubShader { Cull Off ZWrite Off ZTest Always Pass { HLSLPROGRAM #pragma vertex VertDefault #pragma fragment Frag ENDHLSL } } }
C#脚本 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 using System;using UnityEngine;using UnityEngine.Rendering.PostProcessing;[Serializable ] [PostProcess(typeof(BSCRenderer), PostProcessEvent.AfterStack, "Custom/BSC" ) ] public sealed class BSC : PostProcessEffectSettings { [Range(0f, 2f), Tooltip("Brightness effect intensity." ) ] public FloatParameter Brightness = new FloatParameter { value = 1f }; [Range(0f, 2f), Tooltip("Saturation effect intensity." ) ] public FloatParameter Saturation = new FloatParameter { value = 1f }; [Range(0f, 2f), Tooltip("Contrast effect intensity." ) ] public FloatParameter Contrast = new FloatParameter { value = 1f }; } public sealed class BSCRenderer : PostProcessEffectRenderer <BSC >{ public override void Render (PostProcessRenderContext context ) { var sheet = context.propertySheets.Get(Shader.Find("Hidden/BSC-HLSL" )); sheet.properties.SetFloat("_Brightness" , settings.Brightness); sheet.properties.SetFloat("_Saturation" , settings.Saturation); sheet.properties.SetFloat("_Contrast" , settings.Contrast); context.command.BlitFullscreenTriangle(context.source, context.destination, sheet, 0 ); } }
Setting中常用变量类型
类型
描述
FloatParameter
浮点型
BoolParameter
布尔型
ColorParameter
颜色
Vector2Parameter
二维向量
Vector3Parameter
三维向量
Vector4Parameter
四维向量
TextureParameter
纹理贴图,常用的默认值有:None、Black、White、Transparent
最终效果
Ch11.自定义材质面板 P197 Toggle 默认关键词
例句:[Toggle] _Invert color?", Float) = 0
以上例句会被Unity默认设置为_INVERT_ON
自定义关键词
例句:[Toggle(ENABLE_FANCY)] _Fancy ("Fancy?", Float) = 0
P197 Enum 内置枚举类型
例句:[Enum(UnityEngine.Rendering.BlendMode)] _Blend ("Blend mode", Float) = 1
自定义枚举类型
一个枚举最多只能自定义7个名称/数值对
例句:[Enum(Off, 0, On, 1)] _ZWrite ("ZWrite", Float) = 0
P198 KeywordEnum 和Enum类似,只不过关键词枚举会有与之对应的Shader关键词
例句:[KeywordEnum(None, Add, Multiply)] _Overlay ("Overlay mode", Float) = 0
这三个选项对应的Shader关键词分别为:_OVERLAY_NONE
、_OVERLAY_ADD
和_OVERLAY_MULTIPLY
P198 编译指令定义关键词 定义了ToggleDrawer或KeywordEnumDrawer之后,如果想要正常使用,还需要在编译指令中声明Shader关键词
两种编译指令,不同关键词之间用空格隔开
shader_feature
:只会为材质使用到的关键词生成变体,没有使用到的关键词不会生成变体,因此无法在运行的时候通过脚本切换效果
例如,#pragma shader_feature _OVERLAY_NONE _OVERLAY_ADD _OVERLAY_MULTIPLY
例如,#pragma shader_feature _INVERT_ON
multi_compile
:会为所有关键词生成变体,因此可以在运行的时候通过脚本切换效果
例如,#pragma multi_compile _OVERLAY_NONE _OVERLAY_ADD _OVERLAY_MULTIPLY
P199 PowerSlider 指数滑动条
例子:[PowerSlider(3.0)] _Brightness ("Birghtness", Range(0.01, 1)) = 0.1
P200 IntRange 整数滑动条
例子:[IntRange] _Alpha ("Alpha", Range(0, 255)) = 100
Ch13.初级案例 P228 1.流光效果 原理:通过引入_Time对uv坐标进行偏移,实现流光效果
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 Shader "Samples/Light Flow" { Properties { _Tex("Texture" , 2 D) = "white" {} _Color("Color" , Color) = (0 , 1 , 1 , 1 ) [KeywordEnum (X,Y)] _DIRECTION("Flow Direction" , float ) = 0 _Speed("Flow Speed" , float ) = 1 } SubShader { Tags {"RenderType" = "Transparent" "Queue" = "Transparent" } Blend One One Cull Off Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag #pragma shader_feature _DIRECTION_X _DIRECTION_Y #include "unityCG.cginc" struct v2f { float2 texcoord : TEXCOORD0; float4 vertex : SV_POSITION; }; sampler2D _Tex; float4 _Tex_ST; fixed4 _Color; float _Speed; v2f vert (appdata_base v) { v2f o; o.texcoord = TRANSFORM_TEX (v.texcoord, _Tex); o.vertex = UnityObjectToClipPos (v.vertex); return o; } float4 frag (v2f i) : SV_Target { float2 texcoord; #if _DIRECTION_X texcoord = float2 (i.texcoord.x + _Time.x * _Speed, i.texcoord.y); #elif _DIRECTION_Y texcoord = float2 (i.texcoord.x, i.texcoord.y + _Time.x * _Speed); #endif return tex2D (_Tex, texcoord) * _Color; } ENDCG } } }
P234 2.描边效果 原理:通过两个Pass,第一个Pass关闭深度写入,绘制一遍沿法线扩张的描边色模型;第二个Pass正常绘制。由于第一个Pass关闭了深度写入,如果渲染队列为Geometry,可能会导致在模型身后的几何体将描边颜色覆盖住,进而产生错误的效果,因此将整个Shader的渲染队列延迟到Transparent
亮点亦是缺点:第二个Pass会完全覆盖第一个Pass非描边部分,这会导致描边细节丢失,而仅仅绘制模型外围一圈描边,模型内部没有任何描边
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 Shader "Samples/Outline" { Properties { [Header(Texture Group)] [Space(10 )] _Albedo ("Albedo" , 2 D) = "white" {} [NoScaleOffset]_Specular ("Specular (RGB-A)" , 2 D) = "black" {} [NoScaleOffset]_Normal ("Normal" , 2 D) = "bump" {} [NoScaleOffset]_AO ("Ambient Occlusion" , 2 D) = "white" {} [Header(Outline Properties)] [Space(10 )] _OutlineColor ("Outline Color" , Color) = (1 ,0 ,1 ,1 ) _OutlineWidth ("Outline Width" , Range(0 , 0.1 )) = 0.01 } SubShader { Tags { "RenderType" ="Opaque" "Queue" = "Transparent" } Pass { ZWrite Off CGPROGRAM #pragma vertex vert #pragma fragment frag #include "UnityCG.cginc" struct v2f { float4 vertex : SV_POSITION; }; fixed4 _OutlineColor; fixed _OutlineWidth; v2f vert (appdata_base v) { v2f o; v.vertex.xyz += v.normal * _OutlineWidth; o.vertex = UnityObjectToClipPos(v.vertex); return o; } fixed4 frag (v2f i) : SV_Target { return _OutlineColor; } ENDCG } CGPROGRAM #pragma surface surf StandardSpecular fullforwardshadows struct Input { float2 uv_Albedo; }; sampler2D _Albedo; sampler2D _Specular; sampler2D _Normal; sampler2D _AO; void surf (Input IN, inout SurfaceOutputStandardSpecular o) { fixed4 c = tex2D (_Albedo, IN.uv_Albedo); o.Albedo = c.rgb; fixed4 specular = tex2D (_Specular, IN.uv_Albedo); o.Specular = specular.rgb; o.Smoothness = specular.a; o.Normal = UnpackNormal(tex2D (_Normal, IN.uv_Albedo)); o.Occlusion = tex2D (_AO, IN.uv_Albedo); } ENDCG } }
P241 3.遮挡半透明 原理:两个Pass渲染,被遮挡部分深度测试设为Greater,中间半透,边缘发亮;未被遮挡部分正常绘制
遮挡半透明不需要显示后面的物体,就不必更改渲染队列了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 Shader "Samples/X-Ray" { Properties { [Header(The Blocked Part)] [Space(10 )] _Color ("X-Ray Color" , Color) = (0 ,1 ,1 ,1 ) _Width ("X-Ray Width" , Range(1 , 2 )) = 1 _Brightness ("X-Ray Brightness" ,Range(0 , 2 )) = 1 [Header(The Normal Part)] [Space(10 )] _Albedo("Albedo" , 2 D) = "white" {} [NoScaleOffset]_Specular ("Specular (RGB-A)" , 2 D) = "black" {} [NoScaleOffset]_Normal ("Nromal" , 2 D) = "bump" {} [NoScaleOffset]_AO ("AO" , 2 D) = "white" {} } SubShader { Tags{"RenderType" = "Opaque" "Queue" = "Geometry" } Pass { ZTest Greater ZWrite Off Blend SrcAlpha OneMinusSrcAlpha CGPROGRAM #pragma vertex vert #pragma fragment frag #include "UnityCG.cginc" struct v2f { float4 vertexPos : SV_POSITION; float3 viewDir : TEXCOORD0; float3 worldNor : TEXCOORD1; }; v2f vert (appdata_base v) { v2f o; o.vertexPos = UnityObjectToClipPos(v.vertex); o.viewDir = normalize(WorldSpaceViewDir(v.vertex)); o.worldNor = UnityObjectToWorldNormal(v.normal); return o; } fixed4 _Color; fixed _Width; half _Brightness; float4 frag (v2f i) : SV_Target { half NDotV = saturate(dot(i.worldNor, i.viewDir)); NDotV = pow (1 - NDotV, _Width) * _Brightness; fixed4 color; color.rgb = _Color.rgb; color.a = NDotV; return color; } ENDCG } CGPROGRAM #pragma surface surf StandardSpecular #pragma target 3.0 struct Input { float2 uv_Albedo; }; sampler2D _Albedo; sampler2D _Specular; sampler2D _Normal; sampler2D _AO; void surf (Input IN, inout SurfaceOutputStandardSpecular o) { o.Albedo = tex2D(_Albedo, IN.uv_Albedo).rgb; fixed4 specular = tex2D(_Specular, IN.uv_Albedo); o.Specular = specular.rgb; o.Smoothness = specular.a; o.Normal = UnpackNormal(tex2D(_Normal, IN.uv_Albedo)); } ENDCG } }
P249 4.三平面映射 原理:使用世界空间中X、Y、Z三个方向的坐标屏幕对纹理进行采样
X方向上显示colorYZ
Y方向上显示colorXZ
Z方向上显示colorXY
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 Shader "Samples/Tri-Planar Mapping" { Properties { _Tiling ("Tiling" , float ) = 1 [NoScaleOffset]_Albedo ("Albedo" , 2 D) = "white" {} [NoScaleOffset]_Normal ("Normal" , 2 D) = "bump" {} _Bumpiness ("Bumpiness" , Range(0.01 , 10 )) = 1 } SubShader { CGPROGRAM #pragma surface surf Lambert fullforwardshadows struct Input { float3 worldPos; float3 worldNormal; INTERNAL_DATA }; float _Tiling; sampler2D _Albedo; sampler2D _Normal; half _Bumpiness; void surf (Input IN, inout SurfaceOutput o) { float3 texCoord = IN.worldPos * _Tiling; float3 normal = abs (WorldNormalVector(IN, o.Normal)); fixed maskX = saturate(dot(normal, fixed3(1 , 0 , 0 ))); fixed maskY = saturate(dot(normal, fixed3(0 , 1 , 0 ))); fixed4 colorXY = tex2D (_Albedo, texCoord.xy); fixed4 colorYZ = tex2D (_Albedo, texCoord.yz); fixed4 colorXZ = tex2D (_Albedo, texCoord.xz); fixed4 c; c = lerp(colorXY, colorYZ, maskX); c = lerp(c, colorXZ, maskY); o.Albedo = c.rgb; fixed3 normalXY = UnpackNormal(tex2D(_Normal, texCoord.xy)); fixed3 normalYZ = UnpackNormal(tex2D(_Normal, texCoord.yz)); fixed3 normalXZ = UnpackNormal(tex2D(_Normal, texCoord.xz)); fixed3 n; n = lerp(normalXY, normalYZ, maskX); n = lerp(n, normalXZ, maskY); o.Normal = n * half3(_Bumpiness, _Bumpiness, 1 ); } ENDCG } FallBack "Diffuse" }
P258 5.MatCap效果 原理:使用法线向量对纹理进行采样
分析:模型某一点的单位化后的法线向量,投影到XY平面,用投影后的坐标进行采样。因为投影之前,所有法线向量最多构成一个单位球,因此投影后的XY坐标必不可能为(1,1)之类的点(单位圆以外的点),所以你会发现Matcap贴图长成这个样子
最终效果:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 Shader "Samples/MatCap" { Properties { [NoScaleOffset]_MatCap ("MatCap" , 2 D) = "white" {} } SubShader { Tags { "RenderType" ="Opaque" "Queue" = "Geometry" } Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag #include "UnityCG.cginc" struct v2f { float2 texcoord : TEXCOORD0; float4 vertex : SV_POSITION; }; sampler2D _MatCap; v2f vert (appdata_base v) { v2f o; float4 normal = mul(UNITY_MATRIX_IT_MV, float4(v.normal, 0 )); o.texcoord = normalize(normal.xyz).xy; o.vertex = UnityObjectToClipPos(v.vertex); return o; } fixed4 frag (v2f i) : SV_Target { float2 texcoord = i.texcoord * 0.5 + 0.5 ; return tex2D(_MatCap, texcoord); } ENDCG } } FallBack "Diffuse" }
P263 6.物体切割效果 原理:利用世界坐标配合透明度测试实现物体切割效果
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 Shader "Samples/Object Cutting" { Properties { [Header(Textures)] [Space(10 )] [NoScaleOffset] _Albedo ("Albedo" , 2 D) = "white" {} [NoScaleOffset] _Reflection ("Specular_Smoothness" , 2 D) = "black" {} [NoScaleOffset] _Normal ("Normal" , 2 D) = "bump" {} [NoScaleOffset] _Occlusion ("Ambient Occlusion" , 2 D) = "white" {} [Header(Cutting)] [Space(10 )] [KeywordEnum(X, Y, Z)] _Direction ("Cutting Direction" , Float) = 1 [Toggle] _Invert ("Invert Direction" , Float) = 0 } SubShader { Tags { "RenderType" ="TransparentCutout" "Queue" ="AlphaTest" } Cull Off CGPROGRAM #pragma surface surf StandardSpecular addshadow fullforwardshadows #pragma target 3.0 #pragma multi_compile _DIRECTION_X _DIRECTION_Y _DIRECTION_Z sampler2D _Albedo; sampler2D _Reflection; sampler2D _Normal; sampler2D _Occlusion; float3 _Position; fixed _Invert; struct Input { float2 uv_Albedo; float3 worldPos; fixed face : VFACE; }; void surf (Input i, inout SurfaceOutputStandardSpecular o) { fixed4 col = tex2D(_Albedo, i.uv_Albedo); o.Albedo = i.face > 0 ? col.rgb : fixed3(0 ,0 ,0 ); #if _DIRECTION_X col.a = step(_Position.x, i.worldPos.x); #elif _DIRECTION_Y col.a = step(_Position.y, i.worldPos.y); #else col.a = step(_Position.z, i.worldPos.z); #endif col.a = _Invert? 1 - col.a : col.a; clip(col.a - 0.001 ); fixed4 reflection = tex2D(_Reflection, i.uv_Albedo); o.Specular = i.face > 0 ? reflection.rgb : fixed3(0 ,0 ,0 ); o.Smoothness = i.face > 0 ? reflection.a : 0 ; o.Normal = UnpackNormal(tex2D(_Normal, i.uv_Albedo)); o.Occlusion = tex2D(_Occlusion, i.uv_Albedo); } ENDCG } }
C#脚本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 using System.Collections;using System.Collections.Generic;using UnityEngine;[ExecuteInEditMode ] public class GetPosition : MonoBehaviour { public GameObject CuttingPosition; private Material Material; private Vector3 Center = new Vector3(0 , 0 , 0 ); void Start () { Material = this .GetComponent<Renderer>().sharedMaterial; } void Update () { if (CuttingPosition) Material.SetVector("_Position" , CuttingPosition.transform.position); else Material.SetVector("_Position" , Center); } }
Ch14.进阶案例 P274 1.消融效果 原理:根据噪点图使用透明度测试
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 Shader "Samples/Dissolve" { Properties { [Header(PBS Textures)] [Space(10 )] [NoScaleOffset]_Albedo("Albedo" , 2 D) = "white" {} [NoScaleOffset]_Specular("Specular_Smoothness" , 2 D) = "black" {} [NoScaleOffset]_Normal("Normal" , 2 D) = "bump" {} [NoScaleOffset]_AO("AO" , 2 D) = "white" {} [Header(Dissolve Properties)] [Space(10 )] _Noise("Dissolve Noise" , 2 D) = "white" {} _Dissolve("Dissolve" , Range(0 , 1 )) = 0 [NoScaleOffset]_Gradient("Edge Gradient" , 2 D) = "black" {} _Range("Edge Range" , Range(2 , 100 )) = 6 _Brightness("Brightness" , Range(0 , 10 )) = 1 } SubShader { Tags { "RenderType" ="TransparentCutout" "Queue" = "AlphaTest" } CGPROGRAM #pragma surface surf StandardSpecular addshadow fullforwardshadows struct Input { float2 uv_Albedo; float2 uv_Noise; }; sampler2D _Albedo; sampler2D _Specular; sampler2D _Normal; sampler2D _AO; sampler2D _Noise; fixed _Dissolve; sampler2D _Gradient; float _Range; float _Brightness; void surf (Input IN, inout SurfaceOutputStandardSpecular o) { fixed noise = tex2D(_Noise, IN.uv_Noise).r; fixed dissolve = _Dissolve * 2 - 1 ; fixed mask = saturate(noise - dissolve); clip(mask - 0.5 ); fixed texcoord = saturate(mask * _Range - 0.5 * _Range); o.Emission = tex2D(_Gradient, fixed2(texcoord, 0.5 )) * _Brightness; fixed4 c = tex2D (_Albedo, IN.uv_Albedo); o.Albedo = c.rgb; fixed4 specular = tex2D(_Specular, IN.uv_Albedo); o.Specular = specular.rgb; o.Smoothness = specular.a; o.Normal = UnpackNormal(tex2D(_Normal, IN.uv_Albedo)); o.Occlusion = tex2D(_AO, IN.uv_Albedo); } ENDCG } }
P283 2.动态液体 原理:使用透明度测试,利用模型中心点,剔除上半部分。再使用sin实现波纹效果。
瓶内液体的制作就是将瓶身缩小一点后即可,嵌套进瓶身即可
缺点:从瓶子底部看会穿帮
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 Shader "Samples/Dynamic Liquid" { Properties { _Color("Color" , Color) = (1 , 0 , 0 , 1 ) _Specular("Specular_Smoothness" , Color) = (0 , 0 , 0 , 0 ) _Level("Liquid Level" , float ) = 0 [KeywordEnum(X,Z)] _Direction("Ripple Direction" , float ) = 0 _Speed("Ripple Speed" , float ) = 1 _Height("Ripple Height" , float ) = 1 } SubShader { Tags{"RenderType" = "Transparent" "Queue" = "Transparent" } Blend DstColor SrcColor ZWrite Off CGPROGRAM #pragma surface surf StandardSpecular noshadow #pragma shader_feature _DIRECTION_X _DIRECTION_Z struct Input { float3 worldPos; }; fixed4 _Color; fixed4 _Specular; half _Level; half _Speed; half _Height; void surf (Input IN, inout SurfaceOutputStandardSpecular o) { float3 pivot = mul(unity_ObjectToWorld, float4(0 , 0 , 0 , 1 )); float liquid = pivot.y - IN.worldPos.y + _Level * 0.01 ; float3 ripple = sin (_Time.y * _Speed) * _Height * (IN.worldPos - pivot); #if _DIRECTION_X liquid += ripple.x; #else liquid += ripple.z; #endif liquid = step(0 , liquid); clip(liquid - 0.001 ); o.Albedo = _Color.rgb; o.Specular = _Specular.rgb; o.Smoothness = _Specular.a; } ENDCG } }
P293 3.Billboard效果 原理:重建顶点的模型坐标系。z轴方向为观察方向,x轴为向右方向,y轴由叉乘得出,通过矩阵变换将所有顶点变换至新坐标系下
分类:
Spherical:球体模式。能够在水平和垂直方向旋转,角度完全朝向摄像机。例如,粒子特效
Cylindrical:圆柱体模式。只在水平方向旋转,垂直方向固定。例如,远距离的树
本质就是z轴方向的y轴是否恒为0,恒为0就是圆柱体,否则为球体
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 Shader "Samples/Billboard" { Properties { [NoScaleOffset] _Tex ("Texture" , 2 D) = "white" {} _Tint ("Tine" , Color) = (1 , 1 , 1 , 1 ) [KeywordEnum(Spherical, Cylindrical)] _Type ("Type" , float ) = 0 } SubShader { Tags { "RenderType" = "Transparent" "Queue" = "Transparent" "DisableBatching" = "True" } Blend OneMinusDstColor One ZWrite Off Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag #pragma shader_feature _TYPE_SPHERICAL _TYPE_CYLINDRICAL struct appdata { float4 vertex : POSITION; float2 texcoord : TEXCOORD0; }; struct v2f { float4 vertex : SV_POSITION; float2 texcoord : TEXCOORD0; }; sampler2D _Tex; fixed4 _Tint; v2f vert (appdata v) { v2f o; float3 forward = mul(unity_WorldToObject, float4(_WorldSpaceCameraPos, 1 )).xyz; #if _TYPE_CYLINDRICAL forward.y = 0 ; #endif forward = normalize(forward); float3 up = abs (forward.y) > 0.999 ? float3(0 , 0 , 1 ) : float3(0 , 1 , 0 ); float3 right = normalize(cross(forward, up)); up = normalize(cross(right, forward)); float3 vertex = v.vertex.x * right + v.vertex.y * up; o.vertex = UnityObjectToClipPos(vertex); o.texcoord = v.texcoord; return o; } float4 frag (v2f i) : SV_Target { return tex2D(_Tex, i.texcoord) * _Tint; } ENDCG } } }
P302 4.序列帧动画 原理:根据时间,计算uv,采样纹理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 Shader "Samples/Sequence Animation" { Properties { [NoScaleOffset] _Tex ("Sequence Image" , 2 D) = "white" {} _Tint ("Tint" , Color) = (1 , 1 , 1 , 1 ) _Row ("Row Amount" , float ) = 1 _Column ("Column Amount" , float ) = 1 _Rate ("Animation Rate" , float ) = 1 } SubShader { Tags { "RenderType" = "Transparent" "Queue" = "Transparent" "DisableBatching" = "True" } Blend OneMinusDstColor One ZWrite Off Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag struct appdata { float4 vertex : POSITION; float2 texcoord : TEXCOORD0; }; struct v2f { float4 vertex : SV_POSITION; float2 texcoord : TEXCOORD0; }; sampler2D _Tex; fixed4 _Tint; float _Row; float _Column; float _Rate; v2f vert (appdata v) { v2f o; float3 forward = mul(unity_WorldToObject, float4(_WorldSpaceCameraPos, 1 )).xyz; forward.y = 0 ; forward = normalize(forward); float3 up = abs (forward.y) > 0.999 ? float3(0 , 0 , 1 ) : float3(0 , 1 , 0 ); float3 right = normalize(cross(forward, up)); up = normalize(cross(right, forward)); float3 vertex = v.vertex.x * right + v.vertex.y * up; o.vertex = UnityObjectToClipPos(vertex); float time = floor (_Time.y * _Rate); float row = floor (time / _Column); float column = fmod (time, _Column); float texcoordU = (v.texcoord.x + column) / _Column; float texcoordV = (v.texcoord.y - 1 - row) / _Row; o.texcoord = float2(texcoordU, texcoordV); return o; } float4 frag (v2f i) : SV_Target { return tex2D(_Tex, i.texcoord) * _Tint; } ENDCG } } }
P312 5.卡通风格效果 原理:Half Lambert采样Ramp,描边,边缘高光,光照模型
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 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 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 Shader "Samples/Toon" { Properties { [Header(Diffuse)] [Space(10 )] _Albedo ("Albedo" , 2 D) = "white" {} _Ramp ("Toon Ramp" , 2 D) = "white" {} [Header(Rim)] [Space(10 )][HDR] _RimColor ("Rim Color" , Color) = (0 ,2 ,2 ,1 ) _RimWidth ("Rim Width" , Range(0 ,1 )) = 0 _RimFalloff ("Rim Falloff" , Range(0.01 ,10 )) = 1 [Header(Outline)] [Space(10 )] _OutlineColor ("Outline Color" , Color) = (0 ,0 ,0 ,0 ) _OutlineWidth ("Outline, Width" , Float) = 0.02 } SubShader { Tags { "RenderType" ="Opaque" "Queue" ="Geometry" } Pass { Cull Front CGPROGRAM #pragma vertex vert #pragma fragment frag #include "UnityCG.cginc" fixed4 _OutlineColor; half _OutlineWidth; float4 vert (appdata_base v) : SV_POSITION { v.vertex.xyz += v.normal * _OutlineWidth; return UnityObjectToClipPos(v.vertex); } float4 frag () : SV_Target { return _OutlineColor; } ENDCG } CGPROGRAM #pragma surface surf Toon sampler2D _Albedo; struct Input { float2 uv_Albedo; float3 worldNormal; float3 viewDir; }; struct SurfaceOutputToon { half3 Albedo; half3 Normal; half3 Emission; fixed Alpha; Input SurfaceInput; UnityGIInput GIdata; }; void surf (Input i, inout SurfaceOutputToon o) { o.SurfaceInput = i; o.Albedo = tex2D(_Albedo, i.uv_Albedo); } void LightingToon_GI (inout SurfaceOutputToon s, UnityGIInput GIdata, UnityGI gi) { s.GIdata = GIdata; } sampler2D _Ramp; half4 _RimColor; fixed _RimWidth; half _RimFalloff; half4 LightingToon (SurfaceOutputToon s, UnityGI gi) { UnityGIInput GIdata = s.GIdata; Input i = s.SurfaceInput; gi = UnityGI_Base(GIdata, GIdata.ambient, i.worldNormal); fixed NdotL = dot(i.worldNormal, gi.light.dir); fixed2 rampTexcoord = float2(NdotL * 0.5 + 0.5 , 0.5 ); fixed3 ramp = tex2D(_Ramp, rampTexcoord).rgb; half3 diffuse = s.Albedo * ramp * _LightColor0.rgb * (GIdata.atten + gi.indirect.diffuse); fixed NdotV = dot(i.worldNormal , i.viewDir); fixed rimMask = pow ((1.0 - saturate((NdotV + _RimWidth))), _RimFalloff); half3 rim = saturate(rimMask * NdotL) * _RimColor * _LightColor0.rgb * GIdata.atten; return half4(diffuse + rim, 1 ); } ENDCG } FallBack "Diffuse" }
P326 6.夜视仪后期处理 原理:镜头扭曲(书上只给了公式)、调色、四周暗角效果、随机生成的噪点
Shader 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 Shader "Hidden/Night Vision" { Properties { _MainTex ("MainTex" , 2 D) = "white" {} } SubShader { Cull Off ZWrite Off ZTest Always Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag #include "UnityCG.cginc" struct v2f { float4 vertex : SV_POSITION; half2 uv : TEXCOORD0; half4 screenPos : TEXCOORD1; }; v2f vert (appdata_img v) { v2f o; o.vertex = UnityObjectToClipPos(v.vertex); o.uv = v.texcoord; o.screenPos = ComputeScreenPos(o.vertex); return o; } sampler2D _MainTex; half _Distortion; half _Scale; fixed _Brightness; fixed _Saturation; fixed _Contrast; fixed4 _Tint; half _VignetteFalloff; half _VignetteIntensity; sampler2D _Noise; half _NoiseAmount; half _RandomValue; fixed4 frag (v2f i) : SV_Target { fixed2 center = i.uv - 0.5 ; half radius2 = pow (center.x, 2 ) + pow (center.y, 2 ); half distortion = 1 + sqrt (radius2) * radius2 * _Distortion; half2 uvScreen = center * distortion * _Scale + 0.5 ; fixed4 screen = tex2D(_MainTex, uvScreen); screen += _Brightness; fixed4 luminance = Luminance(screen.rgb).xxxx; screen = lerp(luminance, screen, _Saturation); fixed4 gray = fixed4(0.5 ,0.5 ,0.5 ,1 ); screen = lerp(gray, screen, _Contrast); screen *= _Tint; half circle = distance(i.screenPos.xy, fixed2(0.5 ,0.5 )); fixed vignette = 1 - saturate(pow (circle, _VignetteFalloff)); screen *= pow (vignette, _VignetteIntensity); float2 uvNoise = i.uv * _NoiseAmount; uvNoise.x += sin (_RandomValue); uvNoise.y -= sin (_RandomValue + 1 ); fixed noise = tex2D(_Noise, uvNoise).r; screen *= noise; return screen; } ENDCG } } }
C#脚本 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 using System.Collections;using System.Collections.Generic;using UnityEngine;[RequireComponent(typeof(Camera)) ] [ExecuteInEditMode ] public class NightVision : MonoBehaviour { public Shader EffectShader; [Header("Basic Properties" ) ] [Range(-2, 2) ] public float Distortion = 0.5f ; [Range(0.01f, 1) ] public float Scale = 0.5f ; [Range(-1, 1) ] public float Brightness = 0 ; [Range(0, 2) ] public float Saturation = 1 ; [Range(0, 2) ] public float Contrasrt = 1 ; public Color Tint = Color.black; [Header("Advanced Properties" ) ] [Range(0, 10) ] public float VignetteFalloff = 1 ; [Range(0, 100) ] public float VignetteIntensity = 1 ; public Texture2D Noise; [Range(0, 10) ] public float NoiseAmount = 1 ; private float RandomValue; private Material currentMaterial; Material EffectMaterial { get { if (currentMaterial == null ) { currentMaterial = new Material(EffectShader) { hideFlags = HideFlags.HideAndDontSave }; } return currentMaterial; } } private void OnRenderImage (RenderTexture source, RenderTexture destination ) { if (EffectMaterial != null ) { EffectMaterial.SetFloat("_Distortion" , Distortion); EffectMaterial.SetFloat("_Scale" , Scale); EffectMaterial.SetFloat("_Brightness" , Brightness); EffectMaterial.SetFloat("_Saturation" , Saturation); EffectMaterial.SetFloat("_Contrast" , Contrasrt); EffectMaterial.SetColor("_Tint" , Tint); EffectMaterial.SetFloat("_VignetteFalloff" , VignetteFalloff); EffectMaterial.SetFloat("_VignetteIntensity" , VignetteIntensity); if (Noise != null ) { EffectMaterial.SetTexture("_Noise" , Noise); EffectMaterial.SetFloat("_NoiseAmount" , NoiseAmount); EffectMaterial.SetFloat("_RandomValue" , RandomValue); } Graphics.Blit(source, destination, EffectMaterial); } else Graphics.Blit(source, destination); } private void Update () { RandomValue = Random.Range(-3.14f , 3.14f ); } }