본문 바로가기

DirectX11 3D/기본 문법

<DirectX3D 11> 80 - SpotLighting

필요한 개념


그림 6 -1

 


주인공을 비출때 SpotLighting을 씀

SpotLighting : 위에서 밑으로 퍼지는 빛

다 동일하지만 깍아내는 부분만 다르다.


 

 

 

 

Lighting.h 추가된 내용


더보기
#define MAX_SPOT_LIGHTS 256
struct SpotLight
{
	Color Ambient;
	Color Diffuse;
	Color Specular;
	Color Emissive;

	Vector3 Position;
	float Range;

	Vector3 Direction;
	float Angle;

	float Intensity;
	float Padding[3];
};

class Lighting
{
public:
	UINT SpotLightCount() { return spotLightCount; }
	UINT SpotLights(OUT SpotLight* lights);
	void AddSpotLight(SpotLight& light);
	SpotLight& GetSpotLight(UINT index);

private:    
	UINT spotLightCount = 0;
	SpotLight spotLights[MAX_SPOT_LIGHTS];
}

 

 

 

 

 

 

 

Lighting.cpp 추가된 내용


더보기
UINT Lighting::SpotLights(OUT SpotLight* lights)
{
	memcpy(lights, spotLights, sizeof(SpotLight) * spotLightCount);

	return spotLightCount;
}

void Lighting::AddSpotLight(SpotLight& light)
{
	spotLights[spotLightCount] = light;
	spotLightCount++;
}

SpotLight& Lighting::GetSpotLight(UINT index)
{
	return spotLights[index];
}

 

 

 

 

 

 

 

PerFrame.h 추가된 내용


더보기
class PerFrame
{
private:
	struct SpotLightDesc
	{
		UINT Count = 0;
		float Padding[3];

		SpotLight Lights[MAX_SPOT_LIGHTS];
	} spotLightDesc;
    
private:
	ConstantBuffer* spotLightBuffer;
	ID3DX11EffectConstantBuffer* sSpotLightBuffer;
}

 

 

 

 

 

 

 

PerFrame.cpp 추가된 내용


더보기
#include "Framework.h"
#include "PerFrame.h"

PerFrame::PerFrame(Shader* shader)
	: shader(shader)
{
	...
	spotLightBuffer = new ConstantBuffer(&spotLightDesc, sizeof(SpotLightDesc));
	sSpotLightBuffer = shader->AsConstantBuffer("CB_SpotLights");
}

PerFrame::~PerFrame()
{
	...
	SafeDelete(spotLightBuffer);
}

void PerFrame::Update()
{
	...
	spotLightDesc.Count = Lighting::Get()->SpotLights(spotLightDesc.Lights);
}

void PerFrame::Render()
{	
	...
	spotLightBuffer->Render();
	sSpotLightBuffer->SetConstantBuffer(spotLightBuffer->Buffer());
}

 

 

 

00_Lighting.fx 추가된 내용


더보기
...
////////////////////////////////////////////////////////////////////////////

// 한 맵에 256넘어가는것은 초대형 맵을 제외해서는 없다.
#define MAX_SPOT_LIGHTS 256
struct SpotLight
{
    float4 Ambient; // 주변광을 줄거고 얘에 따라 주변광을 줄일수 있도록
    float4 Diffuse; // 주 빛의 색
    float4 Specular; //  반사 색
    float4 Emissive; // 얘에 의해 주변광이 표시될 색
   
    // 포인트 라이팅도 물체의 색에 영향을 주므로 포인트 라이팅도 매터리얼을 가지고 있다.
    
    float3 Position; // 위치
    float Range; // 범위
    
    float3 Direction; // 조명의 방향
    float Angle; // 조명의 앵글
    
    float Intensity; // 강도
    float3 Padding;
    
    // Padding을 채우는 이유? 배열로 버퍼의 값을 받기 위해 패딩을 사용, 만약 없으면 데이터가 당겨져 제대로 받을 수 없다.
};

cbuffer CB_SpotLights
{
    uint SpotLightCount; // 라이트의 개수(들어온 갯수만)
    float3 CB_SpotLights_Padding; // 16바이트로 끊기 위해
    
    SpotLight SpotLights[MAX_SPOT_LIGHTS];
};

void ComputeSpotLight(inout MaterialDesc output, float3 normal, float3 wPosition)
{
    output = MakeMaterial();
    MaterialDesc result = MakeMaterial();
    
    for (uint i = 0; i < SpotLightCount; i++)
    {
        float3 light = SpotLights[i].Position - wPosition;
        float dist = length(light);
        
        
        [flatten]
        if (dist > SpotLights[i].Range)
            continue;
        
        
        light /= dist;
        
        
        result.Ambient = SpotLights[i].Ambient * Material.Ambient;
        
        float NdotL = dot(light, normalize(normal));
        float3 E = normalize(ViewPosition() - wPosition);

    
    
        [flatten]
        if (NdotL > 0.0f)
        {
            result.Diffuse = Material.Diffuse * NdotL * SpotLights[i].Diffuse;

            [flatten]
            if (Material.Specular.a > 0.0f)
            {
                float3 R = normalize(reflect(-light, normal));
                float RdotE = saturate(dot(R, E));
                
                float specular = pow(RdotE, Material.Specular.a);
                result.Specular = Material.Specular * specular * SpotLights[i].Specular;
            }
        }
        
        [flatten]
        if (Material.Emissive.a > 0.0f)
        {
            float NdotE = dot(E, normalize(normal));
            float emissive = smoothstep(1.0f - Material.Emissive.a, 1.0f, 1.0f - saturate(NdotE));
            result.Emissive = Material.Emissive * emissive * SpotLights[i].Emissive;
        }
        
        
        // 어떻게 조명을 깍어내는냐
        
        // light가 원 모양 밑에서(wPosition) Direction으로 가는것을 반대로 뒤집는다.(위로 올라가는 것을 밑으로 내려가게끔)
        // pow는 구를 형성시키기 위해 사용
        float temp = pow(saturate(dot(-light, SpotLights[i].Direction)), SpotLights[i].Angle);
        
        // 이미 pow가 정의되어 제곱이 계산되었으므로, Temp를 제곱할 필요가 없다.
        // 값 뒤집음(1.0f /max(1.0f - SpotLights[i].Intensity, 1e-8f))
        // 역수로 취했는데 0이면 조명이 제대로 들어 갈 수 없으니까, 1e-8f을 준다.(조명의 강도는 0이 존재하지 않음, 또한 0으로 1.0f를 나눌 수 없다.)
        float att = temp * (1.0f / max(1.0f - SpotLights[i].Intensity, 1e-8f));
        
        
        output.Ambient += result.Ambient * temp;
        output.Diffuse += result.Diffuse * att;
        output.Specular += result.Specular * att;
        output.Emissive += result.Emissive * att;
    }

}

 

 

 

 

 

 

 

 

 

 

 

 

80_SpotLighting.fx


더보기
#include "00_Global.fx"
#include "00_Light.fx"
#include "00_Render.fx"


float4 PS(MeshOutput input) : SV_Target
{
    Texture(Material.Diffuse, DiffuseMap, input.Uv);
    Texture(Material.Specular, SpecularMap, input.Uv);
    
    
    MaterialDesc output = MakeMaterial();
    MaterialDesc result = MakeMaterial();
    
    ComputeLight(output, input.Normal, input.wPosition);
    AddMaterial(result, output);
    
    ComputePointLight(output, input.Normal, input.wPosition);
    AddMaterial(result, output);
    
    ComputeSpotLight(output, input.Normal, input.wPosition);
    AddMaterial(result, output);
    
    return float4(MaterialToColor(result), 1.0f);
}

technique11 T0
{
    P_VP(P0, VS_Mesh, PS)
    P_VP(P1, VS_Model, PS)
    P_VP(P2, VS_Animation, PS)
}

 

 

 

 

결과