※ 제 개인 공부이므로 풀 소스 코드는 작성하지 않습니다. ※
※ 생략 되어 있는 부분이 많습니다. ※
{
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에 각각 설정하고 있다.
'Shader' 카테고리의 다른 글
12. 알록달록 사각형(간단한 픽셀 처리) (0) | 2017.02.09 |
---|---|
11. 스페큘러 조명을 해보자(퐁, 블린-퐁 반사) (0) | 2017.02.08 |
9. 안개 효과를 내보자. (0) | 2017.02.06 |
8. 텍스처를 입혀보자. (지구) (0) | 2017.02.06 |
7. 알록달록 사각형을 돌려보자.(변환2) (0) | 2017.02.06 |