본문 바로가기
DirectX

[Directx 11] 스터디 5일차 조명

by Minok_nok 2020. 2. 1.

Direct x 11 스터디 5주 차
조명

 

참고 블로그

https://copynull.tistory.com/253?category=649932category=649932

 

[DirectX11] Tutorial 6 - 조명

Tutorial 6 - 조명 원문 : http://www.rastertek.com/dx11tut06.html 이 튜토리얼에서는 조명(Diffuse lighting)과 DirectX 11을 활용하여 3D 객체를 그리는 방법을 다룹니다. 코드는 이전 튜토리얼의 소스에서 시..

copynull.tistory.com

이 블로그를 보며 작성한 글입니다.

이 게시글을 기준으로 보는 걸 추천드립니다.

 

개요

 

저번엔 모델링에게 셰이더를 이용하여 텍스쳐를 붙여줬다면, 이번에는 조금 더 자연스럽게 조명을 넣어봅시다.

조명의 종류와 적용원리를 파악하여 적용해봅시다.

 

프레임워크 업데이트

 

텍스쳐 셰이더의 컴파일과, 렌더의 역할을 맡던 TextureShaderClass가 없어지고 조명의 역할로 대체된 LightShaderClass와 LightClass를 새로 생성합니다.

 

이쯤되면 LightShaderClass도 똑같이 조명관련 셰이더를 컴파일 하고, 물체를 그에 맞게 렌더링하는것을 알 수 있습니다.

 

그 뒤 LightClass는 ModelClass에 캡슐화 되어있지 않을걸 보면, Model이 가지고 있는 정보가 아닌 CameraClass와 같이 조명에 관한 계산을 매 프레임마다 하며 전해주는 역할을 가지고 있는걸 추측할 수 있습니다.



Light.vs

/////////////
// GLOBALS //
/////////////
cbuffer MatrixBuffer
{
  matrix worldMatrix;
  matrix viewMatrix;
  matrix projectionMatrix;
};


//////////////
// TYPEDEFS //
//////////////
struct VertexInputType
{
    float4 position : POSITION;
    float2 tex : TEXCOORD0;
	float3 normal : NORMAL;
};

struct PixelInputType
{
    float4 position : SV_POSITION;
    float2 tex : TEXCOORD0;
	float3 normal : NORMAL;
};


////////////////////////////////////////////////////////////////////////////////
// Vertex Shader
////////////////////////////////////////////////////////////////////////////////
PixelInputType LightVertexShader(VertexInputType input)
{
    PixelInputType output;
    

// 적절한 행렬 계산을 위해 위치 벡터를 4 단위로 변경합니다.
    input.position.w = 1.0f;

// 월드, 뷰 및 투영 행렬에 대한 정점의 위치를 ​​계산합니다.
    output.position = mul(input.position, worldMatrix);
    output.position = mul(output.position, viewMatrix);
    output.position = mul(output.position, projectionMatrix);
    
  // 픽셀 쉐이더의 텍스처 좌표를 저장한다.
	output.tex = input.tex;
    
// 월드 행렬에 대해서만 법선 벡터를 계산합니다.
    output.normal = mul(input.normal, (float3x3)worldMatrix);

    // 법선 벡터를 정규화합니다.
    output.normal = normalize(output.normal);

    return output;
}


새로운 코드들이 추가됐습니다.

어떤게 추가가 됐을지 한번 알아봅시다.

 

 Light.vs: Normal정보 추가

 

//////////////
// TYPEDEFS //
//////////////
struct VertexInputType
{
    float4 position : POSITION;
    float2 tex : TEXCOORD0;
	float3 normal : NORMAL;
};

struct PixelInputType
{
    float4 position : SV_POSITION;
    float2 tex : TEXCOORD0;
	float3 normal : NORMAL;
};


VertexInputType과 PixelInputType에 normal이라는 속성이 부가가 되었습니다.



버텍스에서 normal은 정점이 향하는 벡터를 나타냅니다.

버텍스 버퍼를 world좌표로 변환한 뒤 일정한 파이프라인을 거쳐 면의 normal벡터를 만들어냅니다.

 

http://telnet.or.kr/directx/graphics/programmingguide/gettingstarted/3dcoordinatesystems/facevertexnormalvectors.htm

 

면법선 벡터와 정점 법선 벡터

면법선 벡터와 정점 법선 벡터 메쉬의 각면에는, 수직의 법선 벡터가 있다. 법선 벡터의 방향은, 정점이 정의되고 있는 순서와 좌표계가 왼손 좌표계나 오른손 좌표계 등에 의해서 정해진다. 면의 법선 벡터는 면의 앞면으로부터 멀어질 방향으로 향한다. Microsoft®Direct3D®에서는 앞쪽 면만 보인다. 앞면이란 각 정점이 시계방향 순서로 정의된 면이다. 앞면 이외의 면은 뒷면이다. Direct3D 에서는, 뒷면이 항상 렌더링 되는 것은 아니다. 이것을

telnet.or.kr

 

이를 이용하여 조명계산을 하는데, 이를 사용하는 방법은 추후에 알아봅시다.

 

Light.vs: normal벡터 변환

    // 월드 행렬에 대해서만 법선 벡터를 계산합니다.
    output.normal = mul(input.normal, (float3x3)worldMatrix);

    // 법선 벡터를 정규화합니다.
    output.normal = normalize(output.normal);

local공간에 있는 normal벡터를 worldMatrix를 곱하여 world공간으로 변환시킨 후에, 길이가 늘어났을 수도 있으니 normalize를 통해 정규화를 시켜줍니다.

 

정규화를 통해 방향값만 받아올수 있도록 변환시켜 계산에 차질이 없도록 합니다.

Light.ps

 

/////////////
// GLOBALS //
/////////////
Texture2D shaderTexture;
SamplerState SampleType;

cbuffer LightBuffer
{
    float4 diffuseColor;
    float3 lightDirection;
	float padding;
};


//////////////
// TYPEDEFS //
//////////////
struct PixelInputType
{
    float4 position : SV_POSITION;
    float2 tex : TEXCOORD0;
	float3 normal : NORMAL;
};


////////////////////////////////////////////////////////////////////////////////
// Pixel Shader
////////////////////////////////////////////////////////////////////////////////
float4 LightPixelShader(PixelInputType input) : SV_TARGET
{
  float4 textureColor;
  float3 lightDir;
  float lightIntensity;
  float4 color;


  // 이 텍스처 좌표 위치에서 샘플러를 사용하여 텍스처에서 픽셀 색상을 샘플링합니다.
  textureColor = shaderTexture.Sample(SampleType, input.tex);

  // 계산을 위해 빛 방향을 반전시킵니다.
    lightDir = -lightDirection;

    // 이 픽셀의 빛의 양을 계산합니다.
    lightIntensity = saturate(dot(input.normal, lightDir));

    // 빛의 강도와 결합 된 확산 색을 기준으로 최종 색상의 최종 색상을 결정합니다.
    color = saturate(diffuseColor * lightIntensity);

    // 텍스처 픽셀과 최종 확산 색을 곱하여 최종 픽셀 색상 결과를 얻습니다.
    color = color * textureColor;

    return color;
}

 

픽셀 셰이더 단계입니다.

 

새로운 요소들이 추가가 되었습니다.

하나하나씩 한번 알아봅시다.

 

Light.ps: 새로운 상수버퍼

cbuffer LightBuffer
{
    float4 diffuseColor;
    float3 lightDirection;
	float padding;
};


새로운 상수버퍼인 LightBuffer이 추가되었습니다.

빛의 색상값, 빛의 방향값이 들어갑니다.

padding은 이번에는 쓰이지 않으니 나중에 설명하도록 하겠습니다.

 

Light.ps: DiffuseLight

 

이번에 구현할 빛은 DiffuseLight입니다.

이는 난반사광, 확산광, 산란광 등 여러 이름으로 불리웁니다.

 

적당히 Diffuse Light로 불리는게 의사소통이 편합니다.

 

http://learnwebgl.brown37.net/09_lights/lights_diffuse.html

 

9.2 - Diffuse Lighting — LearnWebGL

9.2 - Diffuse Lighting This lesson discusses how to implement diffuse reflection of light. A Simple Diffuse Lighting Model Diffuse light reflection. Light that directly strikes an object and then reflects in all directions is called “diffuse” light. The am

learnwebgl.brown37.net

 

DiffuseLight는 위 그림과 같이 빛의 방향이 면과 수직일수록 면이 더욱 밝아지게 하는 Light연산입니다.

 

람베르트 모델을 이용한 DiffuseLight계산

 

DiffuseLight를 구하는 방법은 여러가지가 있는데 대표적으로 람베르트 모델을 이용한 계산방법입니다.

 

이의 계산 방식은 pixelShader단계 전에서 만들어진 면이 가지고 있는 Normal값과 빛의 방향값의 반대를 dot연산을 통해 구할수 있습니다.

 

dot연산은 두 벡터 a와 b가 있을 때 그 사이의 크기를 θ라고 했을때 

|a||b|cosθ값이 나타납니다.

 

dot연산은 두 벡터 사이의 내적값을 구합니다.

내적값은 두 벡터의 곱 연산이라고 볼 수 있는데, a와b는 서로 향하는 방향이 다르기 때문에 정상적으로 곱할 수 없습니다.

 

그렇기 때문에 |a|cosθ값이 b벡터를 기준으로 하는 a의 길이가 되며, 그 상태로 b벡터의 스칼라값을 곱하면 a와 b의 실질적인 스칼라 값의 곱이 나옵니다.

 

그런데 DiffuseLight를 계산할 때 dot연산이 왜쓰이냐면, |면의 노말벡터||빛의 벡터의 반대|cosθ 연산을 할때 면의 노말벡터와 빛의 노말벡터가 각각 길이가 1이라면 두 사이의 각을 유추할수 있는 cosθ값이 도출이 됩니다.

 

그럼 각각 길이가 1인 노말벡터와 빛의 반대 방향의 벡터를 내적할때 두 벡터가 같은 각이면 cosθ는 1이 되고 직각이 되거나 둔각이 되어 빛을 받고 있지 않은 상태가 된다면 0 또는 음의 실수까지 내려갑니다.

 

이 그림을 보면서 다시 생각해봅시다.

N이 법선 L이 입사광이라고 할때 L의 방향을 반대로 바꾼 뒤 Dot계산을 하면 어떤일이 일어날지 생각해봅시다.

 

이런 계산의 원리를 이해했다면 코드를 보면서 이해할 수 있을것입니다.

 

Light.ps: 셰이더 코드

 

float4 LightPixelShader(PixelInputType input) : SV_TARGET
{
  float4 textureColor;
  float3 lightDir;
  float lightIntensity;
  float4 color;

  // 이 텍스처 좌표 위치에서 샘플러를 사용하여 텍스처에서 픽셀 색상을 샘플링합니다.
  textureColor = shaderTexture.Sample(SampleType, input.tex);

  // 계산을 위해 빛 방향을 반전시킵니다.
    lightDir = -lightDirection;

    // 이 픽셀의 빛의 양을 계산합니다.
    lightIntensity = saturate(dot(input.normal, lightDir));

    // 빛의 강도와 결합 된 확산 색을 기준으로 최종 색상의 최종 색상을 결정합니다.
    color = saturate(diffuseColor * lightIntensity);

    // 텍스처 픽셀과 최종 확산 색을 곱하여 최종 픽셀 색상 결과를 얻습니다.
    color = color * textureColor;

    return color;
}


먼저 텍스쳐 입히기 단계까지는 생략한 뒤 설명하겠습니다.

 

// 계산을 위해 빛 방향을 반전시킵니다.
    lightDir = -lightDirection;

그 다음엔 Dot연산을 통해 cos값을 구할거기 때문에 빛의 방향벡터를 반대로 변환시킵니다.

 

    // 이 픽셀의 빛의 양을 계산합니다.
    lightIntensity = saturate(dot(input.normal, lightDir));

 

그 다음 면의 법선벡터와 빛의 반대 벡터를 내적 시켜줍니다.

 

이때 input.normal과 lightDir은 길이가 1입니다.

 

 

dot연산을 한 후 생성된 cos값을 saturate함수를 통해 0~1을 벗어났다면 0~1사이의 값이 되도록 지정합니다.

예를 들면 사이의 각이 둔각이 되어 cosθ값이 음수가 되었다면 0으로 지정합니다.

 

이는 제일 어두울때를 0으로, 제일 밝을때를 1로 지정하기 위한 연산입니다.

    // 빛의 강도와 결합 된 확산 색을 기준으로 최종 색상의 최종 색상을 결정합니다.
    color = saturate(diffuseColor * lightIntensity);



이번에는 전체적인 빛의 색상을 설정합니다.

기본적인 diffuseColor에 빛의 세기를 곱하면 텍스쳐의 색상에 간섭을 할 color변수가 완성됩니다.

    // 텍스처 픽셀과 최종 확산 색을 곱하여 최종 픽셀 색상 결과를 얻습니다.
    color = color * textureColor;

그 뒤 텍스쳐의 온전한 색상값에 diffuse값을 곱한다면, 빛과 상호작용하는 텍스쳐의 코드가 완성됩니다.

 

버텍스 셰이더에서도 Diffuse 연산을 할 수 있을까?

 

버텍스 셰이더에서도 Diffuse 연산을 할 수 있습니다.

 

버텍스의 normal값을 이용하여 Diffuse값을 구하고 그런 color값은 vectexShader단계와 pixelShader단계 사이에서 보간해 준뒤 pixelShader에선 매핑된 텍스쳐와 보간된 color값을 곱해주면 Diffuse연산이 돌아갑니다.

 

그럼 어느쪽이 더욱 효율적일까요?

 

사실 vertexShader에서 이런 연산을 하는게 더욱 효율적입니다.

왜냐하면, vectexShader은 정점마다 Diffuse의값을 정해준 뒤 다음 파이프라인에서 각각 픽셀마다 diffuse의 값을 보간해줍니다.

 

하지만 PixelShader는 물체를 나타내는 각 픽셀마다 Diffuse연산을 하기때문에 vertexShader보다 Diffuse연산을 더 많이 하는 상황이 나옵니다.

 

나중에 VertexShader에서 Diffuse연산을 해서 이득을 볼수 있도록 해보겠습니다.

 

https://kblog.popekim.com/2012/01/04-part-2.html

 

[포프의 쉐이더 입문강좌] 04. 기초적인 조명쉐이더 Part 2

게임 프로그래머 김포프의 블로그

kblog.popekim.com

 

"그럼 왜 이 예제에서는 PixelShader에서 Diffuse연산을 하나요?"

 

나중에 배울 법선매핑을 하기위해서 PixelShader에서 계산합니다.

법선매핑을 사용할때엔 PixelShader에서 계산하는게 이득이 더 많기 때문에 처음부터 PixelShader에서 연산을 합니다.

 

LightShaderClass.cpp

 

LightShaderClass는 전에 쓰였던 TextureShaderClass를 단순히 변환한 코드이기 때문에 변한 부분만 집중적으로 알아봅시다.

 

VectexBuffer

// 정점 입력 레이아웃 구조체를 설정합니다.
// 이 설정은 ModelClass와 셰이더의 VertexType 구조와 일치해야합니다.
D3D11_INPUT_ELEMENT_DESC polygonLayout[3];
polygonLayout[0].SemanticName = "POSITION";
polygonLayout[0].SemanticIndex = 0;
polygonLayout[0].Format = DXGI_FORMAT_R32G32B32_FLOAT;
polygonLayout[0].InputSlot = 0;
polygonLayout[0].AlignedByteOffset = 0;
polygonLayout[0].InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA;
polygonLayout[0].InstanceDataStepRate = 0;

polygonLayout[1].SemanticName = "TEXCOORD";
polygonLayout[1].SemanticIndex = 0;
polygonLayout[1].Format = DXGI_FORMAT_R32G32_FLOAT;
polygonLayout[1].InputSlot = 0;
polygonLayout[1].AlignedByteOffset = D3D11_APPEND_ALIGNED_ELEMENT;
polygonLayout[1].InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA;
polygonLayout[1].InstanceDataStepRate = 0;

polygonLayout[2].SemanticName = "NORMAL";
polygonLayout[2].SemanticIndex = 0;
polygonLayout[2].Format = DXGI_FORMAT_R32G32B32_FLOAT;
polygonLayout[2].InputSlot = 0;
polygonLayout[2].AlignedByteOffset = D3D11_APPEND_ALIGNED_ELEMENT;
polygonLayout[2].InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA;
polygonLayout[2].InstanceDataStepRate = 0;

VertexShader에 넘겨줄 새로운 시맨틱 Normal을 추가해주었습니다.

이는 버텍스가 가르키는 방향을 나타냅니다.

 

PixelShader에 넘겨줄 빛 정보 LightBuffer

// 픽셀 쉐이더에있는 광원 동적 상수 버퍼의 설명을 설정합니다.
// D3D11_BIND_CONSTANT_BUFFER를 사용하면 ByteWidth가 항상 16의 배수 여야하며 그렇지 않으면 CreateBuffer가 실패합니다.
D3D11_BUFFER_DESC lightBufferDesc;
lightBufferDesc.Usage = D3D11_USAGE_DYNAMIC;
lightBufferDesc.ByteWidth = sizeof(LightBufferType);
lightBufferDesc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
lightBufferDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
lightBufferDesc.MiscFlags = 0;
lightBufferDesc.StructureByteStride = 0;

// 이 클래스 내에서 정점 셰이더 상수 버퍼에 액세스 할 수 있도록 상수 버퍼 포인터를 만듭니다.
result = device->CreateBuffer(&lightBufferDesc, NULL, &m_lightBuffer);
if(FAILED(result))
{
	return false;
}

 


light.ps에서 있던 LightBuffer이름의 상수버퍼에게 값을 전달하기 위해 D3D버퍼를 하나 만들어줍니다.

 

struct LightBufferType
{
  XMFLOAT4 diffuseColor;
  XMFLOAT3 lightDirection;
  float padding;  // 구조체가 CreateBuffer 함수 요구 사항에 대해 16의 배수가되도록 여분의 패딩을 추가했습니다.
};

 

LightBuffer가 넘겨줄 정보입니다.

 

색상값과 빛의 방향, 그리고 LightBuffer가 16바이트의 배수여야 하기때문에 맞춰주는 float값을 하나 넣습니다.

 

그렇게 되면 float이 총 8개가 되기 때문에 16바이트의 배수가 맞춰집니다

 

 

그 뒤 SetShaderParameters함수에서 넘겨주는 과정입니다.

// light constant buffer를 잠글 수 있도록 기록한다.
if(FAILED(deviceContext->Map(m_lightBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedResource)))
{
	return false;
}

// 상수 버퍼의 데이터에 대한 포인터를 가져옵니다.
LightBufferType* dataPtr2 = (LightBufferType*)mappedResource.pData;

// 조명 변수를 상수 버퍼에 복사합니다.
dataPtr2->diffuseColor = diffuseColor;
dataPtr2->lightDirection = lightDirection;
dataPtr2->padding = 0.0f;

// 상수 버퍼의 잠금을 해제합니다.
deviceContext->Unmap(m_lightBuffer, 0);

// 픽셀 쉐이더에서 광원 상수 버퍼의 위치를 ??설정합니다.
bufferNumber = 0;

// 마지막으로 업데이트 된 값으로 픽셀 쉐이더에서 광원 상수 버퍼를 설정합니다.
deviceContext->PSSetConstantBuffers(bufferNumber, 1, &m_lightBuffer);


전에 VertexShader에 있던 행렬을 넘겨주는 방식과 같습니다.

 

버퍼를 잠그기->보낼 버퍼에 값 복사->버퍼 잠금 해제->PSSetConstantBuffers함수를 이용해 0번째 상수버퍼에 1개의 버퍼를 보내기

 

이렇게 하면 외부에서 받은 조명의 정보를 받을 수 있습니다.

 

ModelClass

 

ModelClass에서는 크게 바뀐게 없습니다.

struct VertexType
{
  XMFLOAT3 position;
  XMFLOAT2 texture;
  XMFLOAT3 normal;
};

 

ModelClass헤더에서 VertexType에 normal을 추가해 줍니다.

LightClass

#include "stdafx.h"
#include "LightClass.h"

LightClass::LightClass()
{
}
LightClass::LightClass(const LightClass& other)
{
}
LightClass::~LightClass()
{
}

void LightClass::SetDiffuseColor(float red, float green, float blue, float alpha)
{
	m_diffuseColor = XMFLOAT4(red, green, blue, alpha);
}


void LightClass::SetDirection(float x, float y, float z)
{
	m_direction = XMFLOAT3(x, y, z);
}


XMFLOAT4 LightClass::GetDiffuseColor()
{
	return m_diffuseColor;
}


XMFLOAT3 LightClass::GetDirection()
{
	return m_direction;
}


LightClass는 상당히 간결하게 이루어져 있습니다.

 

빛의 색상을 설정 및 반환하는 함수와, 빛의 방향을 설정 및 반환하는 함수로 구성되어 있습니다.

 

light 객체는 GraphicsClass에서 초기화를 할때 같이 생성합니다.

// m_Light 객체 생성
m_Light = new LightClass;
if(!m_Light)
{
	return false;
}

// m_Light 객체 초기화
m_Light->SetDiffuseColor(1.0f, 0.0f, 1.0f, 1.0f);
m_Light->SetDirection(0.0f, 0.0f, 1.0f);

 

이번 예제에서 Light는 보라색의 색상을 가지고 z방향으로 향하고 있습니다.

 

GraphicsClass.cpp

 

GraphicsClass에서 빛의 방향에 따라 물체의 색이 어떻게 변화가 되는지 확인하기 위해 Frame에 새로운 기능을 추가했습니다.

 

bool GraphicsClass::Frame()
{
  static float rotation = 0.0f;

  // 각 프레임의 rotation 변수를 업데이트합니다.
  rotation += (float)XM_PI * 0.005f;
  if(rotation > 360.0f)
  {
  rotation -= 360.0f;
  }

  // 그래픽 랜더링 처리
  return Render(rotation);
}

 

Frame이 호출될때마다 rotation값을 더해줍니다.

rotaion값이 오일러 각으로 360도를 넘었을때 -360으로 치환 하여 다시 반복되게끔 연산합니다.

 

그 다음은 렌더함수입니다.

 

bool GraphicsClass::Render(float rotation)
{
  // 씬을 그리기 위해 버퍼를 지웁니다
  m_Direct3D->BeginScene(0.0f, 0.0f, 0.0f, 1.0f);

  // 카메라의 위치에 따라 뷰 행렬을 생성합니다
  m_Camera->Render();

  // 카메라 및 d3d 객체에서 월드, 뷰 및 투영 행렬을 가져옵니다
  XMMATRIX worldMatrix, viewMatrix, projectionMatrix;
  m_Direct3D->GetWorldMatrix(worldMatrix);
  m_Camera->GetViewMatrix(viewMatrix);
  m_Direct3D->GetProjectionMatrix(projectionMatrix);

  // 삼각형이 회전 할 수 있도록 회전 값으로 월드 행렬을 회전합니다.
  worldMatrix *= XMMatrixRotationY(rotation);
  worldMatrix *= XMMatrixScaling(1.0f, 1.0f, 1.0f);

  // 모델 버텍스와 인덱스 버퍼를 그래픽 파이프 라인에 배치하여 드로잉을 준비합니다.
  m_Model->Render(m_Direct3D->GetDeviceContext());

  // Light 쉐이더를 사용하여 모델을 렌더링합니다.
  if (!m_LightShader->Render(m_Direct3D->GetDeviceContext(), m_Model->GetIndexCount(), worldMatrix, viewMatrix, projectionMatrix,
    m_Model->GetTexture(), m_Light->GetDirection(), m_Light->GetDiffuseColor()))
  {
  	return false;
  }

  // 버퍼의 내용을 화면에 출력합니다
  m_Direct3D->EndScene();

  return true;
}


Frame함수에서 받아온 rotation함수를 사용하여 worldMatrix를 회전합니다.

 

// 삼각형이 회전 할 수 있도록 회전 값으로 월드 행렬을 회전합니다.
worldMatrix *= XMMatrixRotationY(rotation);
worldMatrix *= XMMatrixScaling(1.0f, 1.0f, 1.0f);

Y축을 기준으로 돌아갈 수 있도록 XMMatrixRotationY함수를 사용하여 회전 행렬을 만듭니다.

그 후 월드행렬과 회전행렬을 곱하고, 월드 행렬이 회전의 역할만 맡도록 XMatrixScaling함수를 통해 크기의 변화가 없는 월드 행렬을 만들어줍니다.

// Light 쉐이더를 사용하여 모델을 렌더링합니다.
if (!m_LightShader->Render(m_Direct3D->GetDeviceContext(), m_Model->GetIndexCount(), worldMatrix, viewMatrix, projectionMatrix,
  m_Model->GetTexture(), m_Light->GetDirection(), m_Light->GetDiffuseColor()))
{
	return false;
}

 

그 뒤 LightShaderClass에게 Light의 정보까지 추가로 보내줍니다.

 

결과

 

 

핑크색 삼각형이 돌아가며, 정면을 보지 않을수록 점점 어두워지는 모델이 표현됩니다.


마무리

간단한 거라도 수학쓰는게 제일 재밌음

댓글