DirectX 11에서 Lighting을 구현하려면, HLSL(고수준 셰이더 언어)을 사용하여 셰이더 코드를 작성해야 한다. HLSL은 Direct3D에서 셰이더 프로그래밍을 위해 사용되는 언어로, 셰이더 코드를 작성하고 컴파일하여 GPU에서 실행할 수 있는 바이너리 코드로 변환한다.

 

목차

     

     


     

     

    Lighting

     

    Lighting을 구현하는 방법은 다양하지만, 일반적으로는 Phong 또는 Blinn-Phong Lighting 모델을 사용한다. 이 모델은 ambient, diffuse, specular lighting을 사용하여 표면의 색상을 계산한다. 각각의 조명 구성 요소는 다음과 같이 계산된다.

     


    Ambient lighting

    float4 ambientColor = float4(0.2, 0.2, 0.2, 1.0); // ambient lighting의 색상값
    float4 ambientLight = ambientColor * materialColor; //ambient lighting과 material color를 곱해서 계산

     

    Diffuse lighting

    float3 lightDirection = normalize(float3(0.0, 1.0, 0.0)); // 조명의 방향 벡터
    float4 diffuseColor = float4(1.0, 1.0, 1.0, 1.0); // diffuse lighting의 색상값
    float4 diffuseLight = saturate(dot(surfaceNormal, lightDirection)) * diffuseColor * materialColor; //surface normal(표면 법선 벡터)와 조명의 방향 벡터의 내적, diffuse lighting과 material color를 곱해서 계산

     

    Specular lighting

    float3 eyeDirection = normalize(eyePosition - surfacePosition); // 눈의 방향 벡터
    float3 reflectDirection = reflect(-lightDirection, surfaceNormal); // 조명의 반사 방향 벡터
    float4 specularColor = float4(1.0, 1.0, 1.0, 1.0); // specular lighting의 색상값
    float4 specularLight = pow(saturate(dot(reflectDirection, eyeDirection)), materialShininess) * specularColor * materialColor; // 반사 방향 벡터와 눈의 방향 벡터의 내적, shininess(광택 정도)의 제곱, specular lighting과 material color를 곱해서 계산

     

    이러한 Lighting 요소들은 조명과 재질(물체의 속성)에 따라 다양하게 계산될 수 있다. 이러한 HLSL 코드는 셰이더에서 조명과 재질을 계산하는 데 사용된다.

     

     

    Shader
      Light.fx
    Lighting.fx
    UnitTest
      Lighting
      LightingDemo.h .cpp 생성

     


     

    빛 연산

     

    Diffuse Color x Texture x NdotL(Lambert)

     

    Specular Color x Texture x (Phong)

     

     

    Light(광원에서 쏘아진 빛), Reflection(반사), Eye(시야로 가는선) 이 있을때

    • 내적 dot(Reflection, Eye)를 구한 뒤
    • power(R dot E, 상수값) 를 구한다.
      • pow(0.5, 10)이라면 0.5^10
      • 상수값이 커질수록 원(=범위)은 커지지만 값을 흐려진다.
      • 상수값이 작을수록 원(=범위)은 작아지지만 빛이 강해진다.

     


     

    윤곽선 구하기

     

    윤곽선(=외각선)을 구하는 방법

    • 눈(Eye)의 방향
    • Normal의 방향
    • 두 개를 사용하여 구할 수 있다.

    바라보는 시점의 외각 = Normal 벡터와의 내적

     

     


     

    Global.fx

     

    Global.fx

    더보기
    float3 ViewPosition()
    {
        return ViewInverse._41_42_43; //카메라 변환되었던 값을 역행렬로 카메라 원래 위치를 구함
    }

    카메라의 원본 위치를 구한다.

     


     

    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++) //PointLight 개수만큼 for문을 돈다.
        {
            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;
    };

    ComputeLight 부분 - ComputePointLight, ComputeSpotLight도 같은 원리

    더보기
    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;
        }
    }

     

     

    Material를 Lighting.fx에 넘겨준다.

     


     

    Lighting

     

    Lighting.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;
        ComputeLight(output, input.Normal, input.wPosition);
        
        return float4(MaterialToColor(output), 1.0f);
    }
    
    technique11 T0
    {
        P_VP(P0, VS_Mesh, PS)
        P_VP(P1, VS_Model, PS)
        P_VP(P2, VS_Animation, PS)
    }

     


     

    LightingDemo

     

    LightingDemo.h

    더보기
    #pragma once
    #include "Systems/IExecute.h"
    
    class LightingDemo : public IExecute
    {
    public:
    	virtual void Initialize() override;
    	virtual void Ready() override {}
    	virtual void Destroy() override {}
    	virtual void Update() override;
    	virtual void PreRender() override {}
    	virtual void Render() override;
    	virtual void PostRender() override {}
    	virtual void ResizeScreen() override {}
    
    private:
    	void Mesh();
    	void Airplane();
    	void Kachujin();
    	void KachujinCollider();
    	void KachujinWeapon();
    
    	void Pass(UINT mesh, UINT model, UINT anim);
    
    private:
    	Shader* shader;
    
    	CubeSky* sky;
    
    	Material* floor;
    	Material* stone;
    	Material* brick;
    	Material* wall;
    
    	MeshRender* cube;
    	MeshRender* cylinder;
    	MeshRender* sphere;
    	MeshRender* grid;
    
    	ModelRender* airplane = NULL;
    
    
    	ModelAnimator* kachujin = NULL;
    	Transform* colliderInitTransforms;
    	ColliderObject** colliders;
    
    	ModelRender* weapon = NULL;
    	Transform* weaponInitTransform;
    
    	vector<MeshRender *> meshes;
    	vector<ModelRender *> models;
    	vector<ModelAnimator *> animators;
    };

     

     

    LightingDemo.cpp

    더보기
    #include "stdafx.h"
    #include "LightingDemo.h"
    
    void LightingDemo::Initialize()
    {
    	Context::Get()->GetCamera()->RotationDegree(20, 0, 0);
    	Context::Get()->GetCamera()->Position(1, 36, -85);
    
    
    	//Performance perfomence;
    	//perfomence.Start();
    	//{
    		shader = new Shader(L"75_Lighting.fxo");
    	//}
    	//float t = perfomence.End();
    	//MessageBox(D3D::GetDesc().Handle, to_wstring(t).c_str(), L"", MB_OK);
    
    
    	sky = new CubeSky(L"Environment/GrassCube1024.dds");
    	
    	Mesh();
    	Airplane();
    	
    	Kachujin();
    	KachujinCollider();
    	KachujinWeapon();
    }
    
    void LightingDemo::Update()
    {
    	ImGui::ColorEdit3("Ambient", Context::Get()->Ambient());
    	ImGui::ColorEdit3("Specular", Context::Get()->Specular());
    
    
    	sky->Update();
    
    	cube->Update();
    	grid->Update();
    	cylinder->Update();
    	sphere->Update();
    
    	airplane->Update();
    	kachujin->Update();
    
    	Matrix worlds[MAX_MODEL_TRANSFORMS];
    	for (UINT i = 0; i < kachujin->GetTransformCount(); i++)
    	{
    		kachujin->GetAttachTransform(i, worlds);
    		//colliders[i]->Collider->GetTransform()->World(worlds[40]);
    		//colliders[i]->Collider->Update();
    
    		weapon->GetTransform(i)->World(weaponInitTransform->World() * worlds[40]);
    	}
    
    	weapon->UpdateTransforms();
    	weapon->Update();
    }
    
    void LightingDemo::Render()
    {
    	sky->Render();
    
    	
    	Pass(0, 1, 2);
    
    	wall->Render();
    	sphere->Render();
    
    	brick->Render();
    	cylinder->Render();
    
    	stone->Render();
    	cube->Render();
    
    	floor->Render();
    	grid->Render();
    
    	airplane->Render();
    	
    	kachujin->Render();
    	weapon->Render();
    	//for (UINT i = 0; i < kachujin->GetTransformCount(); i++)
    		//colliders[i]->Collider->Render();
    }
    
    void LightingDemo::Mesh()
    {
    	//Create Material
    	{
    		floor = new Material(shader);
    		floor->DiffuseMap("Floor.png");
    		floor->Ambient(0.2f, 0.2f, 0.2f, 20);
    		floor->Specular(1, 1, 1, 20);
    		floor->SpecularMap("Floor_Specular.png");
    		//floor->NormalMap("Floor_Normal.png");
    		
    		stone = new Material(shader);
    		stone->DiffuseMap("Stones.png");
    		stone->Ambient(0.2f, 0.2f, 0.2f, 20);
    		stone->Specular(1, 1, 1, 20);
    		stone->SpecularMap("Stones_Specular.png");
    		stone->Emissive(0.15f, 0.15f, 0.15f, 0.3f);
    		//stone->NormalMap("Stones_Normal.png");
    
    		
    		brick = new Material(shader);
    		brick->DiffuseMap("Bricks.png");
    		brick->Ambient(0.2f, 0.2f, 0.2f, 20);
    		brick->Specular(1, 0.3f, 0.3f, 20);
    		brick->SpecularMap("Bricks_Specular.png");
    		brick->Emissive(0.15f, 0.15f, 0.15f, 0.3f);
    		//brick->NormalMap("Bricks_Normal.png");
    
    		wall = new Material(shader);
    		wall->DiffuseMap("Wall.png");
    		wall->Ambient(0.2f, 0.2f, 0.2f, 20);
    		wall->Specular(1, 1, 1, 20);
    		wall->SpecularMap("Wall_Specular.png");
    		wall->Emissive(0.15f, 0.15f, 0.15f, 0.3f);
    		//wall->NormalMap("Wall_Normal.png");
    	}
    
    	//Create Mesh
    	{
    		Transform* transform = NULL;
    
    		cube = new MeshRender(shader, new MeshCube());
    		transform = cube->AddTransform();
    		transform->Position(0, 5, 0);
    		transform->Scale(20, 10, 20);
    
    		grid = new MeshRender(shader, new MeshGrid(5, 5));
    		transform = grid->AddTransform();
    		transform->Position(0, 0, 0);
    		transform->Scale(12, 1, 12);
    
    		cylinder = new MeshRender(shader, new MeshCylinder(0.5f, 3.0f, 20, 20));
    		sphere = new MeshRender(shader, new MeshSphere(0.5f, 20, 20));
    		for (UINT i = 0; i < 5; i++)
    		{
    			transform = cylinder->AddTransform();
    			transform->Position(-30, 6, -15.0f + (float)i * 15.0f);
    			transform->Scale(5, 5, 5);
    
    			transform = cylinder->AddTransform();
    			transform->Position(30, 6, -15.0f + (float)i * 15.0f);
    			transform->Scale(5, 5, 5);
    
    
    			transform = sphere->AddTransform();
    			transform->Position(-30, 15.5f, -15.0f + (float)i * 15.0f);
    			transform->Scale(5, 5, 5);
    
    			transform = sphere->AddTransform();
    			transform->Position(30, 15.5f, -15.0f + (float)i * 15.0f);
    			transform->Scale(5, 5, 5);
    		}
    	}
    
    	sphere->UpdateTransforms();
    	cylinder->UpdateTransforms();
    	cube->UpdateTransforms();
    	grid->UpdateTransforms();
    
    	meshes.push_back(sphere);
    	meshes.push_back(cylinder);
    	meshes.push_back(cube);
    	meshes.push_back(grid);
    }
    
    void LightingDemo::Airplane()
    {
    	airplane = new ModelRender(shader);
    	airplane->ReadMesh(L"B787/Airplane");
    	airplane->ReadMaterial(L"B787/Airplane");
    
    	Transform* transform = airplane->AddTransform();
    	transform->Position(2.0f, 9.91f, 2.0f);
    	transform->Scale(0.004f, 0.004f, 0.004f);
    	airplane->UpdateTransforms();
    
    	models.push_back(airplane);
    }
    
    void LightingDemo::Kachujin()
    {
    	kachujin = new ModelAnimator(shader);
    	kachujin->ReadMesh(L"Kachujin/Mesh");
    	kachujin->ReadMaterial(L"Kachujin/Mesh");
    	kachujin->ReadClip(L"Kachujin/Sword And Shield Idle");
    	kachujin->ReadClip(L"Kachujin/Sword And Shield Walk");
    	kachujin->ReadClip(L"Kachujin/Sword And Shield Run");
    	kachujin->ReadClip(L"Kachujin/Sword And Shield Slash");
    	kachujin->ReadClip(L"Kachujin/Salsa Dancing");
    
    
    	Transform* transform = NULL;
    
    	transform = kachujin->AddTransform();
    	transform->Position(0, 0, -30);
    	transform->Scale(0.075f, 0.075f, 0.075f);
    	kachujin->PlayTweenMode(0, 0, 1.0f);
    
    	transform = kachujin->AddTransform();
    	transform->Position(-15, 0, -30);
    	transform->Scale(0.075f, 0.075f, 0.075f);
    	kachujin->PlayTweenMode(1, 1, 1.0f);
    
    	transform = kachujin->AddTransform();
    	transform->Position(-30, 0, -30);
    	transform->Scale(0.075f, 0.075f, 0.075f);
    	kachujin->PlayTweenMode(2, 2, 0.75f);
    
    	transform = kachujin->AddTransform();
    	transform->Position(15, 0, -30);
    	transform->Scale(0.075f, 0.075f, 0.075f);
    	kachujin->PlayBlendMode(3, 0, 1, 2);
    	kachujin->SetBlendAlpha(3, 1.5f);
    
    	transform = kachujin->AddTransform();
    	transform->Position(30, 0, -32.5f);
    	transform->Scale(0.075f, 0.075f, 0.075f);
    	kachujin->PlayTweenMode(4, 4, 0.75f);
    
    	kachujin->UpdateTransforms();
    
    	animators.push_back(kachujin);
    }
    
    void LightingDemo::KachujinCollider()
    {
    	UINT count = kachujin->GetTransformCount();
    	colliders = new  ColliderObject*[count];
    
    	colliderInitTransforms = new Transform();
    	colliderInitTransforms->Position(-2.9f, 1.45f, -50.0f);
    	colliderInitTransforms->Scale(5, 5, 75);
    
    	for (UINT i = 0; i < count; i++)
    	{
    		colliders[i] = new ColliderObject();
    
    		//colliders[i]->Init = new Transform();
    		//colliders[i]->Init->Position(0, 0, 0);
    		//colliders[i]->Init->Scale(10, 30, 10);
    
    		colliders[i]->Transform = new Transform();
    		//colliders[i]->Collider = new Collider(colliders[i]->Transform, colliders[i]->Init);
    		colliders[i]->Collider = new Collider(colliders[i]->Transform, colliderInitTransforms);
    	}
    }
    
    void LightingDemo::KachujinWeapon()
    {
    	weapon = new ModelRender(shader);
    	weapon->ReadMesh(L"Weapon/Sword");
    	weapon->ReadMaterial(L"Weapon/Sword");
    
    	UINT count = kachujin->GetTransformCount();
    	for (UINT i = 0; i < count; i++)
    		weapon->AddTransform();
    
    	weapon->UpdateTransforms();
    	models.push_back(weapon);
    
    	
    	weaponInitTransform = new Transform();
    	weaponInitTransform->Position(-2.9f, 1.45f, -6.45f);
    	weaponInitTransform->Scale(0.5f, 0.5f, 0.75f);
    	weaponInitTransform->Rotation(0, 0, 1);
    }
    
    void LightingDemo::Pass(UINT mesh, UINT model, UINT anim)
    {
    	for (MeshRender* temp : meshes)
    		temp->Pass(mesh);
    
    	for (ModelRender* temp : models)
    		temp->Pass(model);
    
    	for (ModelAnimator* temp : animators)
    		temp->Pass(anim);
    }

     


     

    실행화면

     

    Specular를 조정한 결과

     

     

    윤곽선 효과를 적용한 결과