일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | ||
6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | 30 |
- 다이나믹 프로그래밍
- DFS
- Unreal Engine 5
- 자료구조
- 브루트포스
- 알고리즘
- 백트래킹
- 우선순위 큐
- Team Fortress 2
- 시뮬레이션
- 투 포인터
- ue5
- 다익스트라
- 트리
- 유니티
- 수학
- 그리디 알고리즘
- 그래프
- VR
- 문자열
- BFS
- XR Interaction Toolkit
- 구현
- 유니온 파인드
- 스택
- 정렬
- 재귀
- c++
- 백준
- 누적 합
- Today
- Total
1일1알
DirectX11 학습 - Light 본문

현실세계에서 물체가 우리의 눈에 보이는 원리는 간단하게 말하자면 광원에서 나온 빛이 물체에 닿은 후 반사되어 눈에 들어오는 것이다.
이런 빛을 가상의 공간에서 표현하려면 어떻게 해야할까?
광원이 하나가 아닐수도 있고, 광원에서는 온갖 방향으로 빛을 쏠것이고, 물체에 반사된 빛은 또 다른 물체에 반사되기도 하는 상황도 있기 때문에 이런 모든 상황에 대해 빛을 추적하여 실시간으로 계산하는 것은 매우 힘들 것이다.

그래픽스에서 선택한 방법은 최소한의 비용으로 그럴듯하게 보이는 조명 연산식을 이용하여 빛을 흉내내는 것이다.
이런 빛 연산들 중 하나인 고전적이고 간단한 방법인 Phong Reflection Model이 있다.
Phong Reflection Model은 표면에서 반사되는 빛을 세가지 요소로 나누고 이것들을 더한것이 최종 모습이 된다.
- Ambient : 현실 세계에서는 물체 앞을 광원과 직접적으로 닿지 않도록 가로막고 있더라도 빛이 다른 물체들에 반사되어 빛이 닿게되고 눈에 보이게 된다. 이것을 표현하기 위해 빛의 방향과 관계없이 모든 픽셀에 공통적으로 적용되는 연산이다.
- Diffuse : 물체의 표면에서 분산되어 반사되는 빛을 표현한 연산이다. 빛이 들어오는 방향과 픽셀의 노말 벡터를 이용하여 빛의 세기를 결정하는 연산이다. Normal Vector를 다룰때 사용한 것이 Diffuse Light이다.
- Specular : 물체의 표면에서 한 방향으로 반사되는 빛을 표현한 연산이다. 눈뽕 효과와 비슷하다고 생각하면 된다. 빛이 들어왔다가 나가는 방향과 픽셀에서의 카메라의 방향을 이용해 카메라 쪽으로 빛이 얼마나 들어오는지를 계산하는 연산이다. 결과값에 상수를 제곱하여 효과를 조절할 수 있다.
Ambient, Diffuse 연산을 수행한 값을 uv매핑을 통해 구한 픽셀의 값과 곱하고 더해준 뒤 specular의 값을 더해주면 위의 사진과 같이 그럴듯한 모습이 나오게 된다.
mat->GetMaterialDesc().ambient = Color(1.f, 1.f, 1.f, 1.f);
mat->GetMaterialDesc().diffuse = Color(1.f, 1.f, 1.f, 1.f);
mat->GetMaterialDesc().specular = Color(1.f, 1.f, 1.f, 1.f);
Vec3 _lightDir = Vec3(1.f, -1.f, 1.f);
_light->GetLight()->SetLightDirection(_lightDir);
_light->GetLight()->SetAmbient(Color(0.2f, 0.2f, 0.2f, 1.f));
_light->GetLight()->SetDiffuse(Color(0.7f, 0.7f, 0.7f, 1.f));
_light->GetLight()->SetSpecular(Color(0.5f, 0.5f, 0.5f, 1.f));
물체의 머테리얼에 3가지 요소들의 어떤 색을 받아들일지를 설정하고 light에는 3가지 요소들의 색상을 설정하여 쉐이더에 넘겨준다.
float4 PS(MeshOutput input) : SV_TARGET
{
float4 color = ComputeLight(input.normal, input.uv, input.worldPosition);
return color;
}
float4 ComputeLight(float3 normal, float2 uv, float3 worldPosition)
{
float4 ambientColor = 0;
float4 diffuseColor = 0;
float4 specularColor = 0;
// Ambient
{
float4 color = GlobalLight.ambient * Material.ambient;
ambientColor = DiffuseMap.Sample(LinearSampler, uv) * color;
}
// Diffuse
{
float4 color = DiffuseMap.Sample(LinearSampler, uv);
float value = saturate(dot(-GlobalLight.direction, normalize(normal)));
diffuseColor = color * value * GlobalLight.diffuse * Material.diffuse;
}
// Specular
{
//float3 R = reflect(GlobalLight.direction, normal);
float3 R = GlobalLight.direction - (2 * normal * dot(GlobalLight.direction, normal));
R = normalize(R);
float3 cameraPosition = CameraPosition();
float3 E = normalize(cameraPosition - worldPosition);
float value = saturate(dot(R, E)); // clamp(0~1)
float specular = pow(value, 10);
specularColor = GlobalLight.specular * Material.specular * specular;
}
return ambientColor + diffuseColor + specularColor;
}
버텍스 쉐이더는 바뀐게 없어서 생략하고 픽셀 쉐이더에서 빛 연산을 해준다.
Specular 연산에서는 제곱할때 상수 대신 color의 w에 값을 넣어서 해당 값을 주로 사용한다고 한다.
빛과 머테리얼의 Ambient, Diffuse, Specular 속성을 각각 곱해 해당 픽셀에 얼마나 영향을 줄지 결정하고 각 light의 연산값에 해당 값을 곱해준다.
각 빛의 연산에서 나온 결과들을 더하면 그것이 최종적으로 해당 픽셀의 색상이 된다.

Phong Reflection Model을 적용한 모습이다.
// cpp
{
mat->GetMaterialDesc().emissive = Color(1.f, 0.f, 0.f, 1.f);
_light->GetLight()->SetEmissive(Color(1.f, 0.f, 0.f, 1.f));
}
// Shader
float4 ComputeLight(float3 normal, float2 uv, float3 worldPosition)
{
float4 ambientColor = 0;
float4 diffuseColor = 0;
float4 specularColor = 0;
float4 emissiveColor = 0;
// Ambient ...
// Diffuse ...
// Specular ...
// Emissive
{
float3 cameraPosition = CameraPosition();
float3 E = normalize(cameraPosition - worldPosition);
float value = saturate(dot(E, normal));
float emissive = 1.0f - value;
// min, max, x
emissive = smoothstep(0.0f, 1.0f, emissive);
emissive = pow(emissive, 2);
emissiveColor = GlobalLight.emissive * Material.emissive * emissive;
}
return ambientColor + diffuseColor + specularColor + emissiveColor;
}
이것과는 별개로 Emissive Light라는 빛이 있다. 이것은 주로 물체의 외곽선을 강조할때 사용한다.
Emissive 값을 구하는 방법은 간단하다. 해당 픽셀의 노말 벡터와 해당 픽셀에서 카메라 방향의 각도를 구해서 내적한 뒤 1에서 빼주고 효과를 조절하기 위해 제곱해준다.
이러면 각도가 클수록 값이 커지게 된다. 카메라에서 안보이는 각도일수록 값이 커지게 되는 것이다. 그래서 외곽선 부분의 값이 커지게 되고, 이것을 최종 색상에 더하면 외곽선이 강조되는 효과를 얻을 수 있다.

'DirectX11' 카테고리의 다른 글
DirectX11 학습 - StaticMesh (0) | 2024.03.31 |
---|---|
DirectX11 학습 - Normal Mapping (0) | 2024.03.30 |
DirectX11 학습 - Normal Vector (0) | 2024.03.28 |
DirectX11 학습 - 텍스쳐 입히기, 카메라 (0) | 2024.03.28 |
DirectX11 학습 - 삼각형 띄우기 (0) | 2024.03.26 |