Basic Shader
Summary In the last three tutorials I explained some of the cornerstones of how shaders work. In this one I show you how to fill in the rest. The main thing I didn’t show was actual executed code. Thats because you don’t need much code to get a shader
www.ronja-tutorials.com
Ronja 님의 허락을 받고 번역한 튜토리얼입니다. 원문은 위 링크에서 확인하실 수 있습니다.
몇몇 부분은 생략·추가하였습니다.
의역과 오역이 넘쳐날 수 있으니 편하게 봐주시고 잘못된 부분은 알려주시면 감사하겠습니다!
목차
- 개요
- 지금까지 배운 것
- 셰이더 스테이지 세팅
- 버텍스 스테이지
- 프래그먼트 스테이지
개요
지난 세 개의 튜토리얼에서는 셰이더가 어떻게 동작하는지 기본 원리에 대해서 설명했습니다. 이번 글에서는 나머지 부분들을 어떻게 채우는지 보여드리겠습니다.
주요한 점은, 실제 실행되는 셰이더 코드를 보여주지 않았다는 것입니다. 이는 처음에는 셰이더를 실행하는 데에 많은 코드가 필요하지 않기 때문입니다. 모든 멋진 코드들은 전문적인 튜토리얼들에 있습니다.
지금까지 배운 것
여기 있는 모든 내용은 이전 세 개의 튜토리얼에서 설명된 바 있습니다.
Shader "Tutorial/001-004_Basic_Unlit"
{
//인스펙터에서 조작할 수 있는 값들이 보여집니다.
Properties
{
_Color ("Tint", Color) = (0, 0, 0, 1)
_MainTex ("Texture", 2D) = "white" {}
}
SubShader
{
//메테리얼은 완전히 불투명하고, 다른 불투명 지오메트리와 같은 타이밍에 렌더됩니다.
Tags{ "RenderType"="Opaque" "Queue"="Geometry" }
Pass
{
CGPROGRAM
//텍스처와 텍스처 좌표
sampler2D _MainTex;
float4 _MainTex_ST;
//텍스처의 컬러 틴트
fixed4 _Color;
//버텍스 셰이더가 읽는 메쉬 데이터
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
};
//버텍스에서 프래그먼트 셰이더로 래스터라이저를 통해 보간되어 전달되는 데이터
struct v2f
{
float4 position : SV_POSITION;
float2 uv : TEXCOORD0;
};
ENDCG
}
}
Fallback "VertexLit"
}
셰이더 스테이지 세팅하기
각각의 셰이더 스테이지는 HLSL 함수로 표현됩니다. 어떤 함수가 어떤 스테이지를 나타내는지를 정의하기 위해서는 #pragma 구문을 추가해야 합니다. 버텍스 스테이지에서 중요한 점은 버텍스 데이터를 입력받아 보간자 (Interpolator) 에게 반환한다는 것이며, 프래그먼트 스테이지에서 중요한 점은 보간된 값을 입력받아 렌더 타겟에 벡터로 반환한다는 것입니다.
따라서 이는 다음과 같이 생겼습니다.
//버텍스와 프래그먼트 셰이더 함수를 정의합니다.
#pragma vertex vert
#pragma fragment frag
//버텍스 셰이더 함수
v2f vert(appdata v)
{
//버텍스 스테이지 코드
}
//프래그먼트 셰이더 함수
fixed4 frag(v2f i) : SV_TARGET
{
//프래그먼트 스테이지 코드
}
버텍스 스테이지
버텍스 스테이지를 정의하는 함수의 시작 부분에서는 마지막에 반환할 보간자 구조체를 생성합니다. 그런 다음 구조체에 데이터를 채우고 반환합니다.
버텍스 스테이지의 주요 작업은 버텍스 포지션을 로컬 오브젝트 스페이스에서 버텍스 위치를 렌더할 수 있는 클립 스페이스로 변환하는 것입니다. 이것은 내부적으로 행렬 곱셈을 통해 수행되지만, 유니티에서 행렬 곱셈 함수를 제공하기 때문에 우리는 행렬 곱셈에 대해 이해하지 않아도 됩니다. 이 매크로들 (그리고 다른 유용한 코드들) 을 우리의 셰이더 내에서 사용하기 위해서는 #include "UnityCG.cginc" 구문을 통해 include 파일을 가져와야 합니다. 버텍스 포지션을 로컬 스페이스에서 클립 스페이스로 변환하는 함수는 UnityObjectToClipPos 라는 함수입니다. 또, UnityCG 파일은 버텍스 데이터와 같이 전달된 uv 좌표를 에디터에서 설정한 타일링과 오프셋이 적용된 uv 좌표로 변환하는 매크로 (함수와 유사한 역할) 를 가지고 있습니다. 이 매크로의 이름은 TRANSFORM_TEX 이며, 기본 uv 좌표와 텍스처 이름을 인자로 받습니다.
이러한 내용이 적용된 버텍스 함수는 아래와 같이 나타낼 수 있습니다.
//버텍스 셰이더 함수
v2f vert(appdata v)
{
v2f o;
//맞게 렌더될 수 있도록 버텍스 좌표를 오브젝트 공간(Object Space)에서
//클립 공간(Clip space)으로 변환합니다.
o.position = UnityObjectToClipPos(v.vertex);
//텍스처 트랜스폼을 UV좌표에 적용해서 v2f 구조체로 넘깁니다.
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
return o;
}
프래그먼트 스테이지
프래그먼트 스테이지에서는 보간자를 입력값으로 받아 유니폼 변수와 함께 사용하여 픽셀이 어떤 색상을 가져야 하는지 결정합니다. 프래그먼트 함수의 가장 간단한 버전은 하얀색 메시를 출력하는 return float4(1, 1, 1, 1); 이지만, 대부분의 경우에서는 조금 더 복잡한 결과와 텍스처를 사용하고는 합니다.
텍스처에 접근하기 위해서는 보통 Tex2D() 함수를 사용하는데, 이 함수는 첫 번째 인자로 텍스처를, 두 번째 인자로 uv 좌표를 받고, 해당 좌표의 색상을 반환합니다. 아래 예제에서는 결과 색상을 저장하고, 유니폼 변수로 정의한 색상 값과 곱한 후 그 결과를 반환할 것입니다.
//프래그먼트 셰이더 함수
fixed4 frag(v2f i) : SV_TARGET
{
//UV좌표의 텍스처 컬러를 읽습니다.
fixed4 col = tex2D(_MainTex, i.uv);
//텍스처 컬러에 틴트 컬러를 곱합니다.
col *= _Color;
//화면에 그려질 최종 색상을 반환합니다.
return col;
}
위의 내용을 토대로 셰이더를 작성하면, 여러분만의 첫 번째 셰이더를 완성하게 됩니다!
'Graphics > Ronja's Unity Shader tutorials' 카테고리의 다른 글
기본 투명 셰이더 (0) | 2024.01.21 |
---|---|
유니티 서피스 셰이더 기본 (0) | 2023.11.19 |
변수 (0) | 2023.04.22 |
HLSL (0) | 2022.11.09 |
유니티 셰이더 구조 (0) | 2022.11.09 |