알라딘MGG와이드바


(번역글) 렌더몽키 초보자용 튜토리얼 RenderMonkey Beginner’s Tutorial 개발 이야기

원본은 [http]David Gouveia 의 Musings on Game Development with XNA 입니다. RenderMonkey 매뉴얼을 이것저것 살펴봤지만 처음 해 보기에는 이 글이 가장 쉬웠기 때문에 블로그 주인장에게 허락맡고 간단하게 번역해 올립니다. 잘못된 점은 언제든지 알려주세요. 렌더몽키로 쉐이더를 배우려면 포프님의 블로그(블라인드 렌더러)에서 진행중인 [http]셰이더 강좌 를 참고하시는게 가장 좋겠네요.

RenderMonkey 초보자 튜토리얼

초보자 용으로 RenderMonkey 를 쓰는 법을 설명하겠습니다. RenderMonkey 는 AMD 에서 쉐이더 코드를 만들고 테스트해 볼 수 있는 개발 프로그램입니다. 비록 AMD 는 렌더몽키 개발을 그만두었지만 여전히 [http]여기에서 공짜로 받아서 쓸 수 있습니다.

예제에서는 HLSL 를 쓰겠습니다만, HLSL 을 어떻게 쓰느냐보다는 RenderMonkey 를 어떻게 셋팅하고 실행하느냐에 집중할 겁니다. 만약 HLSL 에 대해서 잘 모른다면 [http]Catalin Zima 님이 작성한 [http]멋진 튜토리얼을 참고합시다. 문법에 대해 배운 후에는 XNA 교육란을 통해서 간단한 쉐이더를 어떻게 구현하는지도 같이 배워봅시다.

쉐이더 코드

이번 쉐이더에서는 diffuse texture map 에 ambient 와 diffuse 라이팅을 쓸 겁니다. 먼저 shader 코드를 봅시다.
   1: float4x4 WorldViewProjection;
   2: float4x4 WorldInverseTranspose;
   3: float3 LightDirection;
   4: float4 Ambient;
   5: sampler Texture;
   6:  
   7: struct VS_INPUT
   8: {
   9:    float4 position : POSITION0;
  10:    float3 normal : NORMAL0;
  11:    float2 texturecoord : TEXCOORD0;
  12: };
  13:  
  14: struct VS_OUTPUT
  15: {
  16:    float4 position : POSITION0;
  17:    float3 normal : TEXCOORD0;
  18:    float2 texturecoord : TEXCOORD1;
  19: };
  20:  
  21: VS_OUTPUT vs_main(VS_INPUT input)
  22: {
  23:    VS_OUTPUT output;
  24:    output.position = mul(input.position, WorldViewProjection);
  25:    output.normal = mul(input.normal, WorldInverseTranspose);
  26:    output.texturecoord = input.texturecoord;
  27:    return output;
  28: }
  29:  
  30: float4 ps_main(VS_OUTPUT input) : COLOR0
  31: {
  32:    float3 normal = normalize(input.normal);
  33:    float3 light = normalize(LightDirection);
  34:    float4 diffuse = tex2D(Texture, input.texturecoord);
  35:    float diffuseContribution = clamp(dot(normal, -light), 0, 1);
  36:    return Ambient + diffuse * diffuseContribution;
  37: }

다 완성되면 아래와 같은 주전자를 볼 수 있을 겁니다. 그럼 시작해 봅시다.
0.jpg


이펙트 만들기

처음 RenderMonkey 를 실행하면 아래와 같이 Empty Workspace 상태일 겁니다.
01.jpg


많은 작업을 왼편에 있는 Workspace Window 에서 하게 됩니다. 이 창은 기본으로 열려 있습니다. 먼저 새로운 이펙트 를 추가해봅시다. Effect Workspace 에서 우클릭한 후 원하는 타입의 Default Effect 를 추가합니다. HLSL 를 쓸 거라서 DirectX 쉐이더를 생성합니다. shader 를 작성할 때 써 먹을 수 있는 여러 템플릿이 있지만 이번 튜토리얼에서는 최대한 간단한 쉐이더를 만드는 "DirectX" 를 선택합니다.(정점쉐이더에서는 단순히 WorldViewProjection 변환만 하고, 픽셀쉐이더에서는 한 가지 색만 출력합니다).
02.jpg


이펙트를 생성하고 나면 아래와 같을 겁니다.
03.jpg


이제 시작할 준비가 되었습니다.

Model 변경

RenderMonkey 의 기본 모델은 구입니다만 쉐이더를 테스트하기에는 그다지 좋은 모델이 아니므로 바꿔봅시다. Workspace Window 의 Model 위에서 우클릭한 후 새로운 모델을 선택합니다.
04.jpg


주전자가 좋겠네요.
05.jpg


변수 설정

다음 단계에서는 RenderMonkey 와 shader 에서 쓸 전역 변수를 연결합니다. 우리가 구현할 쉐이더에서 쓸 전역 변수는 아래와 같습니다.
   1: float4x4 WorldViewProjection;
   2: float4x4 WorldInverseTranspose;
   3: float3 LightDirection;
   4: float4 Ambient;
   5: sampler Texture;

쉐이더 코드에 필요한 전역변수별로 Workspace Window 의 Effect 내에 새로운 변수를 만들어줘야 RenderMonkey 에서 쉐이더 데이터값을 설정할 수 있습니다. Workspace Window 에서 만든 변수와 쉐이더의 변수를 이름과 타입 모두 같게 만들어주세요. 새로운 변수를 생성하려면 Effect 에서 우클릭 -> Add Variable -> 목록에서 적당한 데이터 타입을 선택하면 됩니다.
06.jpg


Float3 타입인 LightDirection 와 Color 타입인 Ambient 를 추가합시다.

WorldViewProjection 나 WorldInverseTranspose 같은 행렬은 특수한 의미가 있으므로 RenderMonkey 에서 이런 행렬과 scene 을 시맨틱(semantic)으로 연결시켜 줘야 합니다(예 : View 행렬을 카메라와 연결한다던지). 이를 위해서는 적당한 시맨틱이 할당되어 있는 Predefined 행렬 중에 하나를 쓰면 됩니다. 변수이름에 mat~ 이라는 prefix 가 붙어있을텐데 쉐이더코드 변수 이름과 같게 만들기 위해서 Workspace 의 변수이름에서 F2 를 눌러서 이름을 변경해 줍시다.
07.jpg


마지막으로 shader 에서 샘플링할 texture 변수를 만들려면 다음 두 단계가 필요합니다.
먼저, Effect 에서 우클릭한 후, Add Texture -> 원하는 2D Texture 를 선택합니다. (Examples/Media/Textures/Hex.dds 를 선택합시다.)
08.jpg


다음에는 Pass 0 위에서 우클릭 -> Add Texture Object -> 방금 만든 텍스터(hex)를 선택해 link 시킵니다. 이렇게 만든 Texture Object 의 이름은 쉐이더에서 쓸 샘플러 이름과 같아야 합니다(Texture0 라는 이름이 붙을텐데 우리는 Texture 라는 이름을 쓸 거니 바꿔줍니다).
09.jpg


이펙트에 기본으로 들어있던 행렬(matViewProjection)은 필요없으므로 지웁니다. 컴파일하면 RENDERING ERROR(s): Missing shader constant parameter named 'matViewProjection' in vertex shader 'Vertex Shader' in pass 'Pass 0' 이런 에러가 뜰텐데 일단 무시합니다.

여기까지 했다면 Effect 가 다음과 같이 보여야 합니다.
10.jpg


쉐이더 입력 바인딩

그 다음 해야 하는 작업은 (종종 까먹기 쉬운데) RenderMonkey 를 정점쉐이더 입력값에 바인딩시켜서 원하는 데이터가 스트리밍되도록 하는 것입니다. 이전에 보여준 코드에는 정점쉐이더 입력 구조체가 아래와 같습니다.

   1: struct VS_INPUT
   2: {
   3:    float4 position : POSITION0;
   4:    float3 normal : NORMAL0;
   5:    float2 texturecoord : TEXCOORD0;
   6: };

이 구조체를 RenderMonkey 로 표현해 줘야 합니다. Effect 트리의 Stream Mapping 에서 우클릭한 후 Edit 를 선택하면 아래와 같은 창이 뜹니다.
11.jpg


채널 두 개를 추가한 뒤 정점쉐이더 입력값의 데이터 타입과 semantic 와 동일하도록 만들어 줍니다.
12.jpg


코드 작성

이제 가장 중요한 부분인 코드 작성을 할 시간입니다. Pass 0 의 Vertex Shader 우클릭 -> Edit 를 선택하면 기본 구현 코드가 들어있는 Code Editor 창이 뜰 겁니다.
13.jpg

14.jpg


여기에서 가장 눈여겨 봐야할 점은 RenderMonkey 에서는 정점쉐이더와 픽셀쉐이더를 별도의 탭에서 작성한다는 점, 그리고 각각의 탭에서 정의한 변수는 지역변수라 다른 탭에서 접근할 수 없다는 점입니다.

즉, 쉐이더 코드를 정점쉐이더, 픽셀쉐이더로 반반 쪼개어야 하는데 그다지 큰 문제는 아닙니다. 나눈 후 코드는 아래와 같습니다(위에서 봤던 전체 구현 코드와 비교해 봅시다).

Vertex Shader 탭 :
   1: float4x4 WorldViewProjection;
   2: float4x4 WorldInverseTranspose;
   3:  
   4: struct VS_INPUT
   5: {
   6:    float4 position : POSITION0;
   7:    float3 normal : NORMAL0;
   8:    float2 texturecoord : TEXCOORD0;
   9: };
  10:  
  11: struct VS_OUTPUT
  12: {
  13:    float4 position : POSITION0;
  14:    float3 normal : TEXCOORD0;
  15:    float2 texturecoord : TEXCOORD1;
  16: };
  17:  
  18: VS_OUTPUT vs_main(VS_INPUT input)
  19: {
  20:    VS_OUTPUT output;
  21:    output.position = mul(input.position, WorldViewProjection);
  22:    output.normal = mul(input.normal, WorldInverseTranspose);
  23:    output.texturecoord = input.texturecoord;
  24:    return output;
  25: }

Pixel Shader 탭 :
   1: float3 LightDirection;
   2: float4 Ambient;
   3: sampler Texture;
   4:  
   5: struct VS_OUTPUT
   6: {
   7:    float4 position : POSITION0;
   8:    float3 normal : TEXCOORD0;
   9:    float2 texturecoord : TEXCOORD1;
  10: };
  11:  
  12: float4 ps_main(VS_OUTPUT input) : COLOR0
  13: {
  14:    float3 normal = normalize(input.normal);
  15:    float3 light = normalize(LightDirection);
  16:    float4 diffuse = tex2D(Texture, input.texturecoord);
  17:    float diffuseContribution = clamp(dot(normal, -light), 0, 1);
  18:    return Ambient + diffuse * diffuseContribution;
  19: }

VS_OUTPUT 구조체는 양쪽에 다 필요하기 때문에 반복해서 써 넣었다는 점, 그리고 전역변수는 필요한 탭에만 선언되어 있다는 점을 주의해서 봅시다.

정점쉐이더와 픽셀쉐이더 함수 이름은 탭 상단의 Entry Point 에 정의된 것과 같아야 합니다. 기본값은 각각 "vs_main" 과 "ps_main" 입니다.

여기까지 했다면 코드가 동작하는 것을 볼 수 있습니다. 툴바에 있는 Compile 을 눌러봅시다.
15.jpg


16.jpg


주전자가 온통 흰 색이네요. 하지만 걱정마세요. 이것은 LightDirection 과 Ambient 변수에 기본값이 들어있기 때문이니까요. RenderMonkey 에서는 변수값을 바꾸고 그 결과를 실시간으로 확인할 수 있습니다.

변수값 바꾸기

LightDirection 와 Ambient 변수값을 변경해 봅시다. LightDirection 변수 위에서 우클릭한 후에 이를 Artist Variable 로 바꿉니다(바꾼 뒤에는 변수 아이콘 위에 노란색 삼각형 표시가 추가됩니다). Colors 타입은 기본으로 Artist Variables 로 설정되기 때문에 Ambient 변수에는 이런 작업을 반복하지 않아도 됩니다.
17.jpg


이제 Artist Editor 창을 열어 봅시다.
18.jpg


이런 창이 뜨는 걸 볼 수 있습니다.
19.jpg


이제 링크되어 있는 변수값을 바꿔볼 수 있습니다. Artist Editor 창을 확장해 보면 더 다양한 제어방법을 써 볼 수 있습니다. 저는 LightDirection 벡터를 정규화시키도록 지정했고(xyz:0.714, -0.663, 0.221), Ambient 색은 짙은 검은 색(RGB:59,40,57)으로 설정했습니다.
20.jpg


Texture Sampler 옵션

마지막 팁입니다.주전자에 입혀진 텍스터가 약간 지저분해 보이네요. 아마도 포인트 필터링(point filtering)을 쓴 거 같은데요, 다른 걸로 바꿔봅시다.

Texture Object 위에서 우클릭한 후 Edit 를 선택합니다. 이 창에서는 sampler 에 대한 모든 것을 설정할 수 있습니다. magnification/minification/mipmapping(D3DSAMP_MAGFILTER/D3DSAMP_MINFILTER/D3DSAMP_MIPFILTER) 필터링을 linear(D3DTEXF_LINEAR) 로 바꿨더니 텍스터가 훨씬 부드럽게 보이는 걸 확인할 수 있습니다.
21.jpg


22.jpg


결론

이제 끝입니다. 미리보기 창을 크게 만들어 결과물을 확인해 봅시다. 툴바에 있는 카메라 컨트롤을 이용하면 씬을 돌아다닐 수 있습니다.
23.jpg


이게 최종 결과물입니다.
24.jpg


이 튜토리얼이 도움이 되었으면 좋겠네요. 질문이 있으면 아무거나 물어봐 주세요.(질문은 저말고 [http]원작자 블로그에 질문 써 주세요.)

핑백

덧글

댓글 입력 영역


Yes24위대한게임의탄생3

위대한 게임의 탄생 3
예스24 | 애드온2