728x90

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

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

struct VtxRHWDUV1
 {
  D3DXVECTOR4 p;
  DWORD d;
  FLOAT u, v;

  VtxRHWDUV1() : p(0, 0, 0, 1), u(0), v(0), d(0xFFFFFFFF) {}
  VtxRHWDUV1(FLOAT X, FLOAT Y
   , FLOAT U, FLOAT V
   , DWORD D = 0XFFFFFFFF) : p(X, Y, 0, 1), u(U), v(V), d(D) {}

  enum { FVF = (D3DFVF_XYZRHW | D3DFVF_DIFFUSE | D3DFVF_TEX1) };
 };

 

VtxRHWDUV1 m_pVtx[4];   // Vertex Buffer

LPDIRECT3DVERTEXDECLARATION9  m_pFVF;    // Declarator
LPDIRECT3DPIXELSHADER9  m_pPs;    // Pixel Shader
LPD3DXCONSTANTTABLE  m_pTbl;    // Constant Table

LPDIRECT3DTEXTURE9  m_pTx;    // Texture 0

 

이번 정점 데이터의 구조는 w값을 추가로 가지는 XYZRHW, 색상값을 가지는 DIFFUSE, 텍스처를 나타내는 TEX1의 구조로 되어있다.

 

XYZ : 3d 공간상의 좌표, 정점의 변환이 완료되지 않은 좌표(스크린 좌표)

XYZRHW : 2d 인터페이스 좌표에 사용, 정점의 변환이 완료된 좌표(윈도우 좌표)

 

스크린 좌표 : 중심점(0,0) 가운데, 우측으로 x, 위로 y

윈도우 좌표 : 중심점(0,0) 좌측상단, 우측으로 x, 아래로 y 

 

*shader.fx*(단색화)

 

sampler SampDif : register(s0);

 

float4 PxlPrc(float2 Tx0 : TEXCOORD0 /* Texture Coordinate */) : COLOR
{
     float4 Out=0.0F;
     float4 MonoColor ={0.5F, 1.0F, 2.0F, 1.0F};  // 단색화 색상(색상 :   )
     float4 MonoWeight={0.299F, 0.587F, 0.114F, 0.0F}; // 단색화 비중

 

     Out = tex2D( SampDif, Tx0 ); // 샘플링

     Out = dot(Out, MonoWeight);  // 내적(dot)로 단색화
     Out *= MonoColor;    // 단색화 색상을 곱함

 

     Out *= 3.0F;     // Aux: 전체 밝기를 올림
     Out.a = 1.0F;     // 불투명 픽셀로 설정

 

     return Out;
}

 

tex2D 함수를 통해 SampDif(텍스처)에서 Tx0(좌표)의 픽셀값을 가져와 이 값을 MonoWeight(단색화 비중)값과 내적을 시켜 준다.

 

그러면 현재 좌표 픽셀의 단색화 값이 나오게 되고 여기에 MonoColor(단색화 색상)을 곱해주면 비율만큼 색상이 입혀지게 된다.

 

현재 이 코드에는 단색화 비중이 초록색에 가까울수록 제일 높고 파란색에 가까울 경우 낮다.

 

그래서 위 결과 사진을 보면 원본 사진에서 파란색일 경우 연한 파란색이 되고 초록색일 경우 찐한 파란색이 되고 있다.  

 

*shader.fx*(흐림 효과)

 

sampler SampDif : register(s0);

 

float4 PxlPrc(float2 Tx0 : TEXCOORD0 /* Texture Coordinate */) : COLOR
{
     float4 Out=0.0F;

     // 흐림 효과의 픽셀은 인접한 픽셀에 비중을 곱한 값을 누적해서 결정
     // 텍스처 좌표를 변화시키면서 인접한 픽셀을 샘플링
     // Gaussian 분포: exp (x*x/Delta) 값을 픽셀의 비중으로 설정

     for(int x=-4; x<=4; ++x)
     {
          float2 T = Tx0;
          T.x += (2.f * x)/1024.f;      // 텍스처 좌표를 변화시킨다
          Out += tex2D( SampDif, T ) * exp( (-x*x)/8.f); 
// 픽셀을 샘플링하고 가우스 분포 함수로 구한 비중 값을 곱한 후 더한다.

     }

 

     Out *= 0.24F; // 전체 명도를 낮춤
     Out.a = 1.0f; // 불투명 픽셀로 설정

 

     return Out;
}

 

흐림 효과는 인접한 픽셀에 비중 값을 곱하고 더하고를  최종 색상을 만들어 낸다.

 

여기서는 대표적인 알고리즘인 Gaussian Blur을 이용한 방법이다.

Gaussian Blur은 인접한 픽셀까지의 거리를 가지고 비중 값을 exp() 함수로 결정한다.

 

최종 색상 = 

 

인접한 픽셀의 텍스처 좌표를 변화시킨 후에 tex2D() 함수를 사용하여 좌표에 해당하는 픽셀값(P)에 Gaussian 분포값 (exp (x*x/Delta))을 곱하면 최종색상이 나오고 이 값을 출력될 값(Out)에 더해주는것을 for문을 이용해 반복적으로 적용한다.

*shader.fx*(단색화 + 흐림효과)

 

sampler SampDif : register(s0);

 

float4 PxlPrc(float2 Tx0 : TEXCOORD0 /* Texture Coordinate */) : COLOR
{
     float4 Out=0.0F;
     float4 MonoColor ={0.5F, 1.0F, 2.0F, 1.0F};
     float4 MonoWeight={0.8F, 0.9F, 1.0F, 0.0F};

 

     // 텍스처 좌표를 변화시키면서 인접한 픽셀을 샘플링
     // Gaussian 분포: exp (x*x/Delta) 값을 픽셀의 비중으로 설정
     for(int x=-4; x<=4; ++x)
     {
          float2 T = Tx0;
          
T.x += (2.f * x)/1024.f;      // 텍스처 좌표를 변화시킨다
          Out += tex2D( SampDif, T ) * exp( (-x*x)/8.f); // 픽셀을 샘플링하고 가우스 분포 함수로 구한 비중 값을 곱한 후 더한다.
     }

     Out = dot(Out, MonoWeight);  // 단색화
     Out *= MonoColor;    // 단색에 적용할 색상을 곱함


     Out *= 0.18F;     // 전체 명도를 낮춤
     Out.a = 1.0f;     // 불투명 픽셀로 설정

 

     return Out;
}

단색화와 흐림효과 2개를 동시에 적용한 셰이더 파일.

 

*Render()*

 

m_pdev->SetRenderState(D3DRS_LIGHTING, FALSE);
m_pdev->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE);

 

m_pdev->SetSamplerState(0, D3DSAMP_ADDRESSU, D3DTADDRESS_CLAMP);
m_pdev->SetSamplerState(0, D3DSAMP_ADDRESSV, D3DTADDRESS_CLAMP);

 

m_pdev->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_POINT);
m_pdev->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_POINT);
m_pdev->SetSamplerState(0, D3DSAMP_MIPFILTER, D3DTEXF_NONE);

 

m_pdev->SetSamplerState(0, D3DSAMP_ADDRESSU, D3DTADDRESS_CLAMP);
m_pdev->SetSamplerState(0, D3DSAMP_ADDRESSV, D3DTADDRESS_CLAMP);
m_pdev->SetSamplerState(0, D3DSAMP_ADDRESSW, D3DTADDRESS_CLAMP);

 

m_pdev->SetVertexDeclaration(m_pFVF);
m_pdev->SetPixelShader(m_pPs);

 

m_pdev->SetFVF(VtxRHWDUV1::FVF);
m_pdev->SetTexture(0, m_pTx);

 

m_pdev->DrawPrimitiveUP(D3DPT_TRIANGLEFAN, 2, m_pVtx, sizeof(VtxRHWDUV1));


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

 

m_pdev->SetTexture(0, NULL);

m_pdev->SetPixelShader(NULL);

 

SetSamplerState() 함수를 사용하여 u,v,w 값 [0,1]의 범위를 벗어난 정규화 되지 않은 텍스처 좌표에대한 처리를 CLAMP 모드로 설정해주고 있다.

728x90
Posted by 정망스
,
728x90

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

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

struct VtxD
 {
  D3DXVECTOR3 p;
  DWORD d;
  FLOAT u, v;

  VtxD() : p(0, 0, 0), d(0xFFFFFFFF) {}
  VtxD(FLOAT X, FLOAT Y, FLOAT Z, DWORD D = 0XFFFFFFFF) : p(X, Y, Z), d(D) {}

  enum { FVF = (D3DFVF_XYZ | D3DFVF_DIFFUSE) };

 };

 

LPDIRECT3DPIXELSHADER9  m_pPs;    // Pixel Shader
LPD3DXCONSTANTTABLE  m_pTbl;    // Constant Table

INT   m_nType;   // Pixel Processing Type

VtxD  m_pVtx[4];   // Vertex Buffer

 

포지션과, 색상 값으로 구성된 정점 데이터이다.

 

*shader.fx*


// Pixel Processing Type
int g_nPxlPrc = 2;


// 1.Color Inversion
float4 PxlInverse(float4 Input)
{
     return 1 - Input;
}

// 2.Color Monotone
float4 PxlMonotone(float4 InColor)
{
     float4 Out = 0.f;
     float4 d = float4(0.299, 0.587, 0.114, 0);
     float4 t = InColor;
 
     Out = dot(d, t);
     Out.w = 1;
     return Out;
}

// Main Pixel Processing
float4 PxlPrc( in float4 iDif : COLOR0  // From Vertex Processing
) : COLOR0
{
     float4 Out=float4(0,0,0,1);

     if(1== g_nPxlPrc)
      Out = PxlInverse(iDif);
 
     else if(2 == g_nPxlPrc)
      Out = PxlMonotone(iDif);
 
     else
      Out = iDif;

 

     return Out;
}

 

if문을 통해서 g_nPxlPrc의 값이 0이면 기존색상, 1이면 색상 반전, 2이면 단색화 과정을 처리하고 있다.

 

*Render()*

 

HRESULT hr = 0;

 

m_pdev->SetRenderState(D3DRS_LIGHTING, FALSE);

 

m_pdev->SetPixelShader(m_pPs);

 

m_nType = 2;

 

// 픽셀 shader 전역 변수 값 설정
hr = m_pTbl->SetInt(m_pdev, "g_nPxlPrc", m_nType);

 

m_pdev->SetFVF(VtxD::FVF);
m_pdev->DrawPrimitiveUP(D3DPT_TRIANGLEFAN, 2, m_pVtx, sizeof(VtxD));

 

m_pdev->SetPixelShader(NULL);

 

m_nType의 int형 값을 셰이더의 전역 변수 g_nPxlPrc에 설정해주면서 설정 값에 따른 픽셀 처리 과정을 하게끔 하고 있다.

728x90
Posted by 정망스
,
728x90

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

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

struct VtxD
 {
  D3DXVECTOR3 p;
  DWORD d;

  VtxD() : p(0, 0, 0), d(0xFFFFFFFF) {}
  VtxD(FLOAT X, FLOAT Y, FLOAT Z, DWORD D = 0XFFFFFFFF) : p(X, Y, Z), d(D) {}

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

 

LPDIRECT3DDEVICE9  m_pDev;    // Device

LPDIRECT3DPIXELSHADER9  m_pPs;    // Pixel Shader
VtxD  m_pVtx[4];   // Vertex Buffer

 

이번엔 픽셀 셰이더를 사용하기 때문에, LPDIRECT3DPIXELSHADER9 가 새로 추가 되었다.

 

*shader.fx*

 

1. 

// float4 PxlPrc(float4 iDif : COLOR0  // From Vertex Processing
//     ) : COLOR0
// {
//  return iDif;
// }

 

2.
void PxlPrc( in float4 iDif : COLOR0  // From Vertex Processing
   , out float4 oDif: COLOR0  // Output oC0
)
{
 oDif = iDif;
}

 

프로그램 가능한 픽셀 파이프라인은 픽셀의 샘플링(Sampling)와, 다중 텍스처 처리(Multi-Texturing)이다.

 

픽셀 파이프라인에 입력되는 데이터는 정점 처리 과정의 Rastering을 통해서 만들어진 픽셀, 텍스처 좌표, 텍스처 이다.

 

픽셀 처리의 결과는 색상이기 때문에 출력은 float4형이고 시맨틱은 COLOR로 한다.

 

1번은 반환형이 있는 함수를 작성할때, 2번은 void형으로 함수를 작성할때의 방법이다.

 

Create()

 

HRESULT hr = 0;

 

hr = D3DXCompileShaderFromFile(
  "data/Shader.fx"
  , NULL
  , NULL
  , "PxlPrc" // shader 실행 함수
  , "ps_2_0" // shader 버전
  , dwFlags
  , &pShd
  , &pErr
  , NULL
 );

 

hr = m_pdev->CreatePixelShader((DWORD*)pShd->GetBufferPointer(), &m_pPs);

 

정점 처리와 마찬가지로 픽셀 처리도 D3DXCompileShaderFromFile 함수를 사용해서 컴파일을 해야 한다.

 

이 후 컴파일 결과를 가지고 CreatePixelShader 함수를 사용해서 픽셀 셰이더를 생성한다.

 

Render()

 

m_pdev->SetRenderState(D3DRS_LIGHTING, FALSE);

 

m_pdev->SetPixelShader(m_pPs);  // Pixel Shader 사용

 

m_pdev->SetFVF(VtxD::FVF);
m_pdev->DrawPrimitiveUP(D3DPT_TRIANGLEFAN, 2, m_pVtx, sizeof(VtxD));

 

m_pdev->SetPixelShader(NULL);  // Pixel Shader 해제

 

그전 정점 처리 과정과 다른건 SetPixelShader() 함수를 사용해서 픽셀 셰이더의 사용과 해제를 알려준다는 것이다.

그리고 SetFVF라는 함수가 새로 나왔는데 그전 정점 처리 과정에서 사용하던 SetVertexDeclaration() 함수와 사용 의미는 거의 같다고 한다. 차이점은 정점 정보 선언을 SetVertexDeclaration을 이용할 경우 훨씬 많은 데이터를 포함할수가 있다는 이유 때문에 사용한다고 한다.

728x90
Posted by 정망스
,
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

 

포지션과, 법선 벡터 데이터로 이루어진 정점 구조체 이다.

 

*shader.fx* (퐁 반사)

 

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

 

float3x3 m_mtRot; // Rotation

 

float3 m_vcCam; // Camera Position
float m_fSharp; // Sharpness


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


// 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 X;
 float3 N;

 

 SvsOut Out = (SvsOut)0;    // Initialized to 0
 P = Pos;
 P = mul(P, m_mtWld);    // Transform World
 X = P;        // Save Transformed Position
 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;    // Lighting Direction is -Light
 float3 E = normalize(m_vcCam - X); // Normalize Eye Vector
 float3 R = reflect(-L, N);   // Reflection Vector

 

 float  Phong = saturate(dot(R, E)); // Dot R, E

 

 Phong = pow(Phong, m_fSharp);  // Phong Reflection Intensity

 

 Out.Pos = P;      // Output Position
 Out.Dif = m_LgtDif * Phong;   // Output Diffuse = Light Color * Phong

 

 return Out;
}

 

퐁 반사는 정점의 법선 벡터(N)에 의해 반사되는 빛의 반사 벡터(R)와 정점에서 바라보는 카메라에 대한 시선 벡터(E)의 내적의 결과에 멱승(Power)으로 하이라이트(Sharpness) 세기를 구한다.

 

정점의 법선 벡터(N)와 빛의 방향 벡터(L)을 이용하여 반사 벡터(R)을 구한 후 반사의 세기를 결정한다.

 

R = 2 * dot(N, L) * N – L

퐁 반사 밝기 = power(Dot(R, E), Sharpness)

 

D3D에선 반사 벡터를 구해주는 내장 함수 reflect() 가 있다 이 함수는 빛의 방향을 반대 방향으로 값을 주어야 한다. 

 

reflect(L, N) = L - 2 * dot(L, N) * N

반사 벡터 R = reflect(-L, N)

 

멱승(Power)을 구하는 함수는 pow() 이다.

   

float3 m_vcLgt;        // 빛의 방향 벡터

float3 vcNor;  // 정점의 법선 벡터

float3 vcEye;  // 정점에서 카메라 위치를 바라본 시선 방향 벡터

float3  vcR = reflect(-m_vcLgt, vcNor);

float4  Phong = pow( dot(vcR,  vcEye), m_fSharp);

 

주의 해야 할 것은.

1.반사 벡터를 정점에서 카메라의 위치를 바라본 시선 방향 벡터를 구할 때는 구하기 전에 정점의 위치를 월드 변환 시켜 주어야 한다. 카메라와 같은 월드 공간에 위치 시켜 계산 하기 위해서 이다.

 

2.반사 벡터를 구할때는 법선 벡터에 회전 변환을 적용 한 후 구해야 한다.

법선 벡터는 정규화를 통해 법선 벡터의 방향만을 알아내기 위함이기 때문에 방향에 영향을 주는 것은 회전 변환 이기 때문이다.

 

loat4x3 m_mtWld;      // 월드 행렬

float3x3 m_mtRot;      // 회전 행렬

float3  m_vcCam;      // 카메라 위치

float3  m_vcLgt;      // 빛의 방향 벡터

 

float3 vcPos = mul(float4(In.Pos, 1), m_mtWld);      // 위치의 월드 변환

float3 vcNor = mul(In.Nor, m_mtRot);         // 법선의 회전 변환

float3 vcEye = normalize( m_vcCam - vcPos);  // 시선 벡터의 정규화

float3 vcRfc = reflect(-m_vcLgt, vcNor);     // 반사 벡터

float4 Phong = pow( dot(vcRfc, vcEye), m_fSharp); // 퐁 반사 세기

 

*shader.fx* (블린-퐁 반사) 

 

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

 

float3x3 m_mtRot; // Rotation

 

float3 m_vcCam; // Camera Position
float m_fSharp; // Sharpness


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


// 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 X;
 float3 N;

 

 SvsOut Out = (SvsOut)0;    // Initialized to 0
 P = Pos;
 P = mul(P, m_mtWld);    // Transform World
 X = P;        // Save Transformed Position

 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;    // Lighting Direction is -Light
 float3 E = normalize(m_vcCam - X); // Normalize Eye Vector
 float3 H = normalize(E + L);  // Half Vector

 

 float  Blinn = saturate(dot(N, H)); // Dot N, H

 

 Blinn = pow(Blinn, m_fSharp);  // Blinn-Phong Reflection Intensity

 

 Out.Pos = P;      // Output Position
 Out.Dif = m_LgtDif * Blinn;   // Output Diffuse = Light Color * Blinn

 

 return Out;
}

 

블린-퐁 반사는 반사 벡터와, 시선 벡터를 사용하지 않고, 하프 벡터와, 정점의 법선 벡터를 이용한다.

 

Half 벡터 = normalize(E + L)

Blinn-Phong 반사 세기 = Dot(N, H)^Sharpness

 

여기서 하프 벡터란, 두 벡터 ( E(시선 벡터), L(빛의 방향 벡터) )의 덧셈으로 인해 이루어지는 평행사변형의 대각선 성분을 하프 벡터라고 한다.

 

두 벡터가 정규화 되어 길이가 1인 벡터라면 평행사변형의 모든 변의 길이가 1이 되고 대각선분이 평행사변형을 두 개의 삼각형으로 나눌 경우 두 삼각형이 두변의 길이가 같고 사이각이 같은 합동 삼각형이 되므로 E, L 벡터의 사이각을 정확하게 2등분 하기 때문에,

 

하프 벡터 H = E + L 에 정규화를 하면 H = E + L / | E + L | 가 되는것이고

위의 셰이더 코드 부분에선 float3 H = normalize(E + L);  // Half Vector 이다.

 

Render()

 

HRESULT hr = 0;

 

m_pdev->SetRenderState(D3DRS_LIGHTING, FALSE);

 

D3DXMATRIX mtViw;   // View Matrix
D3DXMATRIX mtPrj;   // Projection Matrix

 

D3DXMATRIX mtWld;   // World Matirx
D3DXVECTOR4 vcCam;   // Camera Position

 

FLOAT  fSharp = 10.f; // Sharpness

 

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);


D3DXMATRIX mtViwI;
D3DXMatrixInverse(&mtViwI, NULL, &mtViw);
vcCam = D3DXVECTOR4(mtViwI._41, mtViwI._42, mtViwI._43, 0);

 

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_vcCam", &vcCam);
hr = m_pTbl->SetFloat(m_pdev, "m_fSharp", fSharp);
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(CTest12::VtxN));

 

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

 

뷰 행렬을 얻어와서 역행렬을 구해 월드상의 카메라 위치를 알아와 vcCam에 x,y,z 값을 설정해준다.

셰이더에서 정점에서 카메라를 바라보는 시선 벡터를 구할때의 정점이 월드 공간의 좌표이기때문에 일치 시켜주기 위해서이다.

728x90
Posted by 정망스
,
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 정망스
,


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