수업


주제

  • DirectX11 기반 C++ 엔진에서 정점의 Normal 벡터를 정의하고, 이를 바탕으로 조명 연산을 수행하는 방법을 학습한다.
  • 표면에 수직인 Normal 벡터와 광원 방향 벡터의 내적(dot product) 결과를 통해, 픽셀 셰이더 단계에서 광량을 조절하는 기본 조명 기법을 구현한다.
  • 정점 데이터에 Normal을 포함한 구조체를 생성하고, Quad, Cube, Sphere 등의 도형에 적절한 Normal을 할당하여 기초적인 Diffuse 조명 효과를 실현한다.

개념

  • Normal Vector(법선 벡터): 표면에 수직인 단위 벡터. 조명 연산의 방향성을 판단하는 기준이 된다.
  • Dot Product(내적): 두 벡터의 각도를 코사인 값으로 반환.
    • dot(normal, -light) → 0도: 1(밝음), 90도: 0(어두움)
  • 정점 기반 조명(Vertex Lighting): 각 정점에 Normal을 정의하고 이를 기반으로 조명을 계산하는 방식.
  • Transform Normal: 위치 변환(translation)을 제외하고 회전만 적용된 Normal 벡터 변환.
  • float3x3 World 행렬: World matrix에서 회전 행렬만 추출하여 Normal에 곱하는 구조.

용어정리

용어설명
Normal Vector표면에 수직인 벡터. 광원과의 각도를 통해 픽셀 밝기를 계산
dot(N, -L)Normal과 광원 방향(-L)의 내적. 1~0 범위의 조명 세기 결과
VertexTextureNormalData위치, UV, Normal 정보를 갖는 사용자 정의 정점 구조체
Transform NormalNormal 벡터에 평행이동을 제외한 회전 변환만 적용
float3x3 World월드 행렬의 회전 행렬(3x3)만 추출한 형태
LightDir월드 기준의 광원 방향 벡터
Wireframe메시 외곽선만 보이는 모드. 구조 확인용

코드 분석

✅ 1. 정점 구조체 정의 - VertexTextureNormalData

struct VertexTextureNormalData
{
    Vec3 position = { 0, 0, 0 };
    Vec2 uv = { 0, 0 };
    Vec3 normal = { 0, 0, 0 };
};
  • 기존 정점 정보(position, uv)에 Normal을 추가.
  • 이후 조명 계산을 위해 반드시 필요한 구성이다.

✅ 2. GeometryHelper 도형별 Normal 정의

▷ CreateQuad

vtx[i].normal = Vec3(0.f, 0.f, -1.f);
  • Quad는 평면이므로 모든 정점의 Normal이 동일하다.
  • Look 방향의 반대(-Z)가 평면의 Normal이 된다.

▷ CreateCube

// 앞면 Normal
Vec3(0.0f, 0.0f, -1.0f);
// 뒷면 Normal
Vec3(0.0f, 0.0f, 1.0f);
// 윗면
Vec3(0.0f, 1.0f, 0.0f);
// 아랫면
Vec3(0.0f, -1.0f, 0.0f);
// 왼쪽/오른쪽
Vec3(±1.0f, 0.0f, 0.0f);
  • Cube는 각 면의 방향에 따라 정해진 Normal을 정점에 설정한다.

▷ CreateSphere

v.normal = v.position;
v.normal.Normalize();
  • Sphere는 원점에서 정점 위치로 향하는 벡터가 바로 Normal.
  • 정점의 위치 벡터를 정규화하여 Normal로 사용한다.

✅ 3. Shader 구현 – 07. Normal.fx

▷ Vertex Shader (VS)

output.normal = mul(input.normal, (float3x3)World);
  • Normal에 회전만 적용.
  • 4x4 행렬이 아닌 3x3 행렬을 사용하여 위치 이동은 배제하고 회전만 적용한다.

▷ Pixel Shader (PS)

float3 normal = normalize(input.normal);
float3 light = -LightDir;
return Texture0.Sample(Sampler0, input.uv) * dot(light, normal);
  • Normal과 LightDir의 내적을 통해 조명 세기를 계산.
  • 텍스처 색상에 dot 값을 곱하여 밝기를 조절.

▷ 와이어프레임 상태 추가

RasterizerState FillModeWireFrame
{
    FillMode = Wireframe;
};
  • 도형 구조를 확인할 수 있도록 Wireframe 패스를 추가.

✅ 4. NormalDemo 클래스 구현

_shader = make_shared<Shader>(L"07. Normal.fx");
_geometry = make_shared<Geometry<VertexTextureNormalData>>();
GeometryHelper::CreateCube(_geometry); // Sphere로 교체 가능
_texture = RESOURCES->Load<Texture>(L"Veigar", L"..\\Resources\\Textures\\veigar.jpg");
_lightDir = Vec3(-1.f, 0.f, 0.f);
  • Shader는 Normal 연산용 fx 파일로 설정.
  • 도형은 Normal 정보 포함 구조로 생성.
  • 빛은 오른쪽에서 왼쪽 방향으로 설정됨.

핵심

  1. Normal 벡터는 조명 연산의 핵심 요소로, 정점 단위로 정의되어야 한다.
  2. Normal과 조명 방향 벡터의 내적(dot) 결과를 기반으로 픽셀 밝기를 결정한다.
  3. Transform Normal 시 평행이동은 제외하고 회전만 적용되어야 하므로 float3x3을 사용한다.
  4. 픽셀 셰이더에서는 텍스처 색상에 dot 결과를 곱해 밝기 효과를 표현한다.
  5. Sphere에서는 정점 위치 벡터 자체가 Normal 벡터가 되며, Cube는 각 면 기준으로 Normal을 고정한다.
  6. Wireframe 모드를 통해 메시 구조와 조명 효과를 시각적으로 디버깅할 수 있다.
  7. 이 구조는 향후 NormalMap 기반 조명, Specular, Phong/Blinn-Phong 모델로 확장 가능하다.

profile
李家네_공부방

0개의 댓글