https://lucid-boundary.tistory.com/131
11. Tranparancy And Alpha 심화 - 다니엘 릿 쉐이더 프로젝트
Alpha ClippingOpaque 오브젝트라 할 지라도, Alpha Clipping을 이용하면 Alpha 값을 이용 할 수 있다.Texture 자체에 Transparency 한 부분이 있다면, Alpha Clipping을 쓸 수 있다.Alpha Clipping이란, 커스터마이징 가능
lucid-boundary.tistory.com
이전 포스팅에서 다루었던 Dissolve 이펙트를 개선하기 위한 포스팅이다.
이전 디졸브는 Height에 대한 값만 진행되었다.
유니티는 3D 엔진이다. 하나의 축만 디졸브 시키는건 매우 이상하게 보일 것이다.
이번 포스팅에서는 실제 오브젝트의 정보를 받아 자연스러운 디졸브 이펙트를 연출 할 것이다.
아래는 기존 코드 샘플이다. 해당 샘플을 수정 할 것이다.
Shader "Lucid-Boundary/Tranparent_DissolveEffect"
{
Properties
{
_MainTex ("Texture", 2D) = "white" {}
_BaseColor("Base Color", Color) = (1, 1, 1, 1)
_CutoffHeight("Cutoff Height", Float) = 0.0
_NoiseScale("Noise Scale", Float) = 20
_NoiseStrength("Noise Strength", Range(0.0, 1.0)) = 0.5
}
SubShader
{
Tags
{
"RenderType"="Opaque"
"Queue" = "AlphaTest"
}
Pass
{
Cull Off
HLSLPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
};
struct v2f
{
float2 uv : TEXCOORD0;
float4 vertex : SV_POSITION;
float4 positionOS : TEXCOORD1;
};
texture2D _MainTex;
SamplerState sampler_MainTex;
CBUFFER_START(UnityPerMaterial)
float4 _MainTex_ST;
float4 _BaseColor;
float _CutoffHeight;
float _NoiseScale;
float _NoiseStrength;
CBUFFER_END
v2f vert (appdata v)
{
v2f o;
o.vertex = TransformObjectToHClip(v.vertex.xyz);
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
o.positionOS = v.vertex;
return o;
}
// Generate a grid corner random unit Vector.
float2 generateDir(float2 p)
{
p = p % 289;
float x = (34 * p.x + 1) * p.x % 289 + p.y;
x = (34 * x + 1) * x % 289;
x = frac (x / 41) * 2 - 1;
return normalize(float2(x - floor(x + 0.5), abs(x) - 0.5));
}
float2 generateNoise(float2 p)
{
float2 ip = floor(p);
float2 fp = frac(p);
// calculate the nearest four grid point vectors
float d00 = dot(generateDir(ip), fp);
float d01 = dot(generateDir(ip + float2(0, 1)), fp - float2(0, 1));
float d10 = dot(generateDir(ip + float2(1, 0)), fp - float2(1, 0));
float d11 = dot(generateDir(ip + float2(1, 1)), fp - float2(1, 1));
//Do 'smootherstep' between the dot products then bilinearly interpolate.
fp = fp * fp * fp * (fp * (fp * 6 - 15) + 10);
return lerp(lerp(d00, d01, fp.y), lerp(d10, d11, fp.y), fp.x);
}
float gradientNoise(float2 UV, float Scale)
{
return generateNoise(UV * Scale) * 2.0f;
}
float4 frag (v2f i) : SV_Target
{
float noiseSample = gradientNoise(i.uv, _NoiseScale) * _NoiseStrength;
float noisyPosition = i.positionOS.y + noiseSample;
if(noisyPosition > _CutoffHeight) discard;
float4 col = SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, i.uv);
float4 outputColor = col * _BaseColor;
return outputColor;
}
ENDHLSL
}
}
}
struct v2f
{
float2 uv : TEXCOORD0;
float4 vertex : SV_POSITION;
float4 positionWS : TEXCOORD1;
};
V2f에 positionWS 라는 이름으로 새 UV 채널을 추가한다.
이는 World Space의 정보를 담을 채널이다.
v2f vert (appdata v)
{
v2f o;
o.vertex = TransformObjectToHClip(v.vertex.xyz);
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
o.positionWS = mul(unity_ObjectToWorld, v.vertex);
return o;
}
vertex 함수에 psoitionWS 값을 넣어주고 있다.
https://docs.unity3d.com/kr/2018.4/Manual/SL-UnityShaderVariables.html
빌트인 셰이더 변수 - Unity 매뉴얼
Unity는 셰이더에 사용할 수 있는 여러 빌트인 전역 변수를 제공합니다. 예를 들어 현재 오브젝트의 변환 매트릭스, 광원 파라미터, 현재 시간 등이 있습니다. 이 변수는 다른 변수와 마찬가지로
docs.unity3d.com
unity_ObjectToWorld 는 현재 쉐이더가 적용되는 모델의 matrix 값이다.
World Space Position에 각 Vertext가 위치할 좌표를 UV에 저장한다고 생각하자.
float4 frag (v2f i) : SV_Target
{
float noiseSample = gradientNoise(i.uv, _NoiseScale) * _NoiseStrength;
// float noisyPosition = i.positionOS.y + noiseSample;
float3 noisyPosition = i.positionWS.xyz + _PlaneNormal.xyz * noiseSample;
float3 offset = noisyPosition - _PlaneOrigin;
if(dot(offset, _PlaneNormal) > 0.0f) discard;
float4 col = SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, i.uv);
float4 outputColor = col * _BaseColor;
return outputColor;
}
Fragment Shader에서 바뀐 부분은 noisy position이다.
일단 디졸브 자체는 머리에서부터 발까지 사라지게 하는 연출이니, _PlaneNormal (== Transform.up) 방향으로 노이즈를 더해준다.
noisyPosition과 _PlaneOrigin (== Transform.position) 은 노이즈 만큼의 차이가 난다.
둘을 Dot Product 했을 때, 변화가 존재한다면 (0이 아닌 값이 된다면) 픽셀을 버려버린다.
해당 쉐이더를 그대로 매트리얼로 만들어서 적용하면, 적용이 되지 않는다.
이유는 _PlaneNormal, _PlaneOrigin이 초기화 되지 않았기 때문이다.
새로운 Plane 오브젝트를 만들어서, Material을 등록해주자.
using UnityEngine;
public class ImprovedDissolve_Script : MonoBehaviour
{
[SerializeField] MeshRenderer disRender;
[SerializeField] Material mat;
// Start is called once before the first execution of Update after the MonoBehaviour is created
void Start()
{
mat = disRender.material;
}
private void Update()
{
if (mat == null) return;
mat.SetVector("_PlaneOrigin", transform.position);
mat.SetVector("_PlaneNormal", transform.up);
}
}

쉐이더를 C# 코드와 상호작용하기
위 디졸브 이펙트에서 유니티 스크립트를 통해 Trnasform을 등록 해줬다.
이처럼, Properties뿐만 아니라 게임내 동작을 하면서 쉐이더에 값을 넣어줄 방법이 필요하다.
Material은 Shader로부터 나온거니, Material을 대상으로 여러 함수가 제공된다.
| SetBuffer | ComputeBuffer나 GraphicsBuffer를 입력해준다. |
| SetColor | RGBA 컬러를 입력해준다. |
| SetFloat | Float 값을 입력 해준다. |
| SetInt | Integer 값을 입력 해준다. |
| SetMatrix | 4 x 4 Matrix를 입력 해준다. |
| SetTexture | Texture2D, Texture3D, Cubemap으로 동작할 Texture를 입력 해준다. |
| SetTextureOffset | Offset Vector2를 입력 해준다. |
| SetTextureScale | Scale Vector2 를 입력 해준다. |
| SetVector | Vector4를 입력 해준다. |
위의 함수들은 모두 GPU 상을 통해 실행된다.
반대로 Material로부터 값을 가져올 수 있다.
Set 대신에 Get으로 바꿔주자
(GetColor("BaseColor"))
매트리얼 변경은 애니메이션에서도 적용 가능하다.

마치며
이번 포스팅에서는 디졸브 이펙트를 개선한 것
C# 코드에서 매트리얼을 수정 할 수 있는 코드를 설명하였다.
어렵지 않은 부분이니 포기하지 말자
'Game Dev > Unity Shader' 카테고리의 다른 글
| 14. UsePass, GrapPass - 다니엘 릿 쉐이더 프로젝트 (0) | 2025.10.18 |
|---|---|
| 13. Shader Keywords - 다니엘 릿 쉐이더 프로젝트 (0) | 2025.10.18 |
| 11. Tranparancy And Alpha 심화 - 다니엘 릿 쉐이더 프로젝트 (0) | 2025.10.17 |
| 10. Transparency And Alpha - 다니엘 릿 쉐이더 프로젝트 (0) | 2025.10.17 |
| 09. Depth Buffer 심화 (3) - 다니엘 릿 쉐이더 프로젝트 (0) | 2025.10.16 |