Unity는 회전 빔을 생성합니다.

Unity는 회전 빔을 생성합니다.


  안녕하세요 여러분, 제 이름은 A Zhao입니다.
  이는 많은 게임에서 볼 수 있었던 효과로, 포탈, 마법진, 캐릭터 등의 발 아래에서 브러시드 라이트 빔이 위쪽으로 방출된 후 지속적으로 회전합니다.
여기에 이미지 설명을 삽입하세요.

  이번에는 Unity 엔진에서 이 효과를 실행해 보겠습니다.

1. 재료 준비

  준비해야 할 재료는 매우 간단합니다.
  첫 번째는 상단 및 하단 캡이 제거된 다음 UV가 편평해진 원통형 메시 모델입니다.
여기에 이미지 설명을 삽입하세요.

  두 번째는 노이즈 맵입니다.
여기에 이미지 설명을 삽입하세요.

2. 생산과정

1. 모양 조절

  준비된 출력물은 원통형 그리드이므로 실제 디스플레이 효과는 부채꼴 모양입니다. 따라서 정점을 제어하여 이를 달성해야 합니다. 원리는 매우 간단합니다.모델의 아래쪽에서 위쪽으로 갈수록 UV의 V좌표는 0에서 1로 변하므로, 법선 방향을 따라 UV의 V좌표를 곱한 후 컨트롤 값을 곱하면 됩니다. 정점 좌표에 추가하면 아래쪽은 변경되지 않고 위쪽으로 갈수록 너비가 늘어납니다.

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

효과는 아래와 같습니다
여기에 이미지 설명을 삽입하세요.

2. 브러시 효과

  이 효과를 얻으려면 먼저 노이즈 맵을 그리드 모델에 할당하십시오.
여기에 이미지 설명을 삽입하세요.

  그런 다음 타일링 수를 설정하십시오.
여기에 이미지 설명을 삽입하세요.

  브러싱 효과를 얻을 수 있습니다.
여기에 이미지 설명을 삽입하세요.

  여기에서 고유 색상에 HDR 색상 오버레이를 추가한 다음 브러시 효과를 알파 채널로 입력하고 투명 렌더링을 설정하여 이 효과를 얻습니다.
여기에 이미지 설명을 삽입하세요.

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 );

여기에 이미지 설명을 삽입하세요.

  부드러운 위쪽 및 아래쪽 가장자리에 원래 브러시된 알파 값을 곱하면 다음과 같은 효과를 얻을 수 있습니다.
여기에 이미지 설명을 삽입하세요.

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 ));

  계산 결과는 다음과 같이 왼쪽과 오른쪽이 점차 어두워지는 것입니다.
여기에 이미지 설명을 삽입하세요.

3. 오버레이 마스크

  위의 세 개의 SmoothStep 결과를 곱하여 다음과 같은 마스크 범위를 얻습니다.
여기에 이미지 설명을 삽입하세요.

  그런 다음 브러시된 알파 값을 곱하면 다음과 같은 효과를 얻을 수 있습니다.
여기에 이미지 설명을 삽입하세요.

4. 폐색 문제 해결

  반투명 렌더링에 문제가 있습니다. 특정 각도에서는 오류가 나타납니다.
여기에 이미지 설명을 삽입하세요.

  여기서는 그리드 모델의 또 다른 복사본을 만듭니다.
여기에 이미지 설명을 삽입하세요.

  그런 다음 두 메시 모델은 서로 다른 CullMode를 사용합니다.

여기에 이미지 설명을 삽입하세요.
여기에 이미지 설명을 삽입하세요.
여기에 이미지 설명을 삽입하세요.

여기에 이미지 설명을 삽입하세요.

  그런 다음 두 개의 메쉬 모델이 함께 표시되고 올바른 효과가 얻어집니다.
여기에 이미지 설명을 삽입하세요.

3. 셰이더 소스 코드

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
		}
	}
	
}

추천

출처blog.csdn.net/liweizhao/article/details/133195646