Unity 셰이더를 만드는 방법

셰이더는 조명 입력 및 재질 구성을 기반으로 렌더링된 각 픽셀의 색상을 계산하기 위한 수학적 계산 및 알고리즘이 포함된 작은 스크립트입니다.

Unity 다음 언어로 작성된 셰이더를 사용합니다.

  • HLSL이라는 프로그래밍 언어는 셰이더 프로그램 자체를 작성하는 데 사용됩니다.
  • ShaderLab이라는 Unity 관련 언어는 셰이더 프로그램의 컨테이너 역할을 하는 Shader 개체를 정의하는 데 사용됩니다.

Unity에서 셰이더를 만들려면 아래 단계를 따르세요.

셰이더 만들기

  • 프로젝트 보기를 마우스 오른쪽 버튼으로 클릭 -> 'Create' -> 'Shader'

사용 중인 Unity 버전에 따라 셰이더 옵션이 다를 수 있지만 각 옵션의 의미는 다음과 같습니다.

  1. 'Standard Surface Shader': 이 셰이더는 Unity's PBR(물리 기반 렌더링) 시스템과 함께 작동하도록 설계되었습니다. 이를 통해 개발자는 조명 조건에 현실적으로 반응하는 재질을 만들 수 있습니다. 노멀 매핑, 반사 하이라이트, 반사와 같은 다양한 렌더링 기능을 지원합니다. 현실감과 성능 사이의 적절한 균형을 제공하는 다용도 쉐이더입니다.
  2. 'Unlit Shader': 이름에서 알 수 있듯이 언릿 셰이더는 조명 조건을 고려하지 않습니다. UI 요소, 입자 시스템 또는 특수 효과와 같이 사실적인 조명이 필요하지 않은 효과를 렌더링하는 데 자주 사용됩니다. 언릿 셰이더는 일반적으로 더 효율적이며 조명 계산 없이 객체의 모양을 완전히 제어해야 하는 상황에서 유용할 수 있습니다.
  3. 'Image Effect Shader': 이미지 효과 셰이더는 후처리 효과를 전체 화면이나 특정 렌더 대상에 적용하는 데 사용됩니다. 이를 통해 개발자는 기본 렌더링이 완료된 후 최종 렌더링된 이미지를 수정할 수 있습니다. 이미지 효과의 예로는 흐림, 색상 그레이딩, 왜곡 또는 스타일화된 필터가 있습니다. 시각적 품질을 향상하거나 특정 예술적 효과를 만드는 데 사용할 수 있습니다.
  4. 'Compute Shader': 컴퓨팅 셰이더는 GPU에서 실행되지만 픽셀에서 직접 작동하지 않는 셰이더 유형입니다. 병렬 데이터에 대한 범용 계산에 사용되므로 개발자가 복잡한 계산이나 시뮬레이션을 효율적으로 수행할 수 있습니다. 컴퓨팅 셰이더는 일반적으로 물리 시뮬레이션, 절차 생성 또는 데이터 처리와 같은 작업에 사용됩니다.
  5. 'Ray Tracing Shader': 광선 추적 셰이더는 광선 추적 기술을 활용하여 기존 래스터화 기술에 비해 빛의 동작을 더 정확하게 시뮬레이션합니다. 광선 추적 셰이더는 일반적으로 실시간 애플리케이션에서 매우 사실적인 조명, 반사 및 그림자를 구현하는 데 사용됩니다. 강력한 하드웨어가 필요하며 게임이나 건축 시각화와 같은 그래픽 집약적인 분야에서 자주 활용됩니다.
  • 셰이더를 선택한 후 이름을 입력하고 Enter를 누르세요.

새로운 셰이더가 생성되어 모든 스크립트 편집기에서 열 수 있으며 필요에 맞게 수정할 수 있습니다.

기본값 'Standard Surface Shader':

Shader "Custom/NewSurfaceShader"
{
    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
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        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;
        };

        half _Glossiness;
        half _Metallic;
        fixed4 _Color;

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

        void surf (Input IN, inout SurfaceOutputStandard o)
        {
            // Albedo comes from a texture tinted by color
            fixed4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color;
            o.Albedo = c.rgb;
            // Metallic and smoothness come from slider variables
            o.Metallic = _Metallic;
            o.Smoothness = _Glossiness;
            o.Alpha = c.a;
        }
        ENDCG
    }
    FallBack "Diffuse"
}

기본값 'Unlit Shader':

Shader "Unlit/NewUnlitShader"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        LOD 100

        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            // make fog work
            #pragma multi_compile_fog

            #include "UnityCG.cginc"

            struct appdata
            {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
            };

            struct v2f
            {
                float2 uv : TEXCOORD0;
                UNITY_FOG_COORDS(1)
                float4 vertex : SV_POSITION;
            };

            sampler2D _MainTex;
            float4 _MainTex_ST;

            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv = TRANSFORM_TEX(v.uv, _MainTex);
                UNITY_TRANSFER_FOG(o,o.vertex);
                return o;
            }

            fixed4 frag (v2f i) : SV_Target
            {
                // sample the texture
                fixed4 col = tex2D(_MainTex, i.uv);
                // apply fog
                UNITY_APPLY_FOG(i.fogCoord, col);
                return col;
            }
            ENDCG
        }
    }
}

기본값 'Image Effect Shader':

Shader "Hidden/NewImageEffectShader"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
    }
    SubShader
    {
        // No culling or depth
        Cull Off ZWrite Off ZTest Always

        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"

            struct appdata
            {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
            };

            struct v2f
            {
                float2 uv : TEXCOORD0;
                float4 vertex : SV_POSITION;
            };

            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv = v.uv;
                return o;
            }

            sampler2D _MainTex;

            fixed4 frag (v2f i) : SV_Target
            {
                fixed4 col = tex2D(_MainTex, i.uv);
                // just invert the colors
                col.rgb = 1 - col.rgb;
                return col;
            }
            ENDCG
        }
    }
}

기본값 'Compute Shader':

// Each #kernel tells which function to compile; you can have many kernels
#pragma kernel CSMain

// Create a RenderTexture with enableRandomWrite flag and set it
// with cs.SetTexture
RWTexture2D<float4> Result;

[numthreads(8,8,1)]
void CSMain (uint3 id : SV_DispatchThreadID)
{
    // TODO: insert actual code here!

    Result[id.xy] = float4(id.x & id.y, (id.x & 15)/15.0, (id.y & 15)/15.0, 0.0);
}

기본값 'Ray Tracing Shader':

RWTexture2D<float4> RenderTarget;

#pragma max_recursion_depth 1

[shader("raygeneration")]
void MyRaygenShader()
{
    uint2 dispatchIdx = DispatchRaysIndex().xy;
   
    RenderTarget[dispatchIdx] = float4(dispatchIdx.x & dispatchIdx.y, (dispatchIdx.x & 15)/15.0, (dispatchIdx.y & 15)/15.0, 0.0);
}

결론

각 셰이더 유형에는 고유한 장점과 용도가 있습니다. 특정 요구 사항과 프로젝트에서 달성하려는 시각적 효과를 기반으로 적절한 셰이더를 선택하는 것이 중요합니다.

추천 기사
Unity에서 객체가 마우스 커서를 따르도록 만드는 방법
Unity 코드에서 JSON을 사용하는 내장된 작업 방법
Unity에서 값을 초기화하는 런타임 시작 시 메서드
Unity에서 컷씬을 트리거하는 방법
Unity에서 더 나은 프로그래머가 되는 방법
Unity 난독화 방법 및 해킹 방지 보호
Poppy Playtime에서 영감을 받아 Unity에서 GrabPack 만들기