2011년 12월 12일 월요일

[포프의 쉐이더 입문강좌] 03. 텍스처매핑 Part 1

이전편 보기

샘플파일 받기

제3장 텍스처매핑

이 장에서 새로 배우는 HLSL

  • sampler2D - 텍스처에서 텍셀을 구해올 때 사용하는 샘플러 데이터형
  • tex2D() - 텍스처 샘플링에 사용하는 HLSL 함수
  • 스위즐(swizzle) - 벡터 성분의 순서를 마음대로 뒤섞을 수 있는 방법


저번 장에서 배운 내용 어떠셨나요? 너무 쉬었다고요? 실제 게임에서 별 쓸모가 없어 보인다고요? 네, 사실 그렇습니다. 저번 장의 주 목적은 실습을 통해 HLSL의 기초 문법을 배우는 것이었습니다. 보통 프로그래밍 책에서 헬로월드(hello world) 예제를 처음에 드는 것과 마찬가지 이치죠. 그럼 이번 장에서는 그보다 조금 더 쓸모가 있는 내용을 배워볼까요? 물체를 단색으로 출력하는 대신에 표면에 텍스처(texture)(3D 그래픽에서는 이미지를 사용하여 표면의 색감 및 질감(texture)을 표현합니다. 여기서 사용하는 이미지들을 텍스처라고 부릅니다.) 를 입혀보는 게 어떨까요? 이걸 보통 텍스처매핑(texture mapping)이라고 부른다는 것쯤은 다 아시죠?

텍스처매핑과 UV 좌표
3D 물체를 이루는 구성요소는 삼각형이라고 이전에 말씀드렸습니다. 정점 3개로 삼각형을 만들 수 있다는 것도요. 그렇다면 삼각형 위에 이미지를 입히려면 어떻게 해야 할까요? '이 삼각형의 왼쪽 꼭짓점에 저 이미지의 오른쪽 귀퉁이 픽셀을 출력할 것'과 같은 지시를 내릴 수 있어야겠죠? (이렇게 다른 두 점을 서로 대응시키는 것을 영어로 매핑(mapping)이라고 합니다.)삼각형은 이미 정점 3개로 이루어져 있으니 각 정점을 텍스처 위에 있는 한 픽셀에 대응시켜 주면 되겠군요. 그럼 텍스처 위에서 한 픽셀을 어떻게 가리킬까요? 텍스처란 결국 이미지 파일이니까 'x = 30, y = 101에 있는 픽셀'이라는 식으로 정의하면 될까요? 만약 이렇게 정의를 해버리면 나중에 이미지 파일의 크기를 2배로 늘리면 이것을 다시 x = 60, y = 202로 바꿔야겠네요. 별로 바람직하지 않죠?

저번 장에서 배운 내용을 떠올려보니 색상을 표현할 때도 비슷한 이야길 했던 것 같군요. 그 때, 채널의 비트 수에 상관없이 통일적으로 색상을 표현하려면 어떻게 해야 한다고 했죠? 모든 값을 백분율(0~1)로 표현한다고 했죠? 여기서도 똑같은 방법을 사용합니다.  x = 0이 텍스처의 제일 왼쪽 열을, x = 1은 제일 오른쪽 열을 나타낸다고 하면 되겠죠? 마찬가지로 y = 0은 텍스처의 제일 처음 행을, y = 1은 마지막 행을 나타냅니다. 참고로 텍스처매핑을 사용할 때는 XY대신에 UV를 사용하는 게 보통입니다. 특별한 이유는 없고 그냥 위치를 표현할 때 흔히 xy를 사용하니 그와 혼돈을 피하기 위해서 입니다. 이것을 그림으로 표현하면 다음과 같습니다.

그림 3.1. 텍스처의 UV 좌표


이제 다양한 UV 좌표를 대입하면 어떻게 결과가 달라지는지는 몇 가지 예를 들어보도록 하죠. 역시 그림으로 보면 이해가 쉽겠죠?

그림 3.2 다양한 텍스처매핑의 예


(a) 아직 텍스처를 입히지 않은 두 삼각형입니다. 정점 v0, v1, v2와 v0, v2, v3가 각각 삼각형을 하나씩 이루고 있군요.
(b) UV 좌표의 범위가 (0,0) ~ (1,1)입니다. 텍스처를 전부 다 보여줍니다.
(c) UV 좌표의 범위가 (0,0) ~ (0.5, 1)입니다. 따라서 텍스처의 왼쪽 절반만을 모여줍니다. 0.5가 백분율로는 50%니까 딱 중간인 거 맞죠?
(d) UV 좌표의 범위가 (0,0) ~ (0.5, 0.5) 이군요. 따라서 이미지의 왼쪽 절반과 위쪽 절반만을 보여줍니다.
(e) UV 좌표의 범위가 (0,0) ~ (1,2)니까 텍스처를 위아래로 두 번 반복을 해줘야겠네요. (UV 좌표가 0~1 범위 밖에 있을 때 이것을 처리하는 방법에는 여러 가지가 있습니다. 위에서 든 예는 랩(wrap, 반복) 모드를 사용할 때만 올바릅니다. 이 외에도 미러(mirror, 거울)라던가 클램프(clamp, 비반복) 모드도 있습니다.)
(f) UV 좌표의 범위가 (0,0) ~ (2,2)니까 텍스처를 위아래로 두 번, 그리고 좌우로 두 번 반복해줍니다. (역시 마찬가지로 랩모드에서만 올바른 예입니다.)

이 외에도 UV 좌표의 범위를 (1,0) ~ (0,1)로 하면 텍스처의 좌우를 뒤집는 등의 효과도 줄 수 있습니다. 이 정도면 어떻게 UV 좌표를 지정해야 원하는 결과를 얻을 수 있는 지 대충 아시겠죠? 이 정도면 실제로 텍스처매핑 쉐이더를 작성할 준비가 된 것 같군요.


기초설정

  1. '제2장: 진짜 쉬운 빨강쉐이더'에서 했던 것과 마찬가지로 렌더몽키 안에서 새로운 DirectX 이펙트를 만든 뒤, 정점쉐이더와 픽셀쉐이더 코드를 삭제합니다.
  2. 이제 쉐이더의 이름을 TextureMapping으로 바꿉니다.
  3. 정점의 위치를 변환할 때 사용할 gWorldMatrix, gViewMatrix, gProjectionMatrix를 추가하는 것도 잊지 맙시다. 변수 시맨틱을 이용해서 실제 데이터를 전달해 주는 방법도 기억하시죠?
  4. 이제 텍스처로 사용할 이미지를 추가할 차례입니다. TextureMapping 쉐이더에 오른쪽 마우스 버튼을 누른 뒤, Add Texture > Add 2D Texture > 렌더몽키 설치폴더\examples\media\textures\earth.jpg 파일을 선택합니다. Earth라는 이름의 텍스처가 추가되었을 겁니다.
  5. 이 텍스처의 이름을 DiffuseMap으로 변경합니다.
  6. 이제 Pass 0위에 마우스 오른쪽 버튼을 누른 뒤, Add Texture Object > DiffuseMap을 선택합니다. Texture0 이란 이름의 텍스처 개체가 추가되었죠?
  7. 이제 Texture0의 이름을 DiffuseSampler로 바꿉니다.
  8. 이 모든 설정을 마치셨다면 Workspace 패널이 다음 그림처럼 보일 겁니다.


그림 3.3. 기초설정을 마친 렌더몽키 프로젝트


정점쉐이더
일단 전체 소스코드부터 보여드린 뒤, 한 줄씩 차근차근 설명해드리겠습니다.

struct VS_INPUT
{
   float4 mPosition : POSITION;
   float2 mTexCoord : TEXCOORD0;
};

struct VS_OUTPUT
{
   float4 mPosition : POSITION;
   float2 mTexCoord : TEXCOORD0;
};

float4x4 gWorldMatrix;
float4x4 gViewMatrix;
float4x4 gProjectionMatrix;


VS_OUTPUT vs_main(VS_INPUT Input)
{
   VS_OUTPUT Output;
 
   Output.mPosition = mul(Input.mPosition, gWorldMatrix);
   Output.mPosition = mul(Output.mPosition, gViewMatrix);
   Output.mPosition = mul(Output.mPosition, gProjectionMatrix);
 
   Output.mTexCoord = Input.mTexCoord;
 
   return Output;
}

정점쉐이더를 살펴보기 전에 텍스처매핑을 하려면 어떤 데이터가 새로 필요한지 생각해봅시다. 일단 당연히 텍스처로 사용할 이미지 하나가 필요하겠죠? 그렇다면 텍스처를 입히는 작업을 어디에서 해야 할까요? 정점쉐이더일까요? 아니면 픽셀쉐이더일까요? 각 쉐이더가 실행되는 시점을 생각해보면 이에 대한 대답을 쉽게 구할 수 있습니다. 정점쉐이더는 각 정점마다 실행이 된다고 말씀드렸었죠? 근데 텍스처는 어디에 입히죠? 정점에 입히는 게 아니라 표면을 구성하는 모든 픽셀에 입혀야 하죠? 따라서 정점쉐이더에서 하기엔 뭔가 부족할 듯 싶군요. 정점쉐이더와는 달리 픽셀쉐이더는 각 픽셀마다 호출이 되니까 당연히 픽셀쉐이더에서 텍스처매핑을 해야겠군요. 자, 그럼 이미지는 텍스처로 사용할 테니 정점쉐이더에서 선언해줄 필요가 없네요.

그럼 이 외에 다른 정보가 필요할까요? 바로 위에서 말씀드렸었는데 말이죠. 그렇습니다. UV 좌표가 필요하지요. UV 좌표를 어디에 지정했었죠? 각 정점마다였죠? 따라서 UV 좌표는 전역변수가 아니라 정점데이터의 일부로 전달됩니다. 자~ 그럼 이 점을 염두에 두고 정점쉐이더의 입출력 데이터를 살펴보도록 합시다.

정점쉐이더 입력데이터
'제2장: 진짜 쉬운 빨강쉐이더'에서 사용했던 입력데이터의 구조체를 일단 가져와보도록 하지요.

struct VS_INPUT
{
    float4 mPosition : POSITION;
};


자, 이제 여기에 UV 좌표를 추가해야겠죠? UV 좌표는 u하고 v로 나뉘니까 데이터형은 float2가 되겠네요. 그렇다면 어떤 시맨틱을 사용해야 할까요? 위치정보가 POSITION이라는 시맨틱을 가졌듯이 UV 좌표도 자신만의 시맨틱을 가지겠죠? TEXCOORD(텍스처좌표(texture coordinate)의 줄임말입니다.)란 시맨틱이 바로 그것입니다. UV 좌표 데이터를 삽입한 뒤의 정점쉐이더 입력데이터는 아래와 같습니다.

struct VS_INPUT
{
    float4 mPosition : POSITION;
    float2 mTexCoord : TEXCOORD0;
};

TEXCOORD뒤에 0을 붙인 이유는 HLSL에서 지원하는 TEXCOORD 수가 여럿이기 때문입니다. 쉐이더에서 여러 개의 텍스처를 동시에 사용할 때, 둘 이상의 UV 좌표를 사용할 경우가 있는데 그럴 때에는 TEXCOORD0, TEXCOORD1등으로 시맨틱을 사용하시면 됩니다.

정점쉐이더 출력데이터
우선 '제2장: 진짜 쉬운 빨강쉐이더'에서 사용했던 정점쉐이더 출력데이터를 가져와 봅시다.

struct VS_OUTPUT
{
    float4 mPosition : POSITION;
};

여기에 다른 정보를 추가해야 할까요? '제2장: 진짜 쉬운 빨강쉐이더'에서 설명해 드리지 않았던 내용 중 하나가 정점쉐이더는 위치정보 외에도 다른 정보들을 반환할 수 있다는 것입니다. 정점쉐이더가 위치정보를 반환하는 이유는 래스터라이저가 픽셀들을 찾아낼 수 있도록 하기 위해서였습니다. 하지만, 위치 이외의 다른 정보를 반환하는 이유는 래스터라이저를 위해서가 아닙니다. 이는 오히려 픽셀쉐이더를 위해서입니다. 텍스처매핑에 필요한 UV 좌표가 그 좋은 예입니다.

픽셀쉐이더는 정점 버퍼 데이터에 직접적으로 접근을 못 합니다. 따라서, 픽셀쉐이더에서 사용해야 할 정점데이터가 있다면(예, UV 좌표), 그 데이터는 정점쉐이더를 거쳐 픽셀쉐이더에 전달돼야 합니다. 좀 쓸데없는 제약 같다고요? 다음의 그림을 보시면 왜 이런 제약이 붙어 있는지를 알 수 있으실 것입니다.

그림 3.4. 과연 저 픽셀의 UV 좌표 값은 무엇일까?

UV 좌표가 정의된 장소는 각 정점입니다. 하지만 위 그림에서 볼 수 있듯이 픽셀의 UV 좌표는 정점의 UV 좌표와도 다른 것이 대부분입니다. (픽셀의 위치가 정점과 일치하는 경우에는 UV 좌표가 같습니다.) 따라서 이 픽셀의 올바른 UV 값을 구하는 방법은 현재 위치에서 세 정점까지의 거리를 구한 뒤 그 거리의 비율에 따라 세 UV 값을 혼합하는 것이겠지요. 하지만 이런 혼합을 직접해줄 필요는 없습니다. 정점쉐이더에서 출력한 위치 정보를 래스터라이저가 알아서 처리해줬듯이 정점 이외의 기타 정보는 보간기(interpolator)라는 장치가 알아서 혼합해줍니다. 그럼 '제1장: 쉐이더란 무엇이죠?'에서 보여드렸던 GPU 파이프라인에 보간기를 추가해보죠. 그림 3.5가 되겠습니다.

그림 3.5. 보간기까지 추가했지만 여전히 너무 간략한 3D 파이프라인


참고로 보간기가 보간(보간(interpolate)이란 단어가 이해 되시는 분들은 그냥 위에서 설명해 드렸다시피  '인접한 정점까지의 거리에 비례하여 값을 혼합하는 '이라고 이해하세요.)을 하는 것은 UV 좌표만이 아닙니다. 정점쉐이더가 반환하는 어떤 값이든 보간기는 보간을 해서 픽셀쉐이더에 전달해줍니다.

자, 그럼 이제 정점쉐이더에서 UV 좌표값도 반환해야 한다는 사실, 이해하시겠죠? 추가합시다.

struct VS_OUTPUT
{
    float4 mPosition : POSITION;
    float2 mTexCoord : TEXCOORD0;
};


전역변수
'제2장: 진짜 쉬운 빨강쉐이더'에서 사용했던 것 이외에 별도로 필요한 전역변수는 없습니다. 따라서 별다른 설명 없이 코드만 보여 드리겠습니다.

float4x4 gWorldMatrix;
float4x4 gViewMatrix;
float4x4 gProjectionMatrix;

정점쉐이더 함수
누누이 말씀드리지만 정점쉐이더의 가장 중요한 임무는 정점의 위치를 투영공간으로 변환시키는 것입니다. 이 코드는 '제2장: 진짜 쉬운 빨강쉐이더'의 쉐이더에서 사용했던 것과 똑같습니다.

VS_OUTPUT vs_main( VS_INPUT Input )
{
   VS_OUTPUT Output;

   Output.mPosition = mul( Input.mPosition, gWorldMatrix );
   Output.mPosition = mul( Output.mPosition, gViewMatrix );
   Output.mPosition = mul( Output.mPosition, gProjectionMatrix );

이제 UV 좌표를 전달해 줄 차례군요. Output 구조체에 UV 좌표를 대입하기 전에 공간변환을 적용해야 할까요? 그렇지 않습니다. UV 좌표는 여태까지 다뤘던 3차원 공간에 존재하는 게 아니라 삼각형의 표면상에 존재하기 때문입니다. 따라서 아무 변환 없이 UV 좌표를 전달해 줍니다.

   Output.mTexCoord = Input.mTexCoord;

더는 처리할 데이터가 떠오르지 않는군요. 이제 Output을 반환하면서 이 함수를 마무리 짓겠습니다.

   return Output;
}



댓글 24개:

  1. 포프님 감사합니다.. 잘 보고있습니다..^^

    답글삭제
  2. 열심히하고 있습니다.

    답글삭제
  3. 고맙습니다. ^^

    여기까지 무사히 따라 왔습니다.~~~

    답글삭제
  4. @익명: 여기까지 잘 따라오셨다면 끝까지 하시는데 무리가 없으시겠는데요?

    답글삭제
  5. 주옥같은 글 잘 읽고 있습니다. 감사합니다.
    이해 안되는 내용은 여러번 읽어 이젠 내용을 어느 정도 이해하고 있습니다.
    그런데 읽으면서 드는 궁금한 내용은 shader 와 d3dx 의 역활에 대해 감이 잘 안옵니다. 개별적으론 shader 는 픽셀의 색갈은 정하여 주고 d3dx 는 메쉬를 그리거나 그래픽으로 부터 넘어온 메쉬 정보를 그려주는 거라 생각 되어지는데 그렇다면 d3dx 는 메쉬의 색을 정할 수 없는지 궁금합니다. 또한 메쉬에 텍스처를 입히는 과정 또한 shader 없이도 가능한거 같지만 shader 에서 텍스처의 uv 좌표로 메쉬의 텍스쳐를 정하는 거 같아 혼란스럽 습니다. 위 랜더 파이프 그림에서 d3dx 는 어떤 관계가 있는지 알려주시면 정말 많은 도음이 될거 같습니다. 감사합니다

    답글삭제
  6. @kingof: Directx에서 둘다 지원을 하는 이유는 역사적인 이유때문입니다. 그리고 사실 이제는 프로그램 가능한 쉐이더만 지원됩니다.

    처음 DirectX가 나왔을때는 프로그램 가능한 쉐이더(programmable shader)란 놈이 없었습니다. 화면에 무언가를 그리려면 위에서 말씀해주신 것처럼 D3D에서 다 정해줘야 했지요. 이걸 고정함수 파이프라인(fixed function pipeline)이라고 했습니다. D3D에서 지원해주는 테크닉만 쓸 수 있었거든요. 그게 바로 한 8년전쯤 게임 보면 다 그놈이 그놈같아 보이는 이유입니다. 물론 게이머들이나 게임프로그래머들이 좀더 색다른 비주얼을 원했겠지요?

    그래서 새로 등장한게 프로그램 가능한 쉐이더(programmable shader)인데 이걸 요즘 보통 쉐이더 쉐이더라고 부르는 놈입니다. 쉐이더의 장점은 프로그래머 맘대로 최종결과를 주물러 줄 수 있다. 고로, 비주얼이 다른 게임과 다르게 만들 수 있고 새로운 기법이 나오면 MS사에서 그 기법을 고정함수에 추가해주길 기다릴 필요없이 곧바로 구현할 수 있다. 정도입니다...

    현재 쉐이더를 지원하는 그래픽 카드에서 고정함수도 지원하는 것처럼 보이지만 사실 내부적으로는 쉐이더를 돌려서 고정함수 파이프라인도 처리한다고 알고 있습니다. (이게 바로 위에서 프로그램가능한 쉐이더만 지원된다고 말씀드린 이유).

    그리고 DirectX 11 에서는 아예 고정함수들이 사라진걸로 알고있습니다. (무조건 쉐이더임 이제)

    답글삭제
  7. 글 잘읽고있습니다.하나하나따라가며 알아가는게 정말재밌고 정말 감사합니다. 많은 도움이 되는것같습니다.

    답글삭제
  8. 안녕하세요! 궁금한점이 있어서 이렇게 글 남깁니다.
    쉐이더 함수에서 VS_OUT VS (float4 t, TEXCOORD0 tx)
    {
    }
    이런식으로 받아 왔을때 t라는것과 tx라는 것을 우리가 흔히 쓰는 일반 함수처럼 써서 보내지 않았는데 어떻게 저 값들을 알 수 있는건가요? 궁금합니다.

    답글삭제
  9. @ASUMEGA: 그래요? 그렇게도 되요? 전 그냥 struct 를 정의하고 시맨틱을 지정해주거든요 위에서 본것 처럼... 뭐 제가 ASUMEGA님이 보여주신 함수처럼 인자를 안전해줘서 확실하진 않지만 아마 시맨틱 대로 될꺼에요...

    그럼 조금 자세한 설명...

    일단 버텍스 버퍼라고 하면 그냥 버텍스 정보 n개가 들어있는 배열이거든요. 그냥 메모리 덩어리죠. 그럼 그 안에서 어디에 위치정보가 들어있고 어디에 텍스처 좌표 정보가 들어있을까?.... 하드웨어는 그걸 몰라요. 그냥 메모리니까.. 그래서 그걸 정의해 줘야 하는데, 그 때 쓰는게 바로 시맨틱이에요. DX를 예를 들면 정점선언(vertex declaration)를 통해 정의하죠(DX9 전엔 FVF라는 게 있었는데 결국 이것도 Vertex Declartion을 통해 표현됨). vertex declaration정의할 때 대충 이런 식의 정보를 전달해줘요.

    첫번째 정보: POSITION, float3 타입, 오프셋 0 바이트
    두번째 정보: TEXCCOORD0, float2 타입. 오프셋 12 바이트
    세번째 정보: NORMAL, float3 타입. 오프셋 20 바이트.

    따라서 DX는 이 정보를 보고.. "아 이 메모리 덩어리에서 12번째 바이트가 텍스처좌표 구나." 라는 걸 아는 거죠... 위에서 보여드린 POSITION TEXCOORD0 NORMAL 등이 시맨틱이라고 하는 건데.. 쉐이더에서 시맨틱을 써서 변수를 불러오면 하드웨어가 알아서 저 메모리 위치에 있는 값을 전달해주는 겁니다. (따라서 DX에 잘못된 Vertex Declaration을 넣어주면 변수값이 다 틀리게 되죠 ㅎㅎㅎ)

    답글삭제
    답글
    1. 질문에 앞서 강좌정말 잘 보고 있습니다. 이해가 정말 잘 되는거 같습니다.
      그런데 궁금한게 하나 있습니다.
      POSITION, POSITION0, POSITION1, POSITION2... , TEXCCOORD, TEXCCOORD0, TEXCCOORD1, TEXCCOORD2 ...
      이렇게 여러가지가 있던데 왜 0,1,2,3,4.. 이런식으로 구분을 해놓은 건가요?
      그리고 그냥 POSITION 처럼 번호 안붙이고 쓰기도 하던데 이부분이 잘 이해가 안되네요

      삭제
    2. 여러가지 정보를 넣으려고 하기 때문이에요.. 메쉬에 UV좌표를 두개 이상 써줄 수도 있거든요. 예를 들어 diffuse하고 normal텍스처에 사용하는 UV좌표와 라이트맵에 쓰는 uv좌표가 다르면 정점마다 UV좌표를 두개씩 넣어줘야 하죠.

      그럼 diffuse하고 normal에 쓰는 uv좌표는 TEXCOORD0에 넣고 라이트맵에 쓰는 UV 좌표는 TEXCOORD1에 넣는거죠. 위치는 POSITION 0 1 2 3 가 있는지는 잘 모르겠네요. (있을지도 모르는데 안써봐서..^^)

      TEXCOORD도 숫자 안붙이면 TEXCOORD0과 같을거에요.

      (제가 질문하신 분의 배경이 정확히 어떤지 몰라서 그냥 아티스트 기준으로 설명드렸는데 이해가 안되셨다면 다시 질문주세요.)

      삭제
  10. @ASUMEGA: 아 참고로 POSITION정보는 정점정보에 반드시 들어있어야 하니까.. float4 t라고 하고 시맨틱 안붙여준건 그냥 위치를 읽어올거 같다고 추측만..... -_-

    답글삭제
  11. 오~ 감사 합니다. 더 알고 싶은 것이 있는데요. 지금 쉐이더 스키닝을 적용하고, Fog효과를 쉐이더로 구현해서 적용하려고 합니다. 궁금한게 A라는 스키닝 쉐이더 effect를 만들고 B라는 안개 쉐이더 Effect를 만들었습니다.
    적용을 할려고 하는데 어떤것을 먼저 해야 하나요? 지금 이것때문에 골치가 아프네요. B_fog쉐이더로 A를 감쌌더니 이상하게 캐릭터에 새 하얀 색만 나오네요...


    쉐이더 입문자라 너무 모르는게 많아 이렇게 질문드립니다.
    어쨌든 시맨틱이라는것을 알게되어 감솨합니다.~

    답글삭제
  12. @PROGOFJM: 모두다 픽셀쉐이더 하나에서 해주면 됩니다... 즉 이런식

    float4 pixelshader(...)
    {
    float4 outColor = //일반적인 조명계산 등등을 한다.
    return calcFog(outColor.rgb); // 포그를 적용한 뒤 반환한다.
    }

    답글삭제
  13. 안녕하세요. 기초적인 부분부터 쉐이더에 대해 차근차근 공부하고 있어요. 카툰렌더링까지 보고서 이제 제가 만들 게임에 넣어보고 싶어서 조금 해봤는데요, 약간의 고민이 생겼습니다.

    제가 최종적으로 만들고 싶은 것은 쉐이더를 이용해서 높이맵에 텍스쳐를 입히는 건데요, 이 높이맵의 정점들은 프로그램 시작 시에 높이맵텍스쳐를 읽어서 프로그램상에서 만들려고 합니다. 3dmax를 잘 다루지 못해서, 모델은 만들 수가 없어서요. 그런데 이렇게 된다면 정점쉐이더를 사용하지 않아도 될 것 같아요. 픽셀쉐이더만 필요하게 되는데, 이런 경우엔 쉐이더 프로그래밍을 어떻게 해줘야 하나요? 정점쉐이더는 작성하지 않고 픽셀쉐이더만 작성해도 괜찮은지 궁금합니다.

    다른 한 가지는 directx상에서 불러올 때 렌더링을 어떻게 해야 할지 모르겠어요. 모델링된 것을 그리는 것이 아니라서 DrawPrimitive를 호출해야 할텐데, 그냥 이 함수를 예제처럼 DrawSubset대신에 호출해주면 되는지 알고 싶어요. 답변 기다릴게요~

    답글삭제
    답글
    1. 일단 쓴소리 부터 시작한 뒤, 답글을 달겠습니다.

      0) 쓴소리
      질문을 2개 해주셨는데 질문 1번에 대한 이야기입니다.

      보통 질문하시는 분들의 질문을 읽어보면 내용을 잘 이해하셨는지 기초가 잡히셨는지가 보입니다. '초보자'님은 아직 이 책의 내용을(특히 앞부분) 이해하지 못하신걸로 보입니다. 특히 제1장과 제2장을 다시 한번 읽어보시라고 권해드리고 싶네요.

      1) 정점쉐이더
      여전히 필요합니다. 정점쉐이더가 하는일은 공간의 변환이니까요. 월드에 있는 위치나 회전에 따라 월드공간변환이 필요할수도 아닐수도 있지만(아닌 경우는 이미 지형의 모든 정점이 월드공간에 있다고 가정할떄...) 그래도 여전히 카메라의 위치나 투영방식에 따른 공간 변환이 필요합니다.

      (물론 DX의 fixed 파이프라인에 의존하는 법도 있지만 이건 어차피 더이상 사용하지 않을놈이고 속도도 더 느리므로 무시하겠습니다..)

      이해가 안되시면 제1장 및 제2장을 다시 읽어보세요~

      2) DrawSubset함수..
      D3DX로 시작하는 함수들 혹은 D3DX로 시작하는 클래스의 멤버함수들은 내부적으로 D3D함수들을 호출합니다. DrawSubset함수도 내부적으로는 4가지 함수를 호출합니다.

      a) 인덱스 버퍼 셋
      b) 버텍스 버퍼 셋
      c) 버텍스 declaration 셋
      d) DrawIndexedPrim 함수 호출

      이 함수들을 직접 호출해주시면 drawsubset이랑 동일한 일을 하실수 있습니다. 자세한 내용은 D3D SDK 문서를 참조하시구요.

      삭제
    2. 답변 정말 감사합니다. 덜렁대는 성격이라 몇 번을 읽었는데도 내용에 대해서 오해했나 봅니다. 공간변환에 관한 이야기가 생각나서 "아 그거 directx에서도 하잖아! 그럼 그걸로 그냥 떼우자!" 하면서 상상의 나래를 펼쳤네요...

      여기에 질문하지 않았다면 혼자서 며 칠밤을 새우면서 삽질을 했을 거에요. 답변을 보고 대강 감을 잡았습니다. 감사해요.

      삭제
  14. 작성자가 댓글을 삭제했습니다.

    답글삭제
  15. 픽셀의 UV 좌표는 정점의 UV 좌표와도 다른 것이 대부분입니다.<<< 죄송합니다 . 이거 무슨 뜻인지 모르겠어요. 어케 다르다는 건지 쉽게 설명 부탁 드립니다.

    답글삭제
    답글
    1. 설명은 이미 그 뒤에 바로 괄호안에 넣어놨어요. 이렇게 "(픽셀의 위치가 정점과 일치하는 경우에는 UV 좌표가 같습니다.)" 그리고 그 아래 보간 설명도 그 설명이구요.

      한마디로 정점바로 위에 있는 픽셀이 아니라면 정점의 UV좌표와 같은 좌표는 없고.. 각 정점까지의 거리에 따라 그 중간값이 나온단 이야기였습니다.

      삭제
    2. 빠른 답변 감사드립니다.^^

      삭제