수업


✅ 주제

  • 본 강의의 주제는 Ambient / Diffuse / Specular / Emissive 네 가지 조명을 하나의 구조로 통합하고,
  • 이를 바탕으로 Shader와 RenderManager 간 조명-재질 정보 전달 구조를 확립하는 것이다.
  • 이를 통해 향후 다중 광원이나 다양한 재질 반응 시스템 구현을 위한 통합 조명 프레임워크 기반을 마련한다.

📘 개념

  • 기존에는 조명별로 셰이더 파일을 따로 작성했으나, 이제는 하나의 쉐이더에서 조명 종류별 연산을 함수로 처리하고,
  • ConstantBuffer를 통해 구조체 단위로 값들을 GPU에 전달하여, 코드 재사용성과 유지 보수성을 향상시킨다.
  • 핵심 구성 요소는 다음과 같다:

🔷 핵심 구조 정리

요소목적
Light.fx네 가지 조명을 연산하는 공용 쉐이더 함수 및 구조체 정의
Lighting.fxComputeLight 호출만 수행하는 실제 렌더링 쉐이더
RenderManager조명 및 재질 데이터를 셰이더로 밀어 넣는 역할
LightingDemo.cpp오브젝트마다 조명/재질 데이터를 지정 및 연동

🧾 용어 정리

용어설명
Ambient Light기본 환경광. 그림자 없이 은은하게 조명
Diffuse Light람베르트 반사 모델. 빛 방향과 노멀 각도 기반
Specular Light퐁 반사 모델. 반사 벡터와 시선 벡터 일치 시 하이라이트
Emissive Light외곽선 강조용 조명. 노멀과 시선이 수직일수록 강조
LightDesc / MaterialDesc조명/재질 정보를 담은 구조체
ConstantBuffer구조체 정보를 GPU로 전달하는 상수 버퍼
ComputeLight()네 가지 조명 연산을 통합 처리하는 함수

💻 코드 분석


📁 1. 조명 연산 쉐이더 – 00. Light.fx

🔸 구조체 선언

struct LightDesc {
    float4 ambient;
    float4 diffuse;
    float4 specular;
    float4 emissive;
    float3 direction;
    float padding; // 16바이트 정렬
};

struct MaterialDesc {
    float4 ambient;
    float4 diffuse;
    float4 specular;
    float4 emissive;
};
  • 조명 및 재질 구조체를 분리하여 관리
  • float3 뒤에 반드시 float padding 삽입 → 16바이트 정렬

🔸 ConstantBuffer 정의

cbuffer LightBuffer {
    LightDesc GlobalLight;
};

cbuffer MaterialBuffer {
    MaterialDesc Material;
};
  • Global 조명과 오브젝트 재질을 각각 따로 관리 → 효율적인 상태 갱신

🔸 Texture & Compute 함수

Texture2D DiffuseMap;

float4 ComputeLight(float3 normal, float2 uv, float3 worldPosition)
{
    float4 ambientColor = 0;
    float4 diffuseColor = 0;
    float4 specularColor = 0;
    float4 emissiveColor = 0;

    // Ambient
    {
        float4 color = GlobalLight.ambient * Material.ambient;
        ambientColor = DiffuseMap.Sample(LinearSampler, uv) * color;
    }

    // Diffuse
    {
        float value = dot(-GlobalLight.direction, normalize(normal));
        float4 color = DiffuseMap.Sample(LinearSampler, uv);
        diffuseColor = color * value * GlobalLight.diffuse * Material.diffuse;
    }

    // Specular
    {
        float3 R = normalize(GlobalLight.direction - 2 * normal * dot(GlobalLight.direction, normal));
        float3 E = normalize(CameraPosition() - worldPosition);
        float value = saturate(dot(R, E));
        float specular = pow(value, 10);
        specularColor = GlobalLight.specular * Material.specular * specular;
    }

    // Emissive
    {
        float3 E = normalize(CameraPosition() - worldPosition);
        float value = saturate(dot(E, normal));
        float emissive = pow(smoothstep(0.0f, 1.0f, 1.0f - value), 2);
        emissiveColor = GlobalLight.emissive * Material.emissive * emissive;
    }

    return ambientColor + diffuseColor + specularColor + emissiveColor;
}
  • 모든 조명 연산을 하나의 함수로 통합
  • dot, smoothstep, pow 등을 조합하여 각 조명 효과 구현

📄 2. 통합 셰이더 파일 – 13. Lighting.fx

#include "00. Global.fx"
#include "00. Light.fx"

MeshOutput VS(VertexTextureNormal input)
{
    MeshOutput output;
    output.position = mul(input.position, W);
    output.worldPosition = input.position.xyz;
    output.position = mul(output.position, VP);
    output.uv = input.uv;
    output.normal = mul(input.normal, (float3x3)W);
    return output;
}

float4 PS(MeshOutput input) : SV_TARGET
{
    return ComputeLight(input.normal, input.uv, input.worldPosition);
}

technique11 T0 {
    PASS_VP(P0, VS, PS);
};
  • 조명 연산은 ComputeLight에 위임
  • Lighting.fx는 구조만 담당 → 재사용성 극대화

📁 3. RenderManager 설정

선언

LightDesc _lightDesc;
shared_ptr<ConstantBuffer<LightDesc>> _lightBuffer;
ComPtr<ID3DX11EffectConstantBuffer> _lightEffectBuffer;

MaterialDesc _materialDesc;
shared_ptr<ConstantBuffer<MaterialDesc>> _materialBuffer;
ComPtr<ID3DX11EffectConstantBuffer> _materialEffectBuffer;

Init()

_lightBuffer = make_shared<ConstantBuffer<LightDesc>>();
_lightBuffer->Create();
_lightEffectBuffer = _shader->GetConstantBuffer("LightBuffer");

_materialBuffer = make_shared<ConstantBuffer<MaterialDesc>>();
_materialBuffer->Create();
_materialEffectBuffer = _shader->GetConstantBuffer("MaterialBuffer");

Push 함수

void RenderManager::PushLightData(const LightDesc& desc) {
    _lightDesc = desc;
    _lightBuffer->CopyData(_lightDesc);
    _lightEffectBuffer->SetConstantBuffer(_lightBuffer->GetComPtr().Get());
}

void RenderManager::PushMaterialData(const MaterialDesc& desc) {
    _materialDesc = desc;
    _materialBuffer->CopyData(_materialDesc);
    _materialEffectBuffer->SetConstantBuffer(_materialBuffer->GetComPtr().Get());
}
  • 구조체 데이터를 버퍼에 복사 → GPU에 전달

📁 4. LightingDemo 적용

Update()

{
    LightDesc lightDesc;
    lightDesc.ambient = Vec4(0.5f);
    lightDesc.diffuse = Vec4(1.f);
    lightDesc.specular = Vec4(1.f);
    lightDesc.emissive = Vec4(1.f);
    lightDesc.direction = Vec3(0.f, -1.f, 0.f);
    RENDER->PushLightData(lightDesc);
}

{
    MaterialDesc desc;
    desc.ambient = Vec4(0.2f);
    desc.diffuse = Vec4(1.f);
    desc.specular = Vec4(1.f);
    desc.emissive = Vec4(0.3f, 0.f, 0.f, 1.f);
    RENDER->PushMaterialData(desc);
    _obj->Update();
}

{
    MaterialDesc desc;
    desc.ambient = Vec4(0.5f);
    desc.diffuse = Vec4(1.f);
    RENDER->PushMaterialData(desc);
    _obj2->Update();
}
  • 오브젝트마다 재질 값 개별 지정
  • 큐브는 Ambient 강화로 전체 발광 효과 시도 가능

🔍 테스트 및 실습

실험결과
ambient = 0어두운 곳 완전 검정
ambient = 0.5전체적으로 은은하게 밝음
emissive 값 증가외곽선 강조 증가
specular = 0하이라이트 없음

✅ 핵심

  • 조명 구조체 기반 통합 시스템을 구현
  • 각 조명 모델의 수식을 함수화하여 ComputeLight에서 처리
  • 16바이트 정렬 준수 필수 → float3 뒤에는 padding
  • RenderManager를 통해 Light/Material 구조체 데이터를 정확히 셰이더에 전달
  • 실습을 통해 Ambient / Diffuse / Specular / Emissive 효과를 자유롭게 실험 가능

profile
李家네_공부방

0개의 댓글