DirectX 11에서 Spotlight를 만들기. Spotlight용 셰이더를 작성한다. 먼저, 셰이더 상수 버퍼를 정의한다. ObjectTransforms 상수 버퍼는 개체의 변환 행렬을 저장하고 SpotLightBuffer 상수 버퍼는 스포트 라이트의 위치, 방향, 색상 및 라이트의 스포트 파라미터를 저장한다. Spotlight 셰이더의 VSMain 함수는 입력 버텍스의 위치와 법선을 투영 공간으로 변환하고 광원 공간으로 변환한다.

 

목차

     

     


     

     

    Spot Lighting

     

    DirectX 11에서 Spotlight를 만들기

    1. Spotlight용 셰이더를 작성한다. 
    2. 먼저, 셰이더 상수 버퍼를 정의한다. ObjectTransforms 상수 버퍼는 개체의 변환 행렬을 저장하고 SpotLightBuffer 상수 버퍼는 스포트 라이트의 위치, 방향, 색상 및 라이트의 스포트 파라미터를 저장한다.
    3. Spotlight 셰이더의 VSMain 함수는 입력 버텍스의 위치와 법선을 투영 공간으로 변환하고 광원 공간으로 변환한다.
    4. PSMain 함수는 빛의 방향을 계산하고 빛의 색상과 빛의 스포트 파라미터를 사용하여 빛의 감쇠를 계산한다. 그런 다음, 빛과 입력 버텍스의 법선 사이의 코사인 값과 빛의 색상과 감쇠 값을 사용하여 디퓨즈 색상을 계산한다.
    5. 마지막으로, 스포트 라이트를 렌더링하기 전에 상수 버퍼를 설정하고 셰이더를 바인딩한다. 또한, 입력 레이아웃 및 버텍스 및 인덱스 버퍼를 설정한다.

    예시코드

    더보기
    cbuffer ObjectTransforms : register(b0)
    {
        matrix WorldViewProjection;
        matrix World;
        matrix InverseTransposeWorld;
    };
    
    cbuffer SpotLightBuffer : register(b1)
    {
        float3 LightPos;
        float3 LightDir;
        float3 LightColor;
        float4 SpotParams; // x = cos(outerConeAngle), y = cos(innerConeAngle), z = falloff, w = unused
    };
    
    struct VS_INPUT
    {
        float3 Position : POSITION;
        float3 Normal : NORMAL;
    };
    
    struct VS_OUTPUT
    {
        float4 Position : SV_POSITION;
        float3 LightSpacePosition : TEXCOORD0;
        float3 Normal : TEXCOORD1;
    };
    
    VS_OUTPUT VSMain(VS_INPUT input)
    {
        VS_OUTPUT output;
        
        output.Position = mul(float4(input.Position, 1.0f), WorldViewProjection);
        output.LightSpacePosition = mul(float4(input.Position, 1.0f), World);
        output.Normal = mul(input.Normal, InverseTransposeWorld);
        
        return output;
    }
    
    float4 PSMain(VS_OUTPUT input) : SV_TARGET
    {
        float3 lightDir = normalize(LightPos - input.LightSpacePosition);
        float spotFactor = dot(-lightDir, LightDir);
        float falloff = saturate((spotFactor - SpotParams.x) / (SpotParams.y - SpotParams.x));
        falloff = pow(falloff, SpotParams.z);
        float3 diffuseColor = LightColor * falloff * max(0, dot(input.Normal, lightDir));
        
        return float4(diffuseColor, 1.0f);
    }

     

     

    Shader
      Light.fx
    Lighting.fx
    PointLighting.fx
    SpotLighting.fx
    Framework
      Renders
      Lighting.h .cpp
    PerFrame.h .cpp
    Shader
      Lighting
      SpotLightingDemo.h .cpp

     


     

    Light.fx

     

    Light.fx

    더보기
    struct LightDesc
    {
        float4 Ambient;
        float4 Specular;
        float3 Direction;
        float Padding;
        float3 Position;
    };
    
    cbuffer CB_Light
    {
        LightDesc GlobalLight;
    };
    
    
    Texture2D DiffuseMap;
    Texture2D SpecularMap;
    Texture2D NormalMap;
    TextureCube SkyCubeMap;
    Texture2D ShadowMap;
    SamplerComparisonState ShadowSampler;
    
    struct MaterialDesc
    {
        float4 Ambient;
        float4 Diffuse;
        float4 Specular;
        float4 Emissive;
    };
    
    cbuffer CB_Material
    {
        MaterialDesc Material;
    };
    
    MaterialDesc MakeMaterial()
    {
        MaterialDesc output;
        output.Ambient = float4(0, 0, 0, 0);
        output.Diffuse = float4(0, 0, 0, 0);
        output.Specular = float4(0, 0, 0, 0);
        output.Emissive = float4(0, 0, 0, 0);
    
        return output;
    }
    
    float3 MaterialToColor(MaterialDesc result)
    {
        return (result.Ambient + result.Diffuse + result.Specular + result.Emissive).rgb;
    }
    
    void AddMaterial(inout MaterialDesc result, MaterialDesc val)
    {
        result.Ambient += val.Ambient;
        result.Diffuse += val.Diffuse;
        result.Specular += val.Specular;
        result.Emissive += val.Emissive;
    }
    
    ///////////////////////////////////////////////////////////////////////////////
    
    void Texture(inout float4 color, Texture2D t, float2 uv, SamplerState samp)
    {
        float4 sampling = t.Sample(samp, uv);
        
        color = color * sampling;
    }
    
    void Texture(inout float4 color, Texture2D t, float2 uv)
    {
        Texture(color, t, uv, LinearSampler);
    }
    
    ///////////////////////////////////////////////////////////////////////////////
    
    void ComputeLight(out MaterialDesc output, float3 normal, float3 wPosition)
    {
        output = MakeMaterial();
        
        float3 direction = -GlobalLight.Direction;
        float NdotL = dot(direction, normalize(normal));
        
        output.Ambient = GlobalLight.Ambient * Material.Ambient;
        float3 E = normalize(ViewPosition() - wPosition);
    
        [flatten]
        if(NdotL > 0.0f)
        {
            output.Diffuse = Material.Diffuse * NdotL;
            
            [flatten]
            if(Material.Specular.a > 0.0f)
            {
                float3 R = normalize(reflect(-direction, normal));
                float RdotE = saturate(dot(R, E));
                
                float specular = pow(RdotE, Material.Specular.a);
                output.Specular = Material.Specular * specular * GlobalLight.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));
            
            output.Emissive = Material.Emissive * emissive;
        }
    }
    
    ///////////////////////////////////////////////////////////////////////////////
    
    #define MAX_POINT_LIGHTS 256
    struct PointLight
    {
        float4 Ambient;
        float4 Diffuse;
        float4 Specular;
        float4 Emissive;
        
        float3 Position;
        float Range;
        
        float Intensity;
        float3 Padding;
    };
    
    cbuffer CB_PointLights
    {
        uint PointLightCount;
        float3 CB_PointLights_Padding;
        
        PointLight PointLights[MAX_POINT_LIGHTS];
    };
    
    void ComputePointLight(inout MaterialDesc output, float3 normal, float3 wPosition)
    {
        output = MakeMaterial();
        MaterialDesc result = MakeMaterial();
    
        for (uint i = 0; i < PointLightCount; i++)
        {
            float3 light = PointLights[i].Position - wPosition;
            float dist = length(light);
            
            
            [flatten]
            if(dist > PointLights[i].Range)
                continue;
            
            
            light /= dist; //Normalize
            
            result.Ambient = PointLights[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 * PointLights[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 * PointLights[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 * PointLights[i].Emissive;
            }
            
            float temp = 1.0f / saturate(dist / PointLights[i].Range);
            float att = temp * temp * (1.0f / max(1.0f - PointLights[i].Intensity, 1e-8f));
            
            output.Ambient += result.Ambient * temp;
            output.Diffuse += result.Diffuse * att;
            output.Specular += result.Specular * att;
            output.Emissive += result.Emissive * att;
        }
    }
    
    ///////////////////////////////////////////////////////////////////////////////
    
    #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;
    };
    
    cbuffer CB_SpotLights
    {
        uint SpotLightCount;
        float3 CB_SpotLights_Padding;
        
        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; //Normalize
            
            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;
            }
            
            
            float temp = pow(saturate(dot(-light, SpotLights[i].Direction)), SpotLights[i].Angle);
            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;
        }
    }
    
    ///////////////////////////////////////////////////////////////////////////////
    
    void NormalMapping(float2 uv, float3 normal, float3 tangent, SamplerState samp)
    {
        float4 map = NormalMap.Sample(samp, uv);
        
        [flatten]
        if (any(map.rgb) == false)
            return;
    
        
        float3 coord = map.rgb * 2.0f - 1.0f; //-1 ~ +1
        
        
        //탄젠트 공간
        float3 N = normalize(normal); //Z
        float3 T = normalize(tangent - dot(tangent, N) * N); //X
        //float3 T = tangent;
        //float3 T = float3(1, 0, 0);
        float3 B = cross(N, T); //Y
        
        float3x3 TBN = float3x3(T, B, N);
        
        coord = mul(coord, TBN);
        
        Material.Diffuse *= saturate(dot(-GlobalLight.Direction, coord));
    }
    
    void NormalMapping(float2 uv, float3 normal, float3 tangent)
    {
        NormalMapping(uv, normal, tangent, LinearSampler);
    }
    
    ///////////////////////////////////////////////////////////////////////////////
    
    float4 PS_AllLight(MeshOutput input)
    {
        NormalMapping(input.Uv, input.Normal, input.Tangent);
        
        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).rgb, 1.0f);
    }
    
    ///////////////////////////////////////////////////////////////////////////////
    
    Texture2D ProjectorMap;
    
    struct ProjectorDesc
    {
        matrix View;
        matrix Projection;
        
        float4 Color;
    };
    
    cbuffer CB_Projector
    {
        ProjectorDesc Projector;
    };
    
    void VS_Projector(inout float4 wvp, float4 oPosition)
    {
        wvp = WorldPosition(oPosition);
        wvp = mul(wvp, Projector.View);
        wvp = mul(wvp, Projector.Projection);
    }
    
    void PS_Projector(inout float4 color, float4 wvp)
    {
        float3 uvw = 0;
        
        uvw.x = wvp.x / wvp.w * 0.5f + 0.5f;
        uvw.y = -wvp.y / wvp.w * 0.5f + 0.5f;
        uvw.z = wvp.z / wvp.w;
    
        [flatten]
        if (saturate(uvw.x) == uvw.x && saturate(uvw.y) == uvw.y && saturate(uvw.z) == uvw.z)
        {
            float4 map = ProjectorMap.Sample(LinearSampler, uvw.xy);
            
            map.rgb *= Projector.Color.rgb;
            color = lerp(color, map, map.a);
        }
    }
    
    ///////////////////////////////////////////////////////////////////////////////
    
    cbuffer CB_Shadow
    {
        matrix ShadowView;
        matrix ShadowProjection;
        
        float2 ShadowMapSize;
        float ShadowBias;
        
        uint ShadowQuality;
    };

    구조체, cbuffer, 함수 추가

    • struct SpotLight
    • cbuffer CB_SpotLights { ... }
    • void ComputeSpotLight(inout MaterialDesc output, float3 normal, float3 wPosition)

     


     

    SpotLighting.fx

     

    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)
    }

    SpotLighting.fx 생성


     

    Light

     

    Light.h

    더보기
    #pragma once
    
    #define MAX_POINT_LIGHTS 256
    struct PointLight
    {
    	Color Ambient;
    	Color Diffuse;
    	Color Specular;
    	Color Emissive;
    
    	Vector3 Position;
    	float Range;
    
    	float Intensity;
    	Vector3 Padding;
    };
    
    #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:
    	//싱글톤 패턴
    	static Lighting* Get();
    	static void Create();
    	static void Delete();
    
    private:
    	Lighting();
    	~Lighting();
    
    public:
    	UINT PointLightCount() { return pointLightCount; }
    	UINT PointLights(OUT PointLight* lights); //PointLight 전체를 리턴 받을때 배열로 받아 개수 리턴
    	void AddPointLight(PointLight& light); //PointLight 추가
    	PointLight& GetPointLight(UINT index); //PointLight 하나씩 가져올때 사용
    
    public:
    	UINT SpotLightCount() { return spotLightCount; }
    	UINT SpotLights(OUT SpotLight* lights);
    	void AddSpotLight(SpotLight& light); 
    	SpotLight& GetSpotLight(UINT index);
    
    private:
    	static Lighting* instance; //싱글톤 패턴
    
    private:
    	UINT pointLightCount = 0;
    	PointLight pointLights[MAX_POINT_LIGHTS];
    
    private:
    	UINT spotLightCount = 0;
    	SpotLight spotLights[MAX_SPOT_LIGHTS];
    };

    구조체 추가

    • struct SpotLight

    함수 추가

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

     

     

    Light.cpp

    더보기
    #include "Framework.h"
    #include "Lighting.h"
    
    Lighting* Lighting::instance = NULL;
    
    Lighting * Lighting::Get()
    {
    	assert(instance != NULL);
    
    	return instance;
    }
    
    void Lighting::Create()
    {
    	assert(instance == NULL);
    
    	instance = new Lighting();
    }
    
    void Lighting::Delete()
    {
    	SafeDelete(instance);
    }
    
    Lighting::Lighting()
    {
    
    }
    
    Lighting::~Lighting()
    {
    
    }
    
    UINT Lighting::PointLights(OUT PointLight * lights)
    {
    	memcpy(lights, pointLights, sizeof(PointLight) * pointLightCount);
    
    	return pointLightCount;
    }
    
    void Lighting::AddPointLight(PointLight & light)
    {
    	pointLights[pointLightCount] = light;
    	pointLightCount++;
    }
    
    PointLight & Lighting::GetPointLight(UINT index)
    {
    	return pointLights[index];
    }
    
    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

     

    PerFrame.h

    더보기
    #pragma once
    
    class PerFrame
    {
    public:
    	PerFrame(Shader* shader);
    	~PerFrame();
    
    	void Update();
    	void Render();
    
    private:
    	struct Desc
    	{
    		Matrix View;
    		Matrix ViewInverse;
    		Matrix Projection;
    		Matrix VP;
    
    		Plane Culling[4];
    		Plane Clipping;
    
    		float Time;
    		float Padding[3];
    	} desc;
    
    	struct LightDesc
    	{
    		Color Ambient;
    		Color Specular;
    		Vector3 Direction;
    		float Padding;
    
    		Vector3 Position;
    		float Padding2;
    	} lightDesc;
    
    	struct PointLightDesc
    	{
    		UINT Count = 0;
    		float Padding[3];
    
    		PointLight Lights[MAX_POINT_LIGHTS];
    	} pointLightDesc;
    
    	struct SpotLightDesc
    	{
    		UINT Count = 0;
    		float Padding[3];
    
    		SpotLight Lights[MAX_SPOT_LIGHTS];
    	} spotLightDesc;
    
    private:
    	Shader* shader;
    
    	ConstantBuffer* buffer;
    	ID3DX11EffectConstantBuffer* sBuffer;
    
    	ConstantBuffer* lightBuffer;
    	ID3DX11EffectConstantBuffer* sLightBuffer;
    
    	ConstantBuffer* pointLightBuffer;
    	ID3DX11EffectConstantBuffer* sPointLightBuffer;
    
    	ConstantBuffer* spotLightBuffer;
    	ID3DX11EffectConstantBuffer* sSpotLightBuffer;
    };

    구조체 추가

    • struct SpotLightDesc

    변수 추가

    • ConstantBuffer* spotLightBuffer;
    • ID3DX11EffectConstantBuffer* sSpotLightBuffer;

     

     

    PerFrame.cpp

    더보기
    #include "Framework.h"
    #include "PerFrame.h"
    
    PerFrame::PerFrame(Shader * shader)
    	: shader(shader)
    {
    	buffer = new ConstantBuffer(&desc, sizeof(Desc));
    	sBuffer = shader->AsConstantBuffer("CB_PerFrame");
    
    	lightBuffer = new ConstantBuffer(&lightDesc, sizeof(LightDesc));
    	sLightBuffer = shader->AsConstantBuffer("CB_Light");
    
    	pointLightBuffer = new ConstantBuffer(&pointLightDesc, sizeof(PointLightDesc));
    	sPointLightBuffer = shader->AsConstantBuffer("CB_PointLights");
    
    	spotLightBuffer = new ConstantBuffer(&spotLightDesc, sizeof(SpotLightDesc));
    	sSpotLightBuffer = shader->AsConstantBuffer("CB_SpotLights");
    }
    
    PerFrame::~PerFrame()
    {
    	SafeDelete(buffer);
    	SafeDelete(lightBuffer);
    	SafeDelete(pointLightBuffer);
    	SafeDelete(spotLightBuffer);
    }
    
    void PerFrame::Update()
    {
    	desc.Time = Time::Get()->Running();
    
    	lightDesc.Ambient = Context::Get()->Ambient();
    	lightDesc.Specular = Context::Get()->Specular();
    	lightDesc.Direction = Context::Get()->Direction();
    	lightDesc.Position = Context::Get()->Position();
    
    	pointLightDesc.Count = Lighting::Get()->PointLights(pointLightDesc.Lights);
    	spotLightDesc.Count = Lighting::Get()->SpotLights(spotLightDesc.Lights);
    }
    
    void PerFrame::Render()
    {
    	desc.View = Context::Get()->View();
    	D3DXMatrixInverse(&desc.ViewInverse, NULL, &desc.View);
    
    	desc.Projection = Context::Get()->Projection();
    	desc.VP = desc.View * desc.Projection;
    
    	buffer->Render();
    	sBuffer->SetConstantBuffer(buffer->Buffer());
    
    	lightBuffer->Render();
    	sLightBuffer->SetConstantBuffer(lightBuffer->Buffer());
    
    	pointLightBuffer->Render();
    	sPointLightBuffer->SetConstantBuffer(pointLightBuffer->Buffer());
    
    	spotLightBuffer->Render();
    	sSpotLightBuffer->SetConstantBuffer(spotLightBuffer->Buffer());
    }

     

     


     

     

    실행화면