Unity Shaders and Graphics

유니티 셰이더와 그래픽스 (Unity Shaders and Graphics)

소개(Introduction):
유니티의 셰이더와 그래픽스 시스템은 게임의 비주얼 퀄리티를 향상시키는 중요한 요소입니다. 셰이더는 GPU에서 실행되는 작은 프로그램으로, 각 픽셀의 색상과 특성을 결정합니다. 유니티에서는 표준 셰이더 외에도 커스텀 셰이더를 작성하여 독특한 그래픽 효과를 구현할 수 있습니다. 이 가이드는 셰이더의 기본 개념과 유니티에서 그래픽스를 다루는 방법에 대해 상세히 설명하며, 다양한 예제를 통해 실제 구현 방법을 제공합니다.

셰이더 기본 개념(Shader Basics):
셰이더는 크게 두 가지로 나뉩니다: 버텍스 셰이더와 프래그먼트 셰이더. 버텍스 셰이더는 각 정점(Vertex)의 위치와 데이터를 처리하고, 프래그먼트 셰이더는 각 픽셀(Fragment)의 색상과 특성을 처리합니다. 유니티에서는 셰이더를 작성하기 위해 HLSL(High-Level Shading Language)을 사용합니다. 아래는 hlsl로 작성된 코드입니

Shader "Custom/ExampleShader" {
    Properties {
        _MainTex ("Texture", 2D) = "white" {}
        _Color ("Main Color", Color) = (1,1,1,1)
    }
    SubShader {
        Tags { "RenderType"="Opaque" }
        LOD 200

        CGPROGRAM
        #pragma surface surf Lambert

        sampler2D _MainTex;
        fixed4 _Color;

        struct Input {
            float2 uv_MainTex;
        };

        void surf (Input IN, inout SurfaceOutput o) {
            fixed4 c = tex2D(_MainTex, IN.uv_MainTex) * _Color;
            o.Albedo = c.rgb;
            o.Alpha = c.a;
        }
        ENDCG
    }
    FallBack "Diffuse"
}

머티리얼(Materials)과 셰이더(Materials and Shaders):
셰이더는 머티리얼을 통해 적용됩니다. 유니티 에디터에서 머티리얼을 생성하고, 해당 머티리얼에 셰이더를 할당한 후 게임 오브젝트에 적용할 수 있습니다.

using UnityEngine;

public class ApplyMaterial : MonoBehaviour {
    public Material customMaterial;

    void Start() {
        Renderer renderer = GetComponent<Renderer>();
        if (renderer != null) {
            renderer.material = customMaterial;
        }
    }
}

셰이더 그래프(Shader Graph):
유니티는 셰이더를 시각적으로 작성할 수 있는 Shader Graph 툴을 제공합니다. 이를 통해 코드를 작성하지 않고도 노드를 연결하여 복잡한 셰이더를 만들 수 있습니다.

간단한 셰이더 그래프 예제(Simple Shader Graph Example):

  1. 프로젝트 창에서 우클릭하고 Create > Shader > PBR Graph를 선택합니다.
  2. 새로운 셰이더 그래프를 열고, 노드를 추가하여 셰이더를 구성합니다.
  3. 생성한 셰이더 그래프를 머티리얼에 적용하고, 게임 오브젝트에 할당합니다.

포스트 프로세싱(Post-Processing):
포스트 프로세싱은 화면에 렌더링된 이미지를 후처리하여 다양한 비주얼 효과를 추가하는 기술입니다. 유니티는 포스트 프로세싱 스택을 통해 블룸, 색 보정, 안티앨리어싱 등의 효과를 제공합니다.

using UnityEngine;
using UnityEngine.Rendering.PostProcessing;

public class ApplyPostProcessing : MonoBehaviour {
    public PostProcessVolume volume;
    public PostProcessProfile profile;

    void Start() {
        if (volume == null) {
            volume = gameObject.AddComponent<PostProcessVolume>();
        }
        volume.profile = profile;
        volume.isGlobal = true;
    }
}

프래그먼트 셰이더(Fragment Shader):
프래그먼트 셰이더는 각 픽셀의 색상을 계산합니다. 예를 들어, 단순한 색상 그라데이션을 구현하는 프래그먼트 셰이더는 다음과 같습니다. 아래는 hlsl로 작성되었습니다.

Shader "Custom/GradientShader" {
    SubShader {
        Pass {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            struct appdata {
                float4 vertex : POSITION;
            };

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

            v2f vert(appdata v) {
                v2f o;
                o.position = UnityObjectToClipPos(v.vertex);
                o.uv = v.vertex.xy * 0.5 + 0.5;
                return o;
            }

            fixed4 frag(v2f i) : SV_Target {
                return fixed4(i.uv, 0, 1);
            }
            ENDCG
        }
    }
}

셰이더 변수와 유니폼(Shader Variables and Uniforms):
셰이더는 Properties 블록을 통해 외부에서 값을 전달받을 수 있습니다. 이를 통해 텍스처, 색상, 숫자값 등을 셰이더에 전달할 수 있습니다. 아래는 hlsl 입니다

Shader "Custom/UniformShader" {
    Properties {
        _Color ("Main Color", Color) = (1,1,1,1)
    }
    SubShader {
        Pass {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            fixed4 _Color;

            struct appdata {
                float4 vertex : POSITION;
            };

            struct v2f {
                float4 position : SV_POSITION;
            };

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

            fixed4 frag(v2f i) : SV_Target {
                return _Color;
            }
            ENDCG
        }
    }
}

예제: 물 셰이더(Example: Water Shader):
물의 효과를 구현하기 위한 간단한 물 셰이더 예제입니다. 노멀 맵을 사용하여 물결 효과를 추가할 수 있습니다. 아래는 hlsl입니다.

Shader "Custom/WaterShader" {
    Properties {
        _MainTex ("Texture", 2D) = "white" {}
        _NormalMap ("Normal Map", 2D) = "bump" {}
        _Color ("Main Color", Color) = (0.0, 0.5, 1.0, 1.0)
        _WaveSpeed ("Wave Speed", float) = 0.5
    }
    SubShader {
        Tags { "RenderType"="Transparent" "Queue"="Transparent" }
        LOD 200

        CGPROGRAM
        #pragma surface surf Lambert alpha

        sampler2D _MainTex;
        sampler2D _NormalMap;
        fixed4 _Color;
        float _WaveSpeed;

        struct Input {
            float2 uv_MainTex;
            float2 uv_NormalMap;
        };

        void surf (Input IN, inout SurfaceOutput o) {
            fixed4 c = tex2D(_MainTex, IN.uv_MainTex) * _Color;
            float4 normalTex = tex2D(_NormalMap, IN.uv_NormalMap);
            float3 bump = UnpackNormal(normalTex);
            bump.xy *= _WaveSpeed;
            o.Normal = bump;
            o.Albedo = c.rgb;
            o.Alpha = c.a;
        }
        ENDCG
    }
    FallBack "Transparent"
}

유니티의 셰이더와 그래픽스 시스템을 이해하고 활용하면, 게임의 비주얼을 크게 향상시킬 수 있습니다. 위 예제들을 통해 셰이더를 작성하고 적용하는 방법을 배우면, 보다 복잡하고 독창적인 그래픽 효과를 구현할 수 있습니다.

Leave a Reply

Your email address will not be published. Required fields are marked *