728x90

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

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

struct VtxN
 {
   D3DXVECTOR3 p;
   D3DXVECTOR3 n;

   VtxN() : p(0, 0, 0), n(0, 0, 0) {}
   VtxN(FLOAT X, FLOAT Y, FLOAT Z
    , FLOAT Nx, FLOAT Ny, FLOAT Nz) : p(X, Y, Z), n(Nx, Ny, Nz) {}

   enum { FVF = (D3DFVF_XYZ | D3DFVF_NORMAL), };
 };

 

LPDIRECT3DVERTEXSHADER9  m_pVs;    // Vertex Shader
LPDIRECT3DVERTEXDECLARATION9  m_pFVF;    // Declarator
LPD3DXCONSTANTTABLE  m_pTbl;

 

D3DXMATRIX  m_mtRot;   // Rotation Matrix
D3DXMATRIX  m_mtWld;   // World Matrix

 

INT   m_iNvx;    // Vertex Number
VtxN*  m_pVtx;    // Vertex Buffer

 

이번엔 법선벡터를 나타내는 D3DXVECTOR3 n, D3DFVF_NORMAL 추가된다.

 

 *shader.fx*

 

float4x4 m_mtWld; // World Matrix
float4x4 m_mtViw; // View Matrix
float4x4 m_mtPrj; // Projection Matrix

 

float3x3 m_mtRot; // Rotation

 

float3 m_LgtDir; // Lighting Direction
float4 m_LgtDif; // Lighting Color

 

static float m_Sat_A = 0.5f; // Saturation Flag A
static float m_Sat_B = 0.5f; // Saturation Flag B


// For Vertex Processing Output
struct SvsOut
{
  float4 Pos : POSITION ; // oPos
  float4 Dif : COLOR0  ; // oD0
};


SvsOut VtxPrc( float4 Pos : POSITION0, float3 Nor : NORMAL0)
{
  float4 P;
  float3 N;

 

  SvsOut Out = (SvsOut)0;    // Initialized to 0
  P = Pos;
  P = mul(P, m_mtWld);    // Transform World
  P = mul(P, m_mtViw);    // Transform View
  P = mul(P, m_mtPrj);    // Transform Projection

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

 

  float3 L = -m_LgtDir;    // 빛의 반대 방향 벡터

 

  float  ReflectIntensity = saturate(m_Sat_A * dot(N, L) + m_Sat_B);

 

  Out.Pos = P;
  Out.Dif = m_LgtDif * ReflectIntensity;

 

  return Out;
}

 

굵은 부분을 위주로 보면 되는 셰이더 코드 이다.

빛의 방향과 정점의 법선벡터를 이용하여 반사될 빛의 세기를 구하여 정점의 diffuse에 설정하는 과정이다.

 

분산 조명은 램버트 확산 이론에 기초를 두고 있다고 한다.

빛의 세기를 빛의 방향(L)과, 정점의 법선 벡터(N)와의 내적을 통해서 구한다.

 

반사 밝기 = Dot(N, L)

 

이 공식을 그대로 적용하면 값의 범위가 -1.0 ~ +1.0이 나오는데 음수 값에선 보이지 않으므로

음수 값을 제거 하기 위해서 saturation을 적용한다.

 

반사 밝기 = saturate( Dot(N, L) )

 

하지만 현실 세계에서 빛은 공기 때문에 산란이 생겨서 90도가 넘어도 약간의 반사가 있다고 한다.그래서 아래와 같은 공식으로 이런 부분을 처리할 수 있다고 한다.

 

반사 밝기 = (Dot(N, L) + 1) /2

 

이 공식을 일반화 시키면 아래와 같다. a(Saturation Flag A), b(Saturation Flag B)

 

반사 밝기 = saturate ( a * Dot(N, L) + b )

 

외부에서 Saturation 값을 바꾸고 싶으면 static 키워드를 해제 하면 되고, 정점의 법선 벡터(N) 같은 경우는 내적을 구하는 dot() 함수를 사용하기 전에 회전과 같이 정점의 상태가 변환 되는 경우에는,

N = mul(Nor, m_mtRot); 과 같이 변환을 먼저 법선벡터에 적용해주고 해야 한다. 

 

구를 만들기위한 정점 데이터 설정

 

// 버텍스 생성
 INT iNSphereSegments = 128;
 m_iNvx = 2 * iNSphereSegments*(iNSphereSegments + 1);

 

 FLOAT fDeltaRingAngle = (D3DX_PI / iNSphereSegments);
 FLOAT fDeltaSegAngle = (2.0f * D3DX_PI / iNSphereSegments);

 

 m_pVtx = new CTest11::VtxN[m_iNvx];
 CTest11::VtxN* pV = m_pVtx;

 

 // Generate the group of rings for the sphere
 for (DWORD ring = 0; ring < iNSphereSegments; ring++)
 {
      FLOAT r0 = 50 * sinf((ring + 0) * fDeltaRingAngle);
      FLOAT r1 = 50 * sinf((ring + 1) * fDeltaRingAngle);
      FLOAT y0 = 50 * cosf((ring + 0) * fDeltaRingAngle);
      FLOAT y1 = 50 * cosf((ring + 1) * fDeltaRingAngle);

 

      // Generate the group of segments for the current ring
      for (DWORD seg = 0; seg < (iNSphereSegments + 1); seg++)
      {
           FLOAT x0 = r0 * sinf(seg * fDeltaSegAngle);
           FLOAT z0 = r0 * cosf(seg * fDeltaSegAngle);
           FLOAT x1 = r1 * sinf(seg * fDeltaSegAngle);
           FLOAT z1 = r1 * cosf(seg * fDeltaSegAngle);

 

           // Add two vertices to the strip which makes up the sphere
           // (using the transformed normal to generate texture coords)
           pV->p.x = x0;
           pV->p.y = y0;
           pV->p.z = z0;

           pV->n = pV->p;
           D3DXVec3Normalize(&pV->n, &pV->n);

           pV++;


           pV->p.x = x1;
           pV->p.y = y1;
           pV->p.z = z1;

           pV->n = pV->p;
           D3DXVec3Normalize(&pV->n, &pV->n);

           pV++;
      }

 }

정점 마다 법선 벡터 구하고 정규화 해서 &pV->n, 정점 법선 벡터에 값 넣어 주고 있다.

 

Render()

 

HRESULT hr = 0;

 

m_pdev->SetRenderState(D3DRS_LIGHTING, FALSE);

 

D3DXMATRIX mtViw;   // View Matrix

D3DXMATRIX mtPrj;   // Projection Matrix

 

D3DXMATRIX mtWld;   // World Matirx
D3DXMATRIX mtWVP;

 

D3DXCOLOR color(1, 0.6f, 1, 0);
D3DXVECTOR4 vcLgt(-1, -1, 0.f, 0);

 

D3DXVec4Normalize(&vcLgt, &vcLgt);


// Get View and Projection Matrix
m_pdev->GetTransform(D3DTS_VIEW, &mtViw);
m_pdev->GetTransform(D3DTS_PROJECTION, &mtPrj);

 

m_pdev->SetVertexDeclaration(m_pFVF);
m_pdev->SetVertexShader(m_pVs);

 

hr = m_pTbl->SetMatrix(m_pdev, "m_mtWld", &m_mtWld);
hr = m_pTbl->SetMatrix(m_pdev, "m_mtViw", &mtViw);
hr = m_pTbl->SetMatrix(m_pdev, "m_mtPrj", &mtPrj);
hr = m_pTbl->SetMatrix(m_pdev, "m_mtRot", &m_mtRot);

 

hr = m_pTbl->SetVector(m_pdev, "m_LgtDir", &vcLgt);
hr = m_pTbl->SetVector(m_pdev, "m_LgtDif", (D3DXVECTOR4*)&color);

 

m_pdev->SetTexture(0, NULL);
m_pdev->DrawPrimitiveUP(D3DPT_TRIANGLESTRIP, m_iNvx - 2, m_pVtx, sizeof(CTest11::VtxN));

 

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

 

랜더 함수에서 추가 된 부분중 굵게 표시된 부분만 보면 될 듯 하다. 빛의 색상과, 정규화를 통해 빛의 방향을 구하여 셰이더 안의 m_LgtDif, m_LgtDir에 각각 설정하고 있다.

728x90
Posted by 정망스
,


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