• Unity制作旋转光束


    Unity制作旋转光束


      大家好,我是阿赵。
      这是一个在很多游戏里面可能都看到过的效果,在传送门、魔法阵、角色等脚底下往上散发出一束拉丝形状的光,然后在不停的旋转。
    在这里插入图片描述

      这次来在Unity引擎里面做一下这种效果。

    一、准备材料

      需要准备的素材很简单。
      第一个是一个圆柱形的网格模型,删除了上下盖,然后展平UV
    在这里插入图片描述

      第二个是一张噪声贴图
    在这里插入图片描述

    二、制作过程

    1、控制形状

      由于准备的输出是一个圆柱网格,但实际显示的效果是一个类似于扇形的形状。所以需要通过控制顶点来实现。原理很简单,UV的V坐标,是从模型的底部到顶部从0到1变化,所以只要沿着法线方向,乘以UV的V坐标,然后再乘以一个控制值,加到顶点坐标上,就能做到底部不变,越往上宽度越大了。

    float3 vertexValue = ( v.normal * _normalScale * v.uv.y );
    v.vertex.xyz += vertexValue;
    o.vertex = UnityObjectToClipPos(v.vertex);
    
    • 1
    • 2
    • 3

    效果入下图
    在这里插入图片描述

    2、拉丝效果

      先把噪声图赋给网格模型,得到这样的效果:
    在这里插入图片描述

      然后设置一下平铺次数
    在这里插入图片描述

      就得到了拉丝的效果了。
    在这里插入图片描述

      这里给固有色添加一个HDR的颜色叠加,然后把这个拉丝效果作为Alpha通道输入,设置Transparent透明渲染,就能得到这样的效果:
    在这里插入图片描述

    3、透明渐变效果

      上面的效果太强烈,需要对它的透明度做一定的控制。先看看UV坐标的V坐标的实际范围:
    在这里插入图片描述

      之前提到过,V坐标是从模型底部到顶部从0到1变化。接下来就可以通过这个值做一些处理。

    1.上下边缘控制

      首先控制的是上下边缘,现在边缘太硬,我把它用两个SmoothStep,分别对应顶部和底部,让边缘变得柔和:

    float tempOneMinueVal = ( 1.0 - i.uv.y );
    float smoothstepResultV1 = smoothstep( _vMin , _vMax , ( tempOneMinueVal - _vOffset ));
    float smoothstepResultV2 = smoothstep( _vMin2 , _vMax2 , i.uv.y);
    float clampResult = clamp( min( min( smoothstepResultV1 , tempOneMinueVal ) , smoothstepResultV2 ) , 0.0 , 1.0 );
    
    • 1
    • 2
    • 3
    • 4

    在这里插入图片描述

      把这个上下边缘柔和的结果和原来的拉丝Alpha值相乘,得到了这个效果:
    在这里插入图片描述

    2.左右边缘控制

      上面的效果已经很接近我们想要的效果了,但还差一点,左右边缘也很硬,所以用世界法线方向和观察方向做点乘,最后还是加一个SmoothStep,让左右边缘有个柔和渐变。

    float3 worldNormal = i.worldNormal.xyz;
    float3 worldViewDir = UnityWorldSpaceViewDir(i.worldPos);
    worldViewDir = normalize(worldViewDir);
    float dotResult = dot( worldNormal , worldViewDir );
    float smoothstepResultEdge = smoothstep( _edgeMin , _edgeMax , abs( dotResult ));
    
    • 1
    • 2
    • 3
    • 4
    • 5

      计算结果是这样的左右两边渐变的变暗:
    在这里插入图片描述

    3.叠加遮罩

      把上面的3个SmoothStep结果相乘,就得到了这样一个遮罩范围:
    在这里插入图片描述

      然后和拉丝的Alpha值相乘,得到了这样的效果:
    在这里插入图片描述

    4、遮挡问题解决

      这里有一个半透明渲染的问题,在某些角度看,会出现错误显示:
    在这里插入图片描述

      这里我再复制一份网格模型:
    在这里插入图片描述

      然后两个网格模型使用不同的CullMode

    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

    在这里插入图片描述

      然后两个网格模型一起显示,就得到了正确的效果:
    在这里插入图片描述

    三、Shader源码

    Shader "azhao/LightColumn"
    {
    	Properties
    	{
    		[HDR]_emissCol("emissCol", Color) = (0,0,0,0)
    		_emissScale("emissScale", Float) = 1
    		_noiseTex("noiseTex", 2D) = "white" {}
    		_flowSpeed("flowSpeed", Vector) = (0,0,0,0)
    		_vOffset("vOffset", Float) = 0
    		_edgeMin("edgeMin", Range( 0 , 1)) = 0
    		_edgeMax("edgeMax", Range( 0 , 1)) = 1
    		_vMin("vMin", Range( 0 , 1)) = 0
    		_vMax("vMax", Range( 0 , 1)) = 1
    		_normalScale("normalScale", Range(-2,2)) = 1		
    		_vMin2("vMin2", Range( 0 , 1)) = 0
    		_vMax2("vMax2", Range( 0 , 1)) = 1
    [Enum(UnityEngine.Rendering.CullMode)]_CullMode("CullMode", Float) = 2
    
    	}
    	
    	SubShader
    	{
    		
    		
    		Tags { "RenderType"="Opaque" }
    	LOD 100
    
    		CGINCLUDE
    		#pragma target 3.0
    		ENDCG
    		Blend SrcAlpha One, SrcAlpha One
    		AlphaToMask Off
    		Cull [_CullMode]
    		ColorMask RGBA
    		ZWrite On
    		ZTest LEqual
    		Offset 0 , 0
    		
    		
    		
    		Pass
    		{
    			Name "Unlit"
    			Tags { "LightMode"="ForwardBase" }
    			CGPROGRAM
    
    			
    
    			#ifndef UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX
    			//only defining to not throw compilation error over Unity 5.5
    			#define UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(input)
    			#endif
    			#pragma vertex vert
    			#pragma fragment frag
    			#pragma multi_compile_instancing
    			#include "UnityCG.cginc"
    			#include "UnityShaderVariables.cginc"
    
    
    
    			struct appdata
    			{
    				float4 vertex : POSITION;
    				float4 color : COLOR;
    				float3 normal : NORMAL;
    				float2 uv : TEXCOORD0;
    				UNITY_VERTEX_INPUT_INSTANCE_ID
    			};
    			
    			struct v2f
    			{
    				float4 vertex : SV_POSITION;
    
    				float3 worldPos : TEXCOORD0;
    				float2 uv : TEXCOORD1;
    				float3 worldNormal : TEXCOORD2;
    				UNITY_VERTEX_INPUT_INSTANCE_ID
    				UNITY_VERTEX_OUTPUT_STEREO
    			};
    
    			uniform float _CullMode;
    			uniform float _normalScale;
    			uniform float4 _emissCol;
    			uniform float _emissScale;
    			uniform sampler2D _noiseTex;
    			SamplerState sampler_noiseTex;
    			uniform float2 _flowSpeed;
    			uniform float4 _noiseTex_ST;
    			uniform float _vMin;
    			uniform float _vMax;
    			uniform float _vOffset;
    			uniform float _vMin2;
    			uniform float _vMax2;
    			uniform float _edgeMin;
    			uniform float _edgeMax;
    
    			
    			v2f vert ( appdata v )
    			{
    				v2f o;
    				UNITY_SETUP_INSTANCE_ID(v);
    				UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);
    				UNITY_TRANSFER_INSTANCE_ID(v, o);
    
    				
    				float3 worldNormal = UnityObjectToWorldNormal(v.normal);
    				o.worldNormal = worldNormal;				
    				o.uv.xy = v.uv.xy;				
    
    
    				float3 vertexValue = ( v.normal * _normalScale * v.uv.y );
    
    				v.vertex.xyz += vertexValue;
    
    				o.vertex = UnityObjectToClipPos(v.vertex);
    				o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
    				return o;
    			}
    			
    			half4 frag (v2f i ) : SV_Target
    			{
    				UNITY_SETUP_INSTANCE_ID(i);
    				UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(i);
    
    				float2 uv_noiseTex = i.uv.xy * _noiseTex_ST.xy + _noiseTex_ST.zw;
    				float2 panner6 = ( 1.0 * _Time.y * _flowSpeed + uv_noiseTex);
    				float tempOneMinueVal = ( 1.0 - i.uv.y );
    				float smoothstepResultV1 = smoothstep( _vMin , _vMax , ( tempOneMinueVal - _vOffset ));
    				float smoothstepResultV2 = smoothstep( _vMin2 , _vMax2 , i.uv.y);
    				float clampResult = clamp( min( min( smoothstepResultV1 , tempOneMinueVal ) , smoothstepResultV2 ) , 0.0 , 1.0 );
    				float3 worldNormal = i.worldNormal.xyz;
    				float3 worldViewDir = UnityWorldSpaceViewDir(i.worldPos);
    				worldViewDir = normalize(worldViewDir);
    				float dotResult = dot( worldNormal , worldViewDir );
    				float smoothstepResultEdge = smoothstep( _edgeMin , _edgeMax , abs( dotResult ));
    				half4 finalColor = (float4((( _emissCol * _emissScale )).rgb , ( tex2D( _noiseTex, panner6 ).r * clampResult * smoothstepResultEdge )));
    				
    				return finalColor;
    			}
    			ENDCG
    		}
    	}
    	
    }
    
    • 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
  • 相关阅读:
    【快速使用ShardingJDBC的哈希分片策略进行分库分表】
    神经网络入门
    OI 模板合集
    【OpenCV】在MacOS上源码编译OpenCV
    debug调试高级功能 断点、布局 及Android Studio常用快捷按键使用详情
    Go 使用mencached缓存
    车载电子电器架构 —— 智能座舱技术范围(万字长文精讲)
    【软件逆向】带壳带反调试找flag教程(VMProject3.0+X64dbg+ScyllaHide)
    【PowerQuery】PowerBI 手动刷新数据内容
    Mybatis-plus 自定义模板生成代码DTO、VO等
  • 原文地址:https://blog.csdn.net/liweizhao/article/details/133195646