| Shader 코드 (HLSL, OpenGL)도 결국은 다른 프로그래밍 언어와 같다. 규격화된 사용 방법이 존재하며, 구조를 먼저 이해하면 내부 코드도 쉽게 이해가 갈 것이다. 방식은 아무래도 OpenGL부터 이어져온 오래된 방식이다 보니 절차지향식 C에 가깝다. |
01. Shader의 시작 부분
셰이더의 시작부분
Shader "CustomFolder/HelloShader"
}
// Shader 내부
}
Shader의 시작 부분이다.
"CustomFolder" 라는 Path를 지정하고, HelloShader를 선택하여 해당 Shader를 적용한다.
02. Shader의 내부 구조
셰이더 내부 구조
Shader "CustomFolder/HelloShader"
{
Tags {...}
properties{...}
SubShader
{
Tags {...}
Pass
{
Tags {...}
...
}
Pass {...}
...
}
SubShader
{...}
}
*감싸는 구조를 "부분 (Wrapper)"라고 표현 하겠다.
Shader의 내부구조는 다음과 같다.
Tags 라는 이름으로, 해당 Shader의 설정 부분
- Tags에는 RenderType, RenderPipeline, Queue 등으로 어떻게 렌더링 시키고, 어떤 환경에서 동작 시킬지 설정하는 부분이다.
Tags
{
"RenderType"= "QUEUE"
"QUEUE" = "Geometry" // Render 순서
"RenderPipeline" = "UniversalPipeline"
}
Properties 라는 이름으로, 해당 Shader의 사용할 변수 설정 부분
- 해당 Shader의 입력값들을 설정하는 부분이다. Color, 2DSampler, float 등 변수들을 등록하는 부분이다.
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 라는 이름으로, 여러 환경에 따라 Shader를 다양하게 설정해야 할 필요가 있다.
이를 설정하는 부분이다.
SubShader에는 여러가지 Shader Effect 들을 구현할 수 있다.
이를 Pass 키워드를 통해, 순차적으로 적용 할 수 있게 한다.
| 복잡해보이지만, 거의 모든 Shader들이 해당 방식을 따라 구현된다. HLSL 코드의 기본적인 구조는 이해했다고 보면 된다. |
https://lucid-boundary.tistory.com/115
1. Shader 란? - 다니엘 릿 쉐이더 프로젝트
Shader를 알기 위해서는.. 기초적인 그래픽스 지식과 수학이 필요하다.우선, 필요한 지식들을 먼저 이해해보자. RendererRenderering은 일종의 데이터 처리를 아래 처럼, 순차적으로 하는 단계라고 한
lucid-boundary.tistory.com
참고 자료 : RenderProcess
03. 구조체 (Struct)를 이용한 데이터 흐름 관리
우리가 사용할 Shader의 입력 값은 Vertext 정보, Color 정보, 오브젝트의 위치값등 Mesh에 대한 정보들은 굉장히 다양하다.
이 중에서 필요한 것만 Struct화 시켜서, Shader의 입력 값으로 활용한다.
struct appdata // struct 이름은 원하는 대로.
{
float4 positionOS : POSITION
}
우선, Shader Programming에서 지원하는 Shader 함수들을 선언한다.
#pragma를 이용하여 다음 처럼 ShaderFunction을 가져온다.
Pass
{
HLSLPROGRAM
#pragma vertex vert
#pragma fragment frag
ENDHLSL
}
이후에는 v2f (vertext-to-fragment) 구조체를 이용하여 Vertext Shader와 Fragment Shader를 처리할 수 있다.
struct v2f
{
float4 positionCS : SV_POSITION;
};
여기서 중요하게 짚고 넘어가야 할 것이 있다.
RenderProcess를 보면, Vertext -> Fragment 프로세스 사이에는 픽셀화 단계가 존재한다.
기본적으로 오브젝트의 렌더링은 우리의 눈에 보이는 화면 (2D 공간)에 렌더링하는 것이다.
Vertext Shader를 이용하여, 오브젝트의 Vertex 관련 처리를 한 뒤에 View Space으로 변환해주는 작업이 필요하다.
v2f vert (appdata v)
{
v2f o;
// o.positionCS = UnityObjectToClipPos(v.positionOS) built-in
o.positionCS = TransformObjectToHClip(v.positionOS)
return o;
}
이후에는 Fragment Shader를 구현해주면 된다.
float4 frag(v2f i) : SV_TARGET
{
return _BaseColor;
}
04. Shader를 이용한 최적화 진행
유니티에는 SRP Batcher와 GPU Instancing 기능이 존재한다.
여러 오브젝트가 같은 셰이더를 사용하고 있다면, 여러 오브젝트를 계속 렌더링을 하는 것보다
이미 동일한 셰이더를 이용하여 한번에 렌더링 하는 게 더 쉬울 것이다.
SRP Batcher와 GPU Instancing은 동시에 사용 할 수는 없다.
성능은 GPU Instnacing가 훨씬 좋지만, 대신에 복잡한 메쉬, 매트리얼을 사용할 때는 SRP Batch가 좀 더 효율적이다.
SRP Batcher를 사용 하기 위해서는 CBuffer를 정의해야 한다.
CBUFFER_START(UnityPerMaterial) ... CBUFFER_End
(GPU Instancing의 경우는 )
UNITY_INSTANCING_BUFFER_START(Props)
// put more per-instance properties here
UNITY_INSTANCING_BUFFER_END(Props)
05. 마치며
지금까지 아주 기본적인 Shader 구조에 대해 알아보았다.
다들 알겠지만, 거의 대부분의 멋있는 효과들은 Shader Graph만 이용해도 할 수 있다.
하지만, 아는만큼 보인다고. Shader Programming을 잘 알면, 논문 구현이나 Graph로는 한계가 있는 이펙트마저 자력으로 만들 수 있을 것이다.
'Game Dev > Unity Shader' 카테고리의 다른 글
| 05. Texturing 심화 - 다니엘 릿 쉐이더 프로젝트 (0) | 2025.10.11 |
|---|---|
| 04. 본격적인 Shader의 세계, Textures and UV Coordinates - 다니엘 릿 쉐이더 프로젝트 (1) | 2025.10.09 |
| 03. 코드 없이 쉐이더를 다룰 수 있는 Shader Graph - 다니엘 릿 쉐이더 프로젝트 (0) | 2025.10.09 |
| 01. Shader 란? - 다니엘 릿 쉐이더 프로젝트 (6) | 2025.03.30 |
| 00. Shader 관련해서 글을 작성하고자 합니다. (1) | 2025.03.28 |