2011년 12월 6일 화요일

[포프의 쉐이더 입문강좌] 02. 진짜 쉬운 빨강쉐이더 Part 2

이전편 보기

픽셀쉐이더
자, 이제 픽셀쉐이더를 작성해 볼 차례입니다. 정점쉐이더에서 했던 것과 마찬가지로 렌더몽키의 Workspace에서 Pixel Shader를 찾아 더블클릭합니다. 그리고 그 안에 있는 코드를 모두 지웁니다. 실제로 코드를 한 줄씩 쳐보셔야 실력이 늡니다. ^^ 꼭 코드를 다 지우세요.

이제 정점쉐이더에서 그랬던 거와 마찬가지로 전체코드를 보여드린 뒤, 한 줄씩 살펴보기로 하죠.

float4 ps_main() : COLOR
{   
   return float4( 1.0f, 0.0f, 0.0f, 1.0f );
}


픽셀쉐이더의 가장 중요한 임무는 픽셀의 색을 반환하는 것입니다. 현재 저희가 만드는 쉐이더기 빨강쉐이더니까 그냥 빨간색을 반환하면 되겠죠? 그렇다면 빨간색을 RGB값으로 어떻게 표현할까요? RGB(255, 0, 0)이 제일 먼저 떠오르시나요? 흠... 그렇다면 픽셀쉐이더 코드를 작성하기 전에 다음 절을 먼저 보셔야겠습니다.

색의 표현방법
빨간색을 RGB로 표현하라고 하면 (255, 0, 0)을 먼저 떠올리시는 이유는 RGB의 각 채널을 8비트로 저장하는 경우가 대부분이기 때문입니다. 8비트를 정수로 표현하면 총 256개의 값(2∧8 = 256)을 표현할 수 있습니다. 이 값을 0부터 시작하면 0 ~ 255가 되므로 각 채널의 최대값이 255이 되는 거지요. 근데 8비트가 아니라 5비트로 각 채널을 표현하면 어떻게 될까요? 2∧5 = 32이므로 31이 최대 값이 되지요. 따라서 8비트 이미지에서 빨간색은 (255, 0, 0) 이지만 5비트 이미지에서의 빨간색은 (31, 0, 0)이라는 찹찹한 결과가 생기고 마네요?

그러면 비트 수에 상관없이 통일적으로 색을 표현할 방법은 없을까요? 아마 포토샵에서 HDR 이미지를 다뤄보신 분들이라면 이미 그 답을 알고 계실 듯 하네요. 바로 백분율(%)을 사용하면 되지요. 백분율을 사용하면 비트 수에 상관없이 빨간색의 RGB값이 언제나 (100%, 0%, 0%)가 됩니다. 이게 바로 쉐이더에서 색상을 표현할 때 사용하는 방법입니다. 백분율을 그냥 유리수로 나타내면 0.0 ~ 1.0이 되니까 쉐이더에서 빨간색의 RGB값은 (1.0, 0.0, 0.0)이 됩니다.

픽셀쉐이더 함수
자, 그럼 이젠 어떤 RGB 값을 반환해야 할지도 알아봤으니 픽셀쉐이더 함수를 작성할 일만 남았군요. 픽셀쉐이더 함수의 헤더부터 살펴봅시다.

float4 ps_main() : COLOR
{

이 헤더가 의미하는 바는 다음과 같습니다.

  • 이 함수의 이름은 ps_main이다.
  • 이 함수는 매개변수를 받지 않는다.
  • 이 함수의 반환형은 float4이다.
  • 이 함수의 반환 값을 백 버퍼의 색상(COLOR)값으로 처리할 것.


여기서 딱히 추가로 설명해 드릴 부분은 반환 값의 데이터형으로 float3가 아니라 float4를 쓴다는 정도입니다. 4번째 값은 알파 채널로 보통 투명효과를 나타내는 용도로 쓰이곤 합니다. (이 값이 1이면 완전 불투명, 0이면 완전 투명입니다.)

자, 그럼 이 함수 안에선 무슨 일을 해야 했었죠? 그렇죠. 빨간색을 반환해야죠. 이렇게 코드를 짜면 됩니다.

   return float4( 1.0f, 0.0f, 0.0f, 1.0f );
}

여기서 특별히 설명드릴 것은 float4(r, g, b, a)라는 형태로 float4 벡터를 새로 생성한다는 것과 알파 채널의 값이 1.0(100%)이므로 픽셀이 완전히 불투명 하다는 정도 입니다. 이제 쉐이더 편집기 안에서 F5키를 눌러 정점쉐이더와 픽셀쉐이더를 각각 컴파일 하면 미리 보기 창에서 다음과 같은 빨간색 공을 보실 수 있을 겁니다.

팁: 렌더몽키에서 쉐이더를 컴파일 하는 법
렌더몽키에서는 정점쉐이더와 픽셀쉐이더를 별도로 컴파일 해줘야 합니다. 편집기에서 각 쉐이더를 불러온 뒤 F5를 눌러주세요. 미리 보기 창이 열릴 때도 두 쉐이더가 모두 컴파일 됩니다.

그림 2.7. 처음으로 만들어본 빨강쉐이더!


정말 간단한 쉐이더였죠? 여기서 빨간색 대신 파란색을 보여주려면 어째야 할까요? float4(0.0, 0.0, 1.0, 1.0)을 반환하면 되겠죠? 노란색은요? 노란색은 연두색과 빨간색을 섞은 거니까.... 음.... 제가 굳이 답을 알려드리지 않아도 아시죠?

이제 이 렌더몽키 프로젝트를 잘 저장해 두세요. 각 장이 끝날 때마다 렌더몽키 프로젝트를 저장해 두시기 바랍니다. 나중에 다른 장에서 다시 이용할 거거든요.

선택사항: DirectX 프레임워크
이제 C++로 작성한 DirectX 프레임워크에서 쉐이더를 사용하시고자 하는 분들을 위한 선택적인 절입니다.

우선 '제1장: 쉐이더란 무엇이죠?'에서 만들었던 프레임워크의 사본을 만들어 새로운 폴더에 저장합니다. 각 장마다 프레임워크를 따로 저장하는 이유는 다른 장에서 이 프레임워크를 가져다가 코드를 추가할 예정이기 때문입니다.

다음은 렌더몽키에서 사용했던 쉐이더와 3D 모델을 DirectX 프레임워크에서 사용할 수 있도록 파일로 저장할 차례입니다.


  1. Workspace 패널에서 ColorShader를 찾아 오른쪽 마우스 버튼을 누릅니다.
  2. 팝업메뉴에서 Export > FX Exporter를 선택합니다.
  3. 위에서 DirectX 프레임워크를 저장했던 폴더를 찾아 그 안에 ColorShader.fx란 이름으로 파일을 저장합니다.
  4. 이제 Workspace 패널에서 Model을 찾아 오른쪽 마우스 버튼을 누릅니다.
  5. 팝업메뉴에서 Save > Geometry Saver를 선택합니다.
  6. 역시 DirectX 프레임워크가 있는 폴더 안에 Sphere.x란 이름으로 파일을 저장합니다.


이제 비주얼 C++ 에서 프레임워크의 솔루션 파일을 연 뒤, 다음의 코드들을 차례대로 추가해보도록 하죠. ShaderFramework.cpp 파일을 열겠습니다.

우선, 투영행렬을 만들 때 필요한 상수들을 #define으로 정의하겠습니다.

#define PI           3.14159265f
#define FOV          (PI/4.0f)                     // 시야각
#define ASPECT_RATIO (WIN_WIDTH/(float)WIN_HEIGHT) // 화면의 종횡비
#define NEAR_PLANE   1                             // 근접 평면
#define FAR_PLANE    10000                         // 원거리 평면

이제 Sphere.x하고 ColorShader.fx 파일을 로딩해서 메모리에 저장해둘 때 사용할 포인터 2개를 선언합니다.

// 모델
LPD3DXMESH        gpSphere        = NULL;


// 쉐이더
LPD3DXEFFECT      gpColorShader   = NULL;

이제 모델과 쉐이더 파일을 로딩해야겠죠? '제1장: 쉐이더란 무엇이죠?'에서 속을 비워두었던 LoadAssets()함수 안에 다음의 코드를 추가할 때로군요.

    // 쉐이더 로딩
    gpColorShader = LoadShader("ColorShader.fx");
    if ( !gpColorShader )
    {
        return false;
    }


    // 모델 로딩
    gpSphere = LoadModel("sphere.x");
    if ( !gpSphere )
    {
        return false;
    }

위 코드는 '제1장: 쉐이더란 무엇이죠?'에서 미리 구현해 두었던 LoadShader() 함수와 LoadModel() 함수를 호출해서 파일들을 로딩한 뒤, 그 중에 하나라도 NULL 포인터이면 로딩에 실패했다는 의미로 false를 반환합니다. 이렇게 로딩에 실패한 경우 비주얼 C++의 출력 창에 에러메시지가 있을 테니 살펴보시기 바랍니다.

새로운 D3D 자원을 로딩할 때 마다 이를 해제하는 코드를 추가하는 습관을 기르도록 합시다. GPU 상의 메모리 누수를 막기 위해서입니다. CleanUp() 함수에서 D3D를 해제하기 바로 전에 다음의 코드를 삽입하겠습니다.

    // 모델을 release 한다.
    if ( gpSphere )
    {
        gpSphere->Release();
        gpSphere = NULL;
    }


    // 쉐이더를 release 한다.
    if ( gpColorShader )
    {
        gpColorShader->Release();
        gpColorShader = NULL;
    }


이제 사전작업은 모두 끝났으니 마지막으로 쉐이더를 이용해서 물체를 그리기만 하면 됩니다. 3D 물체를 그리는 코드는 RenderScene()에 넣기로 했었죠? RenderScene() 함수로 갑니다.

// 3D 물체 등을 그린다.
void RenderScene()
{

쉐이더 안에서 전역변수들을 사용했던 것 기억하시나요? 렌더몽키에서는 변수 시맨틱을 통해 이 값들을 대입해줬지만 여기서는 직접 이 값들을 만들어서 쉐이더에 전달해 줘야 합니다. 우선 뷰행렬부터 만들어 볼까요?

    // 뷰 행렬을 만든다.
    D3DXMATRIXA16 matView;
    D3DXVECTOR3 vEyePt(    0.0f, 0.0f, -200.0f ); 
    D3DXVECTOR3 vLookatPt( 0.0f, 0.0f,  0.0f );
    D3DXVECTOR3 vUpVec(    0.0f, 1.0f,  0.0f );
    D3DXMatrixLookAtLH( &matView, &vEyePt, &vLookatPt, &vUpVec );

위에서 볼 수 있듯이 카메라의 현재 위치와 카메라가 바라보는 곳의 위치, 그리고 카메라의 위쪽을 가리키는 벡터만 있으면 D3DXMatrixLookAtLH() 함수를 호출하여 뷰행렬을 만들 수 있습니다. 여기서는 카메라가 현재 (0, 0, -200)에 위치해 있고 (0, 0, 0)을 바라보고 있다고 가정합니다. 실제 게임에서는 카메라 클래스로부터 이 정보를 가져와서 뷰행렬을 만드는 것이 정석입니다.

다음은 투영행렬을 만들 차례입니다. 투영행렬은 원근투시법(perspective projection)을 사용하느냐 직교투시법(orthogonal projection)을 사용하느냐에 따라 사용할 함수와 매개변수들이 달라집니다. 여기서는 원근투시법을 사용하므로 D3DXMatrixPerspectiveFOVLH() 함수를 사용하겠습니다. (직교투시법을 사용할 때는 D3DXMatrixOrthoLH를 사용하세요.)

    // 투영행렬을 만든다.
    D3DXMATRIXA16 matProjection;
    D3DXMatrixPerspectiveFovLH( &matProjection, FOV, ASPECT_RATIO, NEAR_PLANE,
        FAR_PLANE );

이제 월드행렬을 만들어 보겠습니다. 사실 월드행렬은 한 물체의 위치와 방위, 그리고 확장/축소 변환을 합친 것입니다. 따라서 뷰행렬 및 투영행렬과 달리 각 물체마다 월드행렬을 만들어줘야 합니다. 본 예제에서는 월드의 원점(0, 0, 0)에 물체를 놓아둔다고 가정하므로 월드행렬을 그냥 단위행렬(identity matrix)로 놔두겠습니다.

    // 월드행렬을 만든다.
    D3DXMATRIXA16 matWorld;
    D3DXMatrixIdentity(&matWorld);

쉐이더에서 사용할 전역변수 3개를 전부 다 만들었으니 이제 이 값들을 쉐이더에 전달해줘야 겠군요. 이 때 쉐이더의 SetMatrix함수를 이용하면 이런 일을 쉽게 할 수 있습니다. SetMatrix의 첫 번째 인수는 쉐이더 안에서 사용하는 변수의 이름이고, 두 번째 변수는 위에서 정의한 D3DXMATRIXA16형의 변수입니다.

    // 쉐이더 전역변수들을 설정
    gpColorShader->SetMatrix("gWorldMatrix", &matWorld);
    gpColorShader->SetMatrix("gViewMatrix",  &matView);
    gpColorShader->SetMatrix("gProjectionMatrix",  &matProjection);

쉐이더에 필요한 변수들의 값을 모두 전달해줬다면 이제 GPU에게 명령을 내릴 차례입니다. '앞으로 그릴 모든 물체들에 이 쉐이더들을 적용할 것'이라는 명령을 말입니다. 이런 명령은 쉐이더의 Begin() / BeginPass()와 EndPass() / End() 함수호출로 내립니다. BeginPass()와 EndPass()가 구성하는 블럭 안에 물체를 그리는 함수를 넣으면 물체가 그려질 때 이 쉐이더가 사용되죠. 우선 아래의 코드를 보시죠.


    // 쉐이더를 시작한다.
    UINT numPasses = 0;
    gpColorShader->Begin(&numPasses, NULL);
    {
        for (UINT i = 0; i < numPasses; ++i )
        {
            gpColorShader->BeginPass(i);
            {
                // 구체를 그린다.
                gpSphere->DrawSubset(0);
            }
            gpColorShader->EndPass();
        }
    }
    gpColorShader->End();
}


DrawSubset() 호출이 BeginPass() / EndPass() 안에 있고, 이는 다시 Begin() / End() 호출 안에 있는 거 보이시죠? 이렇게 하면 GPU가 gpColorShader 쉐이더를 이용해서 gpSphere 물체를 그릴 것입니다.

위의 코드를 보시면 쉐이더에서 Begin() 함수를 호출 하고 난 뒤에 다시 BeginPass()를 호출하는 거 보이시죠? 가끔 패스(pass)를 보고 '아니, 쉐이더는 알겠는데 그 안에 들어있는 패스는 또 뭐여?'라고 혼돈스러워하는 학생들을 본 적이 있는데 크게 신경 쓰지 않으셔도 됩니다. 패스는 다양한 쉐이더를 이용하여 동일한 물체를 여러 번 그릴 때 유용하지만 실무에서 둘 이상의 패스를 쓰는 경우가 별로 없으니 그냥 무시하세요. 그냥 Begin() 함수를 호출할 때, numPasses 변수의 주소를 전달하여 쉐이더 안에 들어있는 패스의 수(대부분의 경우 1)를 구해온다는 정도만 아시면 됩니다. 만약 2개 이상의 패스가 존재한다면 정점/픽셀쉐이더 쌍도 둘 이상이 존재한다는 거니까 그 수만큼 BeginPass()/EndPass()를 호출하면서 여러 번 물체를 그려주면 되는 거죠.

이제 코드를 컴파일 한 뒤 프로그램을 실행하면 아까 렌더몽키에서 봤던 것과 동일한 결과를 보실 수 있습니다.

정리
다음은 이 장에서 배운 내용을 짧게 요약해 놓은 것입니다.

  • 각 정점마다 변하는 값은 정점데이터의 멤버변수로 받는다.
  • 모든 정점에 공통적으로 사용되는 값은 전역변수로 받는다.
  • HLSL은 벡터연산에 간편히 사용할 수 있는 float4, float4x4 등의 데이터형을 제공한다.
  • 정점의 공간을 변환할 때는 행렬 곱을 사용한다. HLSL에서 제공하는 내장함수 mul()을 사용하면 손쉽게 행렬과 벡터를 곱할 수 있다.
  • HLSL에서 색상을 표현할 때는 0 ~1 사이로 정규화한 값을 사용한다.



이 장에서 배운 내용은 정말 기초 중의 기초입니다. 이렇게 간단한 쉐이더를 혼자서도 뚝딱 작성하실 정도로 쉐이더 문법의 기본이 되어야 나중에 다른 복잡한 쉐이더도 쉽게 작성하실 수 있습니다. 제가 강의를 할 때, 이 빨갱이 쉐이더가 너무 쉽다고 눈으로만 대충 훑어보고 넘어간 일부 학생들이 나중에 다른 쉐이더에서 고생하는 경우를 종종 봤습니다. 쉐이더 자체가 어려워서가 아니라 아주 기초적인 HLSL 문법조차도 제대로 숙지하지 않았기 때문이었습니다. 다음 장으로 가시기 전에 반드시 빨강쉐이더 정도는 직접 작성하실 수 있을 정도로 한두 번 연습을 해두시기 바랍니다.


댓글 21개:

  1. 우왕~ 꽃미남 포프님 잘보겠습니다.

    답글삭제
  2. 잘보고 있습니다. 관심이 있었지만 어디서부터 어떻게 시작해야 할지 몰랐었는데 많은 도움이 됩니다.

    답글삭제
  3. 마지막까지 공개 돼기를 바랍니다.

    답글삭제
  4. 잘 봤습니다~ 출판이든 연재든 다음편 빨리 보고싶어요~~

    답글삭제
  5. 내일 다음편 올리니다. 오전 9시30분쯤 ^^ part1에 달아주신 틀린거 지적도 감사드립니다~

    답글삭제
  6. 안녕하세요~ 셰이더 배우고 있는 학생입니다. 바로바로 배울수 있어서 재밌게 배우고 있습니다. 그런데 몇가지 질문이 있습니다. (이전에 댓글 하나 올렸는데 안올려서저 다시 씁니다...)

    제가 만든 DirectX에 Sphere랑 ColorShader랑 다 로딩이되고 렌더링을 했는데 렌더링이 안되더군요.. 그래서 포프님이 샘플로 올려주신거와 비교하면서 다 바꿨는데도 안되서.. 포기하다가 ColorShader의 파일을 텍스트로 보니까 그 string ColorShader_Pass_0_Model 이라는 변수의 값이 포프님이 올려주신 파일이랑 다르더군요... 바꿨더니 잘 됩니다..

    저게 질문은 아니구요..

    ColorShader_Pass_0_Model의 변수가 파일 위치를 뜻하는건 알겠는데요.. 제가 그전에 이미 Sphere.x 파일을 불러와서 DrawSubset으로 Shader안에서 그리고 있는데 저 파일 위치가 꼭 필요하나요??

    만약 필요하다면 ASE나 3ds max exporter를 따로 만들어서 파싱한 모델들은 메모리에만 올라놔 있을것인데.. Shader가 적용이 안되는건가요??

    마지막으로는.. 만약 모양이 조금씩 다 다른 100개의 Sphere가 있는데 이 Sphere들을 다 출력을 할려면 Effect의 SetString인가로 해서 그 Shader 변수에 값을 다 지정하는데 매번 바꾸면서 렌더링 해야하나요??


    감사합니다...

    답글삭제
    답글
    1. 1)
      파일 위치 필요없습니다. 제가 아는 한 DirectX쪽에서는 ColorShader_Pass_0_Model : ModelData 변수는 전혀 쓰이지 않습니다. 아마 다른 툴에 사용하려고 쓰는 semantic 인거 같습니다. 제가 잠시 테스트 해봤는데 이 라인을 지워버려도 값을 바꿔도 결과에는 아무 차이가 없습니다.

      아마 모델 로딩을 제대로 안하셨거나.. 사용하는 모델안에 메쉬가 2개 이상이 있는게 아닌가 싶습니다. 그렇다면 DrawSubset(0)을 모델수만큼 루프를 돌려줘야 합니다.

      한번 테스트로 제 샘플프로젝트를 가져다가 현재 사용하시는 .x파일을 로딩해다가 테스트해보세요~

      2)
      1)번 답과 동일..

      3)
      SetMatrix함수 이야기 하시는거죠? 뷰하고 프로젝션 메트릭스는 각 모델마다 변하는게 아니니까 그건 바꿔주지 않으셔도 됩니다. 월드 매트릭스는 각 물체마다의 위치 및 방위를 나타내니 그건 각 sphere마다 바꿔주셔야 합니다.

      여담으로 쉐이더가 복잡해지면 각 물체마다 바뀌는 값이 많아집니다. 이 매개변수들을 설정하는 부분이 성능저하의 요소인 게임도 많이 있었습니다. (뭐 최적화 부분은 아직 신경안쓰셔도 되지만요~)

      삭제
    2. 답변 감사합니다. 하지만 1번은 아직도 안되는 이유를 몰라서.. 나중에 소스 정리하면서 다시 한번 보려고 합니다.

      또 질문을 드리고 싶은것은.. RenderMonkey에서 pass0, pass1 .... 이렇게 있는데요 pass0에다가 모델링 2개이상 추가하려고 하니깐 안되가지고 pass1 하나 만들어서 똑같이 shader소스 복사해서 모델링도 추가하고 해서 2개다 뜹니다.

      pass0이던 pass1이던 같은 Shader를 사용할꺼면 그 SetMarix같은것들만 조금씩 바꿔서 나타내준다고 하셨는데 RenderMonkey에는 그런것이 없는건가요? 그러니깐 여러 모델을 추가하는 방법이요.. 팝업메뉴에도 안나타나네요...

      그리고 pass같은경우 여러가지로 나눠서 하는것도 있고 없는것도 같은데

      만약 카툰 렌더링 식으로 만들려고 하면 카툰셰이딩 + 외각 + 그림자 했을때pass0, pass1, pass2 세가지로 나눠서 적용 시키는 거와 한 Shader에 다 적용한 거랑 차이는 거의 없는건가요 ? 이거 나누면 그냥 옵션에서 적절히 선택할 수 있게 해주는? 그런 차이뿐인가요?

      감사합니다.

      삭제
    3. 1)
      렌더몽키 자체가 프로토타입용으로 만든거라 그정도 기능은 없는걸로 알고 있습니다. 저도 쉐이더를 그냥 복사해다가 쓰지요.

      2)
      가능하면 여러 패스로 나누지 않고 하나로 그리는게 더 바람직합니다. 패스를 나누면 버텍스 쉐이더도 한번더 거치고, rasterization도 한번더 거치고... 그러니 부하가 좀 되거든요. 물론 그게 불가능한 경우가 있는 경우에는 패스를 나누긴 하는데... 그것도 한물체를 그리면서 패스 0, 1, 2를 차례로 그려주기보단 패스 0용으로 여러물체 그리고 그담에 패스 1 용으로 여러물체 그리는게 일반적인거 같습니다.

      참고로 fx파일 안에 패스나 테크닉이 따로 있어서 그렇지... 하드웨어 내부에서는 그런 존재조차 모릅니다. 결과적으로는 어떤 버텍스 쉐이더 함수와 어떤 픽셀 쉐이더 함수를 쓰는지만 압니다.

      삭제
    4. 감사합니다. 좀더 물어복고 싶은것이 있습니다. 책 추천해주실 있으신가요? 포프님이 올려주신것처럼 Rendermonkey 기반에 hlsl을 사용하는 책을 추천해주세요. 원서보다는 번역본쪽으로 부탁드리겠습니다.

      삭제
    5. 솔직히 말씀드리면 입문쪽으론 추천해드릴만한 책이 없습니다. 사실 이 책을 쓰게된 계기도 캐나다 대학에서 강의할때 쓸만한 책이 없어서 내용을 개발한 걸 기반으로 쓴거거든요......

      이 내용을 기반으로 한 책이 5월달에 한빛출판사에서 나오긴 합니다만....

      삭제
    6. 그렇군요.. 책 나오면 사겠습니다. 그런데 지금 당장 필요한 내용을 배우고 싶은데 다른 책들을 그냥 보기만하고 RenderMonkey 기반이 아니라 은근 힘드네요. 제가 필요하는 내용이 그림자 기법이랑 외각선 찾기 부분인데 문제가 되지 않으시면.. 보내주실수 있으신가요..?

      그리고 올려주신 목차말고 부가적으로 책에 내용 더 들어간것 있나요?

      삭제
    7. 출판사하고 출판약정할때 그 뒷부분 내용을 한동안 올리지 않기로 했기때문에 보내드리긴 힘들거 같습니다.

      목차외에 추가적으로 들어가는 내용은 없습니다. 실제 내용은 블로그에 올라온 것과 좀 다를테지만 다른 쉐이더 기법이 추가되진 않았죠...

      삭제
  7. 질문 할곳을 찾지 못하여, 이 강좌가 어느 정도 질문의 내용과 맞다고 생각하여 게시합니다 ^^


    픽셀 쉐이더에서
    return float4(1.0f,0.0f,0.0f,1.0f);

    마직막 원소(알파를 나타낸다는) 0.5.를 설정을하여도 렌더몽키에서 셈플 구는 투명이 되지 않네요.

    제가 찾기 못했서 인지, 아님 랜더 몽키에서 따로 설정을 해야 되는지 궁금하여 질문 남깁니다.

    답글삭제
    답글
    1. 답변이 조금 늦었습니다. 렌더몽키에서 따로 설정하는게 맞습니다.

      알파채널이란게 사실 투명채널이라고 정해져 있는게 아니거든요. 투명으로 쓸수도 있고 다른 용도로도 쓸수 있어서 어느 엔진이든 소프트웨어에서든 이걸 따로 설정해줘야 합니다. 렌더몽키에서 이걸 설정하는 법은 다음과 같습니다.

      1) pass위에 오른쪽 마우스 버튼을 눌러서 Add RenderState Block을 선택합니다
      2) 새로 생긴 Render State에 마우스를 더블 클릭하면 엑셀 표같은게 등장합니다.
      3) D3DRS_ALPHABLENDENABLE을 TRUE로 해줍니다. (알파채널을 이용해서 혼합하라~ 라는 의미)
      4) D3DRS_BLENDOP을 D3DBLENDOP_ADD로 해줍니다. (이미 화면에 있던 색상과 새로 그리는 색상을 서로 더해줄것~ 이란 의미입니다)
      5) D3DRS_DESTBLEND를 D3DBLEND_INVSRCALPHA로 해줍니다. (이미 화면에 있던 색상에는 지금 그리는 픽셀의 알파값을 반전한 값을 곱해줄것~ 이란 의미입니다)
      6) D3DRS_SRCBLEND를 D3DBLEND_SRCALPHA로 해줍니다. (새로 그리는 픽셀의 색상에 알파값을 곱해줄 것이라는 의미입니다.)

      이렇게 해주면...

      화면색상 * (1 - a) + 현재색상 * a 가 되는 거니가 보통 저희가 생각하는 투명블렌딩이 되는거죠.

      이 외에 그냥 가산(additive)블렌딩을 해주시려면 srcapha와 destalpha를 모두 1로 해주시면 됩니다~

      삭제
    2. 친절한 답변 정말 고맙습니다.

      화면의 오브젝트를 바라 볼때, 항상 가장자리가 투명으로 보이는 것을 구현해 보고 싶어서 이리 저리 자료 찾고, 생각하다 보니, 이러게 하면 될것 같은 생각에....
      랜더몽키에서 구현 할려니. 투명 처리에서 걸려버렸습니다. 몇일 웹서핑 하다가 결국은 검색 실력의 한계로 인하여 질물을 게시하였습니다.

      많이 보아왔던 용어들도 눈에 보이고, 처음 접하는 용어들도 있지만 그래도 뭐가를 할 수 있을 것같다는 희망의 빛이 보여, 기쁩니다.


      P.S. 드디어 5월입니다. 가정의 달 5월(한국에선) ..
      그 보단 책 출간하는 달입니다. 기다고 있습니다. ^^

      삭제
  8. Pass 의 개념이 이해가 되지 않았었는데 이 글을 보니까 이해가 되는군요.. 심지어 이글의 주제가 Pass 에 관련된것도 아닌데, 그냥 쭉 따라 읽다보니까 Pass가 이해가됩니다. 정말 감사합니다^^

    답글삭제
    답글
    1. 이해하지 말라고 했는데 이해를 하시다니요! ㅎㅎㅎ.. ^_^

      삭제
  9. 감사합니다. 이런 좋은글을 써주셔서..

    답글삭제
  10. 감사합니다. 책 꼭사서 보겠습니다

    답글삭제
  11. gpColorShader->Begin(&nPasses, NULL);

    여기 리턴값이 D3DERR_INVALIDCALL이 나와서 화면에 빨간원이 나오지 않는데 뭐가 문제일까요?

    답글삭제