18. Multi-Pass

Shader 2017. 3. 10. 11:21
728x90

※ 제 개인 공부이므로 풀 소스 코드는 작성하지 않습니다.

※ 생략 되어 있는 부분이 많습니다.

Effect는 하나의 Technique에 여러 Pass를 가질 수 있다. 이것을 Multi-Pass라 한다.

 

Multi-Pass를 사용하면 같은 렌더링 물체에 대해서 서로 다른 렌더링 환경을 가지고 연속해서 그리는 상황에 대해 편리하다.

 

struct VtxNDUV1
 {
  D3DXVECTOR3 p;
  D3DXVECTOR3 n;
  DWORD d;
  FLOAT u, v;

  VtxNDUV1() : p(0, 0, 0), n(0, 0, 0), u(0), v(0), d(0xFFFFFFFF) {}
  VtxNDUV1(FLOAT X, FLOAT Y, FLOAT Z
   , FLOAT NX, FLOAT NY, FLOAT NZ
   , FLOAT U, FLOAT V
   , DWORD D = 0XFFFFFFFF) : p(X, Y, Z), n(NX, NY, NZ), u(U), v(V), d(D) {}

  enum { FVF = (D3DFVF_XYZ | D3DFVF_NORMAL | D3DFVF_DIFFUSE | D3DFVF_TEX1) };

 };

 

LPD3DXEFFECT  m_pEft;    // ID3DXEffect Instance
LPDIRECT3DVERTEXDECLARATION9  m_pFVF;    // Vertex Declarator

 

D3DXMATRIX  m_mtWld;   // World Matrix

 

INT   m_iNvx;    // Vertex Number
VtxNDUV1* m_pVtx;    // Vertex Buffer
LPDIRECT3DTEXTURE9  m_pTx;

 

정점은 포지션(XYZ), 법선(NORMAL), 색상(DIFFUSE), 텍스처(TEX1)로 이루어진 구를 형성한다.

 

*Render()*

 

HRESULT  hr = -1;


D3DXMATRIX  mtViw;        // View Matrix
D3DXMATRIX  mtPrj;        // Projection Matrix
D3DXVECTOR4 vcLight(1, 0.0f, 0.0f, 0.0f);  // 광원방향
D3DXVECTOR4 xcGlow(1, 1.0f, 0.2f, 1.0f);  // Glow 색상
FLOAT  fThick = 40.F;      // Glow 두께
D3DXVECTOR4 vcGlow;        // Glow 축

 

m_pdev->GetTransform(D3DTS_VIEW, &mtViw);
m_pdev->GetTransform(D3DTS_PROJECTION, &mtPrj);


D3DXMATRIX mtViwI;
D3DXMatrixInverse(&mtViwI, NULL, &mtViw);
vcGlow = D3DXVECTOR4(m_mtWld._41 - mtViwI._41, m_mtWld._42 - mtViwI._42, m_mtWld._43 - mtViwI._43, 0);
D3DXVec4Normalize(&vcGlow, &vcGlow);

 

m_pdev->SetVertexDeclaration(m_pFVF);   // 정점선언

 

// 상수 연결
hr = m_pEft->SetMatrix("m_mtWld", &m_mtWld);
hr = m_pEft->SetMatrix("m_mtViw", &mtViw);
hr = m_pEft->SetMatrix("m_mtPrj", &mtPrj);

 

hr = m_pEft->SetVector("m_vcLgt", &vcLight);

 

hr = m_pEft->SetVector("m_GlowColor", &xcGlow);
hr = m_pEft->SetVector("m_GlowAxis", &vcGlow);
hr = m_pEft->SetFloat("m_GlowThick", fThick);

 

hr = m_pEft->SetTexture("m_TxDif", m_pTx);


// Techinque 지정
hr = m_pEft->SetTechnique("Tech0");


// Pass 개수 확인
UINT nPass = 0;
hr = m_pEft->Begin(&nPass, 0);

 

// Pass 수 만큼 같은 물체를 Rendering
for (UINT n = 0; n < nPass; ++n)
{
      hr = m_pEft->BeginPass(n);
      hr = m_pdev->DrawPrimitiveUP(D3DPT_TRIANGLESTRIP, m_iNvx - 2, m_pVtx, sizeof(VtxNDUV1));
      m_pEft->EndPass();
}

 

m_pEft->End();

 

m_pdev->SetVertexShader(NULL);
m_pdev->SetPixelShader(NULL);

 

월드행렬(m_mtWld)에는 Z축은 고정값으로 회전을하고, Y축은 일정 값을 기준으로 계속해서 회전 하는 값이 설정 되어 있다.

 

Glow축(vcGlow) 구하기 위해 뷰행렬의 역행렬을 구해서 .41, .42, .43의 값 (카메라의 위치를 가리키는 x,y,z값( = 카메라의 z방향 축의 벡터 ) 값을 가져와 월드 행렬의 .41, .42, .43에 각각 뺀 값을 정규화 해서 방향을 구하고 있다.)

 

Begin() 함수를 통해 nPass에 입력되는 Tech0(테크닉) 안에 있는 Pass의 갯수만큼 for문을 실행하여 랜더링 하고 있다.

 

여기서 잠깐,, 항상 궁금했던 것이, 뷰행렬의 역행렬에서 우리가 필요로 하는 카메라의 속성들을 구할 수 있는건 어떤 원리일까 라는 것에서 정보를 좀 찾아 보았다.(잠깐 딴 길로 샌다.)

 

뷰 행렬을 얻어오면 이 행렬의 값들은 현재 카메라 행렬의 역처럼 동작을 한다

(오른쪽을 보면 왼쪽으로 회전, 왼쪽을 보면 오른쪽으로 회전 등등)

 

간단하게는 실제로 카메라는 정지해 있고 월드의 상태가 변하기 때문이다.

(카메라를 오른쪽으로 30도 돌아간다는것은 카메라는 정지하고 월드가 왼쪽으로 30도 돌려진다는 개념.)

 

좀 더 자세히 알아 보자면, 파이프 라인에서 모든 객체들의 정점월드 변환을 해준 다음 카메라 변환을 하여 카메라 좌표계를 기준으로 변환 하게 된다.

 

카메라 또한 하나의 객체이므로 월드 변환을 통해서 월드 좌표계에 위치를 하게 되고 이 때 사용한 월드 변환을 카메라의 월드 변환(월드 행렬)이라고 하였을 때, 이 카메라의 월드 변환(월드 행렬)의 역행렬이 뷰 행렬이 된다는 것이고 이것을 객체들의 정점에 곱해주면 카메라 좌표계를 기준으로 하게 된다는 것이다.

(월드 행렬)

 

(뷰 행렬)

 

뷰 행렬이 위와 같은 모습이 되는 이유를 알아 보자면

뷰 행렬은 기본적으로 카메라의 위치(Eye), up벡터, 카메라가 보고 있는 지점(Look)벡터의 속성들로 뷰 행렬을 구성하게 된다.

 

뷰 행렬을 만들기 위해서 먼저 카메라가 바라보는 방향을 설정하는데 이것을 카메라의 z축으로 설정한다.

 

이후 z축과 Up벡터를 이용해서 카메라의 x, y축 벡터를 만들 수 있다.

 

 

Zaxis  = 카메라가 보고 있는 지점 위치(Look) – 카메라의 위치(Eye);

Zaxis = normalize(Zaxis);

 

Xaxis = Cross(Up, Zaxis);

Xaxis = normalize(Xaxis);

 

Yaxis = Cross( Zaxis, Xaxis);

 

OpenGL의 경우 오른손 좌표계 이므로 마지막 y축을 결정할 때 x와 z축의 순서가 바뀌어야 한다.

 

위와 같이 카메라의 x,y,z축을 구했으니 월드 공간에 있는 정점들의 위치를 연산을 통하여 카메라 공간의 x,y,z축에 위치 하도록 한다.

 

연산은 카메라의 각각의 축을 기준으로 카메라와 정점의 상대 위치 값을 내적하면 된다.

위의 계산을 풀면 다음과 같다.

 

X' = Dot(P – Eye, Xaxis); -> X' = Dot(P, Xaxis) - Dot(Eye, Xaxis);

Y' = Dot(P – Eye, Yaxis); -> Y' = Dot(P, Yaxis) - Dot(Eye, Yaxis);

Z' = Dot(P – Eye, Zaxis); -> Z' = Dot(P, Zaxis) - Dot(Eye, Zaxis);

 

정점의 위치를 4차원 동차 좌표로 바꾸고 행렬을 이용하면 다음과 같이 뷰 행렬이 구해 진다.

 

*shader.fx*

 

float4x4 m_mtWld;  // World
float4x4 m_mtViw;  // View
float4x4 m_mtPrj;  // Projection
float4  m_vcLgt;  // Lighting Direction


texture m_TxDif;
sampler SampDif = sampler_state
{
    Texture = <m_TxDif>;
    MinFilter = LINEAR;
    MagFilter = LINEAR;
    MipFilter = NONE;

    AddressU = Wrap;
    AddressV = Wrap;
};

 

// Vertex Shader Output Structure
struct SVsOut
{
     float4 Pos : POSITION;  // Output Position
     float4 Dif : COLOR0;  // Output Diffuse Color
     float2 Tex : TEXCOORD0; // Output Diffuse Map Coordinate
};


SVsOut VtxPrc0( float3 Pos : POSITION,  // vertex position
    float3 Nor : NORMAL,  // Normal Vector
    float2 Tex : TEXCOORD0  // Diffuse Map )
{
     SVsOut Out = (SVsOut)0;

 

     float4 P = 0;
     float3 N = 0;
     float3 L = normalize(m_vcLgt);

 

     P = float4(Pos, 1);
     P = mul(P, m_mtWld);
     P = mul(P, m_mtViw);
     P = mul(P, m_mtPrj);

 

     N = mul(Nor, (float3x3)m_mtWld);  // Rotation Normal Vector

 

     float  D=0;

 

     D = 0.5F * dot(N, L) + 0.5F;   // Lambert I = (V*L+1)/2
     D = pow(D, 1.3f);

 

     Out.Pos = P;       // Output Position
     Out.Dif = D;       // Diffuse
     Out.Tex = Tex;       // Diffuse Map Coordinate

 

     return Out;
}


// Pixel Shader Processing
float4 PxlPrc0(SVsOut In) : COLOR
{
     return In.Dif * tex2D( SampDif, In.Tex ); // Output color = Diffuse * Texture Color
}

 

float  m_GlowThick;  // Glow Thick
float4 m_GlowColor;  // Glow Color
float3 m_GlowAxis;  // Glow Axis


SVsOut VtxPrc1( float4 Pos : POSITION, // vertex position
    float4 Nor : NORMAL, // Normal Vector
    float2 Tex : TEXCOORD0 // Diffuse Map )
{
     SVsOut Out = (SVsOut)0;

 

     float4 P = Pos;
     float3 N = normalize(Nor);
     P +=float4(N,0) * m_GlowThick;
     P = mul(P, m_mtViw);
     P = mul(P, m_mtPrj);


     float Power=0.F;
     Power  = (1+ dot(N, m_GlowAxis) ) * 0.6666F;  // Glow Color Intensity
     Power = pow(Power, 4.0);       // Glow = Glow ^ 4

 

     Out.Pos = P;          // Output Position
     Out.Dif  = m_GlowColor * Power;

 

     Out.Tex = Tex;          // Diffuse Map Coordinate

 

     return Out;
}


// Pixel Shader Processing
float4 PxlPrc1(SVsOut In) : COLOR
{
     return In.Dif;
}


technique Tech0
{
 // Model Rendering
 pass P0
 {
      LIGHTING = FALSE;
      CULLMODE = NONE;

 

      ALPHABLENDENABLE= False;
      ZWRITEENABLE = TRUE;

 

      VertexShader = compile vs_3_0 VtxPrc0();
      PixelShader  = compile ps_3_0 PxlPrc0();


 }

 // Glow Effect Rendering
 pass P1
 {
      LIGHTING = FALSE;
      CULLMODE = CW;

 

      ALPHABLENDENABLE= TRUE;
      SRCBLEND  = SRCALPHA;
      DESTBLEND  = DESTALPHA;
      ZWRITEENABLE = FALSE;

 

      VertexShader = compile vs_3_0 VtxPrc1();
      PixelShader  = compile ps_3_0 PxlPrc1();
 }
}

이번 셰이더 파일은 고정 파이프라인이 아닌 우리가 직접 작성하는 프로그램 가능한 파이프 라인을 이용 한다.

  

P0 패스는 VtxPrc0의 정점 처리 함수와 PxlPrc0의 픽셀 처리 함수를 사용하여 "earth.bmp" 텍스처를 이용한 지구를 랜더링하고, 램버트 확산 기법을 적용 하고 있다.

 

P1 패스는 VtxPrc1의 정점 처리 함수와 PxlPrc1의 픽셀 처리 함수를 사용하여 Glow를 구하는 연산 과정을 거친 후 적용 하고 있다.

728x90

'Shader' 카테고리의 다른 글

파도 흉내 내보기...  (0) 2017.08.31
17. Effect의 상태 설정.  (0) 2017.03.10
16. D3DEffect  (1) 2017.02.14
15. 다중 텍스처 처리(Multi-Texturing)  (0) 2017.02.13
14. 텍스처 처리(단색화, 흐림효과)  (0) 2017.02.09
Posted by 정망스
,


맨 위로
홈으로 ▲위로 ▼아래로 ♥댓글쓰기 새로고침