- Deferred Rendering : Lighting을 제외한 랜더링을 마친 후 필요한 데이터를 임시로 저장 후, 남은 랜더링(Lighting)을 진행
 - 이렇게 하는 이유는 렌더링 과정에서 생성된 임시데이터를 재사용이 가능하다(Depth, Normal, Color)
 - 그 임시 데이터를 어디쓰냐? 물어보면 사용할 곳은 다양함 예를들어 Normal을 이용해 새로운 데이터를 생성한다든지 만들기 나름이다.
 - 대표적인 예시는 Lighting을 모든 object에 적용하지 않아도 되기에 성능에 유리하다가 있겠지?
 
UI를 그릴때는 아래의 쉐이더를 쓴다.(빛연산을 무시하려고)
// [Texture Shader]
// g_tex_0 : Output Texture
// AlphaBlend : true
struct VS_TEX_IN
{
    float3 pos : POSITION;
    float2 uv : TEXCOORD;
};
struct VS_TEX_OUT
{
    float4 pos : SV_Position;
    float2 uv : TEXCOORD;
};
VS_TEX_OUT VS_Tex(VS_TEX_IN input)
{
    VS_TEX_OUT output = (VS_TEX_OUT)0;
    output.pos = mul(float4(input.pos, 1.f), g_matWVP);
    output.uv = input.uv;
    return output;
}
float4 PS_Tex(VS_TEX_OUT input) : SV_Target
{
    float4 color = float4(1.f, 1.f, 1.f, 1.f);
    if (g_tex_on_0)
        color = g_tex_0.Sample(g_sam_0, input.uv);
    return color;
}
라이팅 쉐이더
#ifndef _LIGHTING_FX_
#define _LIGHTING_FX_
#include "params.fx"
#include "utils.fx"
struct VS_IN
{
    float3 pos : POSITION;
    float2 uv : TEXCOORD;
};
struct VS_OUT
{
    float4 pos : SV_Position;
    float2 uv : TEXCOORD;
};
struct PS_OUT
{
    float4 diffuse : SV_Target0;
    float4 specular : SV_Target1;
};
// [Directional Light]
// g_int_0 : Light index
// g_tex_0 : Position RT
// g_tex_1 : Normal RT
// Mesh : Rectangle
VS_OUT VS_DirLight(VS_IN input)
{
    VS_OUT output = (VS_OUT)0;
    output.pos = float4(input.pos * 2.f, 1.f);
    output.uv = input.uv;
    return output;
}
PS_OUT PS_DirLight(VS_OUT input)
{
    PS_OUT output = (PS_OUT)0;
    // Position Texture에 데이터가 있는지 확인
    float3 viewPos = g_tex_0.Sample(g_sam_0, input.uv).xyz;
    if (viewPos.z <= 0.f)
        clip(-1);   // z가 0일시(카메라 뒤) : clip(-1) = return 0와 동일
    // g_tex_1 : Normal RT
    float3 viewNormal = g_tex_1.Sample(g_sam_0, input.uv).xyz;
    LightColor color = CalculateLightColor(g_int_0, viewNormal, viewPos);
    output.diffuse = color.diffuse + color.ambient;
    output.specular = color.specular;
    return output;
}
// [Point Light]
// g_int_0 : Light index
// g_tex_0 : Position RT
// g_tex_1 : Normal RT
// g_vec2_0 : RenderTarget Resolution
// Mesh : Sphere
VS_OUT VS_PointLight(VS_IN input)
{
    VS_OUT output = (VS_OUT)0;
    output.pos = mul(float4(input.pos, 1.f), g_matWVP);
    output.uv = input.uv;
    return output;
}
PS_OUT PS_PointLight(VS_OUT input)
{
    PS_OUT output = (PS_OUT)0;
    // input.pos = SV_Position = Screen 좌표
    float2 uv = float2(input.pos.x / g_vec2_0.x, input.pos.y / g_vec2_0.y);
    float3 viewPos = g_tex_0.Sample(g_sam_0, uv).xyz;
    if (viewPos.z <= 0.f)
        clip(-1);
    // 광원과 물체의 거리 구해서 광원밖에 있을시 무시
    int lightIndex = g_int_0;
    float3 viewLightPos = mul(float4(g_light[lightIndex].position.xyz, 1.f), g_matView).xyz;
    float distance = length(viewPos - viewLightPos);
    if (distance > g_light[lightIndex].range)
        clip(-1);
    float3 viewNormal = g_tex_1.Sample(g_sam_0, uv).xyz;
    LightColor color = CalculateLightColor(g_int_0, viewNormal, viewPos);
    output.diffuse = color.diffuse + color.ambient;
    output.specular = color.specular;
    return output;
}
// [Final]
// g_tex_0 : Diffuse Color Target
// g_tex_1 : Diffuse Light Target
// g_tex_2 : Specular Light Target
// Mesh : Rectangle
VS_OUT VS_Final(VS_IN input)
{
    VS_OUT output = (VS_OUT)0;
    output.pos = float4(input.pos * 2.f, 1.f);
    output.uv = input.uv;
    return output;
}
float4 PS_Final(VS_OUT input) : SV_Target
{
    float4 output = (float4)0;
    float4 lightPower = g_tex_1.Sample(g_sam_0, input.uv);
    if (lightPower.x == 0.f && lightPower.y == 0.f && lightPower.z == 0.f)
        clip(-1);
    float4 color = g_tex_0.Sample(g_sam_0, input.uv);
    float4 specular = g_tex_2.Sample(g_sam_0, input.uv);
    output = (color * lightPower) + specular;
    return output;
}
#endif
이게 동작하게 코드 수정
class Shader : public Object
{
public:
	Shader();
	virtual ~Shader();
    // 이제 init에서 Main의 어떤 쉐이더함수에 접근할지 지정
	void Init(const wstring& path, ShaderInfo info = ShaderInfo(), const string& vs = "VS_Main", const string& ps = "PS_Main");
	void Update();
    // ...
void Engine::CreateRenderTargetGroups()
{
	// ...
    // 라이팅 그룹 생성
	// Lighting Group
	{
		vector<RenderTarget> rtVec(RENDER_TARGET_LIGHTING_GROUP_MEMBER_COUNT);
		rtVec[0].target = GET_SINGLE(Resources)->CreateTexture(L"DiffuseLightTarget",
			DXGI_FORMAT_R8G8B8A8_UNORM, _window.width, _window.height,
			CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_DEFAULT),
			D3D12_HEAP_FLAG_NONE, D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET);
		rtVec[1].target = GET_SINGLE(Resources)->CreateTexture(L"SpecularLightTarget",
			DXGI_FORMAT_R8G8B8A8_UNORM, _window.width, _window.height,
			CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_DEFAULT),
			D3D12_HEAP_FLAG_NONE, D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET);
		_rtGroups[static_cast<uint8>(RENDER_TARGET_GROUP_TYPE::LIGHTING)] = make_shared<RenderTargetGroup>();
		_rtGroups[static_cast<uint8>(RENDER_TARGET_GROUP_TYPE::LIGHTING)]->Create(RENDER_TARGET_GROUP_TYPE::LIGHTING, rtVec, dsTexture);
	}
}