수업


✅ 주제

  • 본 강의는 Specular Light(반사광)Phong 반사 모델에 기반하여 구현하는 것이다.
  • 빛이 표면에 반사된 방향과 카메라(눈)가 바라보는 방향 사이의 각도에 따라 강한 하이라이트 효과를 계산한다.
  • 이전에 구현한 Diffuse(분산광)는 법선 벡터와 빛의 방향에 집중했지만, Specular는 빛이 눈에 어떻게 반사되어 들어오느냐가 핵심이다.

📘 개념

🔹 핵심 비교

조명 종류기준 벡터밝기 조건주요 공식
DiffuseNormal vs Lightθ 작을수록 밝음 (표면과 빛이 일직선일수록)dot(N, L)
SpecularReflect vs Eyeθ 작을수록 밝음 (반사된 빛이 눈 방향과 일치할수록)dot(R, E)
  • Specular 조명눈뽕, 즉 금속이나 광택 있는 물체에서 빛이 한 방향으로 강하게 반사되는 현상을 표현한다.
  • 하이라이트의 강도는 반사벡터(R)시선벡터(E) 간의 각도에 의해 결정되며,
    각도가 작을수록 밝기가 커진다.

📐 수학 공식 분석

1. 반사 벡터 R 계산 (Phong 모델)

R = L - 2 * N * dot(L, N)
  • L: 광원 방향 벡터
  • N: 표면 노멀
  • R: 반사 방향 (빛이 튕겨나가는 방향)

2. 시선 벡터 E 계산

E = normalize(CameraPos - WorldPos)
  • 카메라 위치에서 픽셀의 월드 위치를 뺀 방향 벡터

3. 최종 밝기 계산

value = saturate(dot(R, E))
specular = pow(value, shininess)
  • saturate(): 0~1로 클램핑
  • pow(): shininess 계수로 강조 범위 조절. 클수록 좁고 강함

🧾 용어 정리

용어의미
Specular Light반사광. 광택, 유리, 금속 반사 효과
Phong 모델반사광을 계산하는 모델 (R과 E 내적 사용)
LightDir광원 방향 벡터
LightSpecular광원의 반사광 색상
MaterialSpecular재질이 반사광을 얼마나 받아들이는가
R (Reflect)반사 벡터
E (Eye)시선 벡터 (카메라 → 픽셀)
pow()강조 범위 조절. 클수록 하이라이트 영역이 작고 강함
View 행렬카메라 공간으로 변환하는 행렬. 위치 정보 포함

🧠 코드 분석


📁 HLSL 셰이더: Lighting_Specular.fx

1. 변수 정의

float3 LightDir;
float4 LightSpecular;
float4 MaterialSpecular;
Texture2D DiffuseMap;
  • LightDir: 빛의 방향
  • LightSpecular: 광원의 색상
  • MaterialSpecular: 물체의 반사 재질
  • DiffuseMap: 필요 시 사용 가능

2. 전역 구조체 정의 (00. Global.fx)

struct MeshOutput
{
    float4 position : SV_POSITION;
    float3 worldPosition : POSITION1;
    float2 uv : TEXCOORD;
    float3 normal : NORMAL;
};
  • 추가된 worldPosition은 카메라 위치와의 방향 계산에 필요

3. 정점 셰이더(VS)

MeshOutput VS(VertexTextureNormal input)
{
    MeshOutput output;
    output.position = mul(input.position, W);
    output.worldPosition = input.position; // World 좌표 보관
    output.position = mul(output.position, VP);

    output.uv = input.uv;
    output.normal = mul(input.normal, (float3x3)W);
    return output;
}

4. 픽셀 셰이더(PS)

float4 PS(MeshOutput input) : SV_TARGET
{
    float3 R = LightDir - (2 * input.normal * dot(LightDir, input.normal)); // 반사 벡터
    R = normalize(R);

    float3 cameraPosition = -V._41_42_43; // View 행렬로부터 카메라 위치 추출
    float3 E = normalize(cameraPosition - input.worldPosition); // 시선 벡터

    float value = saturate(dot(R, E)); // R과 E 사이 내적
    float specular = pow(value, 10); // 강조 조절

    float4 color = LightSpecular * MaterialSpecular * specular;
    return color;
}
  • dot(R, E) 내적 결과가 클수록 → 시선 방향과 반사 방향이 유사함 → 밝음
  • pow() 값이 클수록 → 더 작고 선명한 하이라이트

📁 C++: SpecularDemo.cpp

1. Init()

_shader = make_shared<Shader>(L"11. Lighting_Specular.fx");
_camera->GetOrAddTransform()->SetPosition(Vec3{ 0.f, 0.f, -10.f });
  • 셰이더와 카메라 설정
_obj = make_shared<GameObject>();
_obj->GetMeshRenderer()->SetShader(_shader);
_obj->GetMeshRenderer()->SetMesh(...);
_obj->GetMeshRenderer()->SetTexture(...);
  • 구체 메시 및 텍스처 연결
_obj2 = make_shared<GameObject>();
_obj2->GetOrAddTransform()->SetPosition(Vec3{ 0.5f, 0.f, 2.f });
_obj2->GetMeshRenderer()->SetShader(_shader);
_obj2->GetMeshRenderer()->SetMesh(...);
_obj2->GetMeshRenderer()->SetTexture(...);
  • 큐브 메시 설정

2. Update()

Vec4 light{ 1.f };
_shader->GetVector("LightSpecular")->SetFloatVector((float*)&light);

Vec3 lightDir{ 1.f, -1.f, 0.f };
lightDir.Normalize();
_shader->GetVector("LightDir")->SetFloatVector((float*)&lightDir);

Vec4 material(1.f);
_shader->GetVector("MaterialSpecular")->SetFloatVector((float*)&material);

_obj->Update();
_obj2->Update();
  • 오른쪽 아래 방향으로 빛 설정
  • 흰색 빛으로 모든 색 수용

🔬 실험과 결과

테스트결과
lightDir{1, 0, 0}오른쪽에서 → 왼쪽 면 밝게
lightDir{1, -1, 0}오른쪽 아래 → 왼쪽 위 밝게
pow(value, 1)하이라이트 범위 넓음, 부드러움
pow(value, 10)작고 강한 하이라이트 (눈뽕 효과)

✅ 핵심

  • Specular 조명은 카메라를 기준으로 반사광의 강도를 계산하는 조명 모델이다.
  • Phong 반사 모델에 기반하여, 반사벡터 R과 시선벡터 E의 내적을 통해 밝기를 구하고,
    pow() 함수로 강조 범위를 조절한다.
  • saturate()로 범위 제한, 카메라 위치는 View 행렬로부터 계산
  • Specular 조명은 시선 중심의 반사광 계산이며,
    Diffuse와 Ambient와 함께 조합하면 가장 기본적인 조명 세트를 구성할 수 있다.

profile
李家네_공부방

0개의 댓글