저자 : Harry Alisavakis . 테크니컬 아티스트
또 다른 디졸브 이펙트가 나올 것이라고 말씀드렸습니다. 이 이펙트의 이름도 고민하다가 '근접 디졸브'라는 이름을 붙일까도 생각했지만 정확하지 않을 것 같았습니다. 어쨌든 개념은 간단합니다. 구체 안에 있는 오브젝트의 일부를 녹이는 표면 셰이더입니다. 이 효과는 다음과 같이 보일 수 있습니다:
코드 부분에 들어가기 전에 여기서 무슨 일이 일어나고 있는지에 대한 높은 수준의 개념을 설명하겠습니다:
이 셰이더를 사용하는 단일 머티리얼이 모든 오브젝트에 적용됩니다.
이 셰이더는 반경과 부드러움 속성이 있는 구체 마스크 외부에 있는 모든 픽셀을 클립합니다. 이러한 속성은 해당 빛나는 구체에 첨부된 스크립트를 통해 전역 셰이더 속성으로 결정됩니다.
이 구형 마스크 기법은 다양한 효과에 사용할 수 있기 때문에 실제로 매우 유용합니다. 여기서는이전 디졸브게시물에서 설명한 것과 유사한 디졸브 효과에 사용하고 있습니다. 구형 마스크의 또 다른 멋진 사용법은Peer Play의이 훌륭한 튜토리얼에 나와 있습니다.
그러나 이 셰이더는 현재 형태에서는 하나의 구형 마스크에 따라 작동하므로 여러 개의 구를 가지고 그 교차점 등을 취할 수 없다는 제한이 있습니다.
또한 구형 마스크를 사용하려는 모든 오브젝트에 이 재질을 적용해야 한다는 점도 게으른 제 태도를 괴롭히는 제한 사항인데, 화면 공간 효과로 사용하면 정말 멋질 것 같다는 생각이 들었습니다. 예를 들어 디졸브 효과는 아니지만 영역 내부의 모든 색상을 회색조로 전환하는 데 사용할 수 있습니다.
흥미로운 셰이더인데, 저라면 이 셰이더에 대한 포스팅을 계속 지켜봐 주셨으면 좋겠습니다. (편집: 해당 셰이더에 대한 포스팅이 나왔으며여기에서 확인할 수 있습니다.)
어쨌든, 말 보다는 코드로... :
Shader "Custom/SphericalMaskDissolve" {
Properties {
_Color ("Color", Color) = (1,1,1,1)
_MainTex ("Albedo (RGB)", 2D) = "white" {}
_Glossiness ("Smoothness", Range(0,1)) = 0.5
_Metallic ("Metallic", Range(0,1)) = 0.0
[HDR]_Emission("Emission", Color) = (1,1,1,1)
_NoiseSize("Noise size", float ) = 1
}
SubShader {
Tags { "RenderType"="Opaque" }
Cull off
LOD 200
CGPROGRAM
// Physically based Standard lighting model, and enable shadows on all light types
#pragma surface surf Standard fullforwardshadows
// Use shader model 3.0 target, to get nicer looking lighting
#pragma target 3.0
sampler2D _MainTex;
struct Input {
float2 uv_MainTex;
float3 worldPos;
};
half _Glossiness;
half _Metallic;
fixed4 _Color;
fixed4 _Emission;
float _NoiseSize;
float3 _GLOBALMaskPosition;
half _GLOBALMaskRadius;
half _GLOBALMaskSoftness;
// Add instancing support for this shader. You need to check 'Enable Instancing' on materials that use the shader.
// See https://docs.unity3d.com/Manual/GPUInstancing.html for more information about instancing.
// #pragma instancing_options assumeuniformscaling
UNITY_INSTANCING_BUFFER_START(Props)
// put more per-instance properties here
UNITY_INSTANCING_BUFFER_END(Props)
float random (float2 input) {
return frac(sin(dot(input, float2(12.9898,78.233)))* 43758.5453123);
}
void surf (Input IN, inout SurfaceOutputStandard o) {
fixed4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color;
half dist = distance(_GLOBALMaskPosition, IN.worldPos);
half sphere = 1 - saturate((dist - _GLOBALMaskRadius) / _GLOBALMaskSoftness);
clip(sphere - 0.1);
float squares = step(0.5, random(floor(IN.uv_MainTex * _NoiseSize)));
half emissionRing = step(sphere - 0.1, 0.1) * squares;
o.Emission = _Emission * emissionRing;
o.Albedo = c.rgb;
o.Metallic = _Metallic;
o.Smoothness = _Glossiness;
o.Alpha = c.a;
}
ENDCG
}
FallBack "Diffuse"
}
이 개념은 이전의 디졸브 효과와 매우 유사하지만 더 간단하지는 않습니다. 이 경우 속성으로 방출에 대한 HDR 색상과 가장자리에 픽셀화된 방출 노이즈의 크기를 추가하기만 하면 됩니다. 디졸브 효과이므로 완전히 디졸브되지 않은 오브젝트의 뒷면을 볼 수 있도록 "Cull off" 지시문도 사용합니다. 하지만 여기서는 바닥과 같은 표면에 사용했고 그림자를 제대로 잘라내는 데 신경 쓰지 않았기 때문에 조명 모델을 변경하지 않았습니다. 셰이더를 사용하려는 대상이 그렇지 않은 경우이전 게시물에서 변경한 것과 동일한 방식으로 조명 모델을 램버트로 변경할 수 있습니다.
마찬가지로 이 경우에는 버텍스 위치가 필요하지 않으므로 일괄 처리를 비활성화하거나 버텍스 셰이더를 추가할 필요가 없습니다. 입력 구조체에서 구의 위치와 비교해야 하므로 이셰이더 비트 게시물에 표시된 것처럼 월드 위치에 대한 프로퍼티도 추가합니다.
그 후 33-38줄에서 효과에 필요한 추가 필드를 선언합니다. 그러나 36-38줄에서는 이러한 필드가 속성 블록에서 선언되지 않습니다. 이는 구형 마스크와 관련된 프로퍼티이며 외부 소스에서 제어되기 때문입니다. "GLOBAL" 지시어가 암시하듯이, 스크립트에서 해당 셰이더가 있는 각 머티리얼을 가져와 수동으로 변경하는 대신 스크립트에서 전역적으로 제어됩니다. 이 셰이더를 사용하지만 다른 프로퍼티를 가진 다른 모든 머티리얼도 동일한 구형 마스크의 영향을 받기 때문에 좋은 점도 있지만 나쁜 점도 있습니다. 반면에 해당 셰이더를 사용하는 모든 머티리얼을 검색하고 마스크 속성을 개별적으로 변경할 필요는 없습니다. 따라서 상황과 필요에 따라 선택하면 됩니다. 또한 "GLOBAL" 지시어는 필수 키워드가 아니라 이 속성이 전역적으로 제어된다는 것을 나타내기 위한 것임을 알아두세요.
47-49행에는셰이더 책에서 본 이후로 모든 곳에 복사하여 붙여넣고 있는 잘 알려진 랜덤 함수가 있습니다. 그 후 표면 셰이더에서 모든 마법이 일어납니다:
먼저 53줄에서 구형 마스크의 중심과 현재 픽셀의 월드 위치 사이의 거리를 구합니다.
그런 다음 편리한 공식을 사용하여 구를 계산합니다: 거리 - 반경 / 부드러움, 54 줄에서 결과를 포화시킨 후 (0,1] 스펙트럼으로 유지하기 위해) 1에서 빼서 반전시킨 후 사용합니다. '
그런 다음 55번째 줄에서 'clip' 기능을 사용하여 실제 용해 작업을 합니다. 잊어버릴까 봐 말씀드리자면 clip(x)는 기본적으로 "x가 0보다 작으면 해당 픽셀 전체를 버리고 그릴 필요도 없습니다. 그렇지 않으면 괜찮으니 계속 진행하세요"라는 뜻입니다.
실제 CG 문서에는 이렇게 명시되어 있지 않지만 요점은 알 수 있습니다. 따라서 마스크는 0에서 1 사이의 값을 제공하므로(부드러움이 0이면 실제로는 0 또는 1을 제공하므로 0으로 나누는 것에 대해 신경 쓰지 않아도 됩니다) 마스크를 잘라내는 것만으로는 별다른 효과가 없습니다. 그래서 0.1을 뺍니다. 이것은 다른 셰이더의 디졸브 양과 동일하지만 반경과 부드러움으로 제어하기 때문에 하드 코딩된 값일 수도 있습니다. 물론 더 많은 제어를 원한다면 다른 프로퍼티로 노출할 수도 있습니다.
다음 부분은 이전 효과에서 보았던 귀여운 이미시브 노이즈에 관한 것입니다. 먼저 오브젝트의 UV 좌표와 노이즈 크기에 따라 랜덤 함수를 사용하여 노이즈의 제곱을 계산합니다. 그런 다음 0.1이 구체 마스크인 0.1보다 큰지 확인하여 방출 링을 계산합니다. 0.1보다 크면 방출이 있어야 하고, 그렇지 않으면 방출이 없어야 합니다.
이것이 바로 구형 마스크의 부드러움이 작용하는 부분입니다. 이 컨텍스트에서는 "소프트" 디졸브(픽셀이 렌더링되거나 렌더링되지 않는 것)와 같은 것이 없기 때문에 소프트니스는 방출 링을 조정할 수 있도록 0에서 0.1 사이의 값을 더 많이 제공하는 데 사용됩니다.
이상하게 보일 수 있지만 마스크 값을 조금만 조정하면 더 선명해집니다. 그런 다음 해당 링에 노이즈의 제곱을 곱하고 전체에 방출 색을 곱하여 출력의 방출 값에 할당합니다.
이 경우 셰이더에서 "허용"하는 유일한 방출은 이미시브 링의 방출이지만 다른 방출 값(예: 텍스처 등)과 결합할 수 있습니다.
구형 마스크 컨트롤러
마스크의 값을 수정하기 위해 다음과 같은 스크립트가 사용됩니다:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[ExecuteInEditMode]
public class SphereMaskController : MonoBehaviour {
public float radius = 0.5f;
public float softness = 0.5f;
void Update () {
Shader.SetGlobalVector ("_GLOBALMaskPosition", transform.position);
Shader.SetGlobalFloat ("_GLOBALMaskRadius", radius);
Shader.SetGlobalFloat ("_GLOBALMaskSoftness", softness);
}
}
이 스크립트는 일부 공용 필드와 해당 필드가 연결된 오브젝트의 위치를 가져와 해당 글로벌 셰이더 프로퍼티로 전달합니다. 이 스크립트에는 특정 머티리얼이나 셰이더가 포함되어 있지 않으며, 값 할당은 "Shader" 클래스의 정적 함수인 "SetGlobalVector/Float"를 통해 이루어집니다.
마스크의 값을 가지고 놀면서 머티리얼에 어떤 영향을 미치는지 확인할 수 있습니다. 예를 들어, 부드러움이 0보다 낮으면 어떤 일이 발생하는지 깔끔하게 보여줍니다:
마스크가 뒤집히고 구 주위의 모든 것이 사라집니다!
꽤 멋지다는 생각이 들었지만, 어느 쪽이든 쉽게 흥분됩니다 ¯\_(ツ)_/¯
결론
셰이더에 대한 모든 것이 여기까지입니다. 구형 마스크는 탐색하기 좋은 깔끔한 효과이며 게임에서 정말 흥미로운 시각 효과를 얻을 수 있으므로 잘 활용하시기 바랍니다!
다음 편에서 뵙겠습니다!
원문
https://halisavakis.com/my-take-on-shaders-spherical-mask-dissolve/