Diffuse는 분산광이라고 해서 일반적으로 우리가 생각하는 조명과 유사하다. 빛을 받는곳은 밝고 반대편은 어둡다.
어떤 오브젝트에 대해 표면에 수직인 방향의 값은 노멀맵이 이미 저장되어 있을 것이다.
분산광은 물체의 표면에서 분산되어 눈으로 들어오는 빛이라 각도에 따라 밝기가 달라진다는 특징이 있다.
즉, 노멀맵 데모에서 봤던것처럼 연산을 하면 되는데 이 연산식을 만든 사람이 람베르트라 람베르트 공식이라고한다.
노멀값과 평행인 방향으로 빛이 들어올 때 가장 세고 수직이면 거의 영향을 못받는다. (뒤에서오는것은 마이너스 값이나 아직 계산하지 않을것이다.)( 노멀은 표면에 수직인 벡터이다.)
이 람베르트 공식은 코사인 공식을 이용했었다. 노멀벡터와 조명벡터를 내적해서 구하는데 둘 다 정규화된 값이라 두벡터 사이의 각도만 cos한 값을 구하면 됐다. Cos은 cos0이 1로 0에 가까울수록 크고 cos90이 0으로 수직을 이룰수록 값이 작았다. 또 y축대칭 된다는 특징이 있었다. (그리고 조명벡터의 방향을 반대로해서 -L로 원점을 맞추기도 했다.)
https://white-mouse.tistory.com/135
09.Light_Ambient.fx를 10.Light_Diffuse.fx로 복붙해서 만들어서 전에 했던 내적 공식을 쉐이더에 적용할것이다
그리고 AmbientDemo클래스도 복붙해서 DiffuseDemo로 만들었다.

그리고 빛 방향을 나타낼 LightDir을 float4로 만들었다.
그리고 빛의 색상을 lightAmbient에서 LightDiffuse로, 물체가 받아들이는 빛의 정도를 MaterialAmbient에서 MaterialDiffuse로 만들었다. 빛의 색상을 모두 흰색에서 희미하게 0.5f로 바꾸고 lightDir은 우선 우측으로만 가게 설정한다음에 오브젝트들은 모든 빛을 받아들이도록 값을 줬다.
* 픽셀쉐이더에서 LightDiffuse를 계산에서 빼도 된다고 생각할수도 있지만 빛의 색상이 예를 들면 빨간색으로 필요할때가 있어 써야한다. 혹은 반대로 MaterialDiffuse를 다 1로하지 않고 빨간색 빛만 받아주겠다해서 1,0,0,0으로 세팅하면 LightAmbient값이 모두1로 하얀 빛이래도 보이는건 빨갛게 보이게 된다. 이로써 LightDiffuse는 빛 자체의 색이고 Material값은 빛에서 받아들일 색이라고 보면된다.
쉐이더에서도 Diffuse를 사용하도록 수정해보자.
// 10.Lighting_Diffuse.fx
#include "00. Global.fx"
float3 LightDir;
float4 LightDiffuse;
float4 MaterialDiffuse;
Texture2D DiffuseMap;
VertexOutput VS(VertexTextureNormal input)
{
VertexOutput output;
output.position = mul(input.position, W);
output.position = mul(output.position, VP);
output.uv = input.uv;
output.normal = mul(input.normal, (float3x3)W);
return output;
}
// Diffuse (분산광)
// 물체의 표면에서 분산되어 눈으로 바로 들어오는 빛
// 각도에 따라 밝기가 다르다 (Lambert 공식)
float4 PS(VertexOutput input) : SV_TARGET
{
float4 color = DiffuseMap.Sample(LinearSampler, input.uv);
float value = dot(-LightDir, normalize(input.normal));
color = color * value * LightDiffuse * MaterialDiffuse;
return color;
}
technique11 T0
{
PASS_VP(P0, VS, PS)
};
쉐이더에도 빛의 방향 LightDir값을 float4로 만들고 LightAmbient, MaterialAmbient를 LightDiffuse,MaterialDiffuse라고 이름을 마찬가지로 바꿨다. 또 그리고 지금까지 우리가 Texure0이라고 했던 Texture2D타입의 텍스쳐는 사실 DiffuseMap이었다.(맵핑에 쓰일 텍스쳐는 빛을 받은부분만 샘플링을 그 텍스쳐에 맞춰서 하니깐인가,,?) 이번기회에 이름을 바꿔서 보자.
쉐이더에서 LightDiffuse가 빛의 색상, MaterialDiffuse값이 색을 얼마나 받아들일지이고 여기에 텍스쳐에서 맵핑된 uv값(color)과 빛의 각도에 의해 람베르트공식으로 계산된 비율 값(value)을 곱한 값을 반환한다.
참고로 이렇게만 픽셀쉐이더를 정의하면 조명의 디퓨즈값만 볼 수 있으나 현실세계에서는 빛이 한쪽면에서 온다해도 벽에 반사되기도 해서 밝기차이는 있겠지만 결국 사물의 모든 면에 은은하게 빛이 비추긴한다. 이걸 나타내기 위해 Ambient값과 Diffuse값의 조합이 필요한데 후에 합연산을 통해서 Ambient, Diffuse, Specular, 값이 전체 1을 넘기지 않는 한에서 합해지도록 비율을 조절해 함께 사용할 것이다.
마지막으로 이 텍스쳐의 이름을 바꿧으니 MeshRenderer컴포넌트의 Update에서도 Texture0에서 DiffuseMap으로 이름을 바꿔준다.
// MeshRenderer.cpp
void MeshRenderer::Update()
{
if (_mesh == nullptr || _texture == nullptr || _shader == nullptr)
return;
//_shader->GetSRV("Texture0")->SetResource(_texture->GetComPtr().Get());
_shader->GetSRV("DiffuseMap")->SetResource(_texture->GetComPtr().Get());
auto world = GetTransform()->GetWorldMatrix();
RENDER->PushTransformData(TransformDesc{ world });
uint32 stride = _mesh->GetVertexBuffer()->GetStride();
uint32 offset = _mesh->GetVertexBuffer()->GetOffset();
DC->IASetVertexBuffers(0, 1, _mesh->GetVertexBuffer()->GetComPtr().GetAddressOf(), &stride, &offset);
DC->IASetIndexBuffer(_mesh->GetIndexBuffer()->GetComPtr().Get(), DXGI_FORMAT_R32_UINT, 0);
_shader->DrawIndexed(0, 0, _mesh->GetIndexBuffer()->GetCount(), 0, 0);
}
다시 위의 픽셀쉐이더를 더 보면 그냥 텍스쳐 샘플링한 color값을 반환해주면 조명이 따로 없이 자기 자신의 색상을 온전히 발생하게 된다. 그런데 빛의 방향에 -를 한 -LightDir값과 노멀맵에서 읽어온 값을 input.normal로 받아 (혹시모르니 정규화normalize를 했다.) 람베르트공식을 적용해서 내적한다음 이 값을 color값에 곱해주면 Diffuse 조명값이 적용된다.
내적한 값은 스칼라값이므로 float로 value를 받는다. 그리고 앞시간에 했던 Ambient 조명도 적용되도록 빛의 색깔(LightDiffuse), 머티리얼이 받을 빛의 색깔(MaterialDiffuse)도 같이 곱해준다.
픽셀 쉐이더에서 기본으로 input으로 들어온 uv값으로 텍스쳐에서 샘플링을 한 다음에 input으로 같이 들어온 노멀값으로 빛의 방향과 내적한 값을 곱해준다. 그러면 텍스처에 오브젝트의 빛과 각도값에 따른 밝기가 곱해져서 나온다.

그리고 이상태로 실행해보면 위와같은 화면이 나온다. 빛이 좌상단에서 오른쪽으로 0.5의 크기로 오기 때문에 구는 왼쪽이 밝고 큐브는 카메라를 바라보는 방향이 빛에 수직하기 때문에 보이지 않는다. 다만 왼쪽면을 보면

큐브의 카메라 바라보는 기준 왼쪽면 빛을 받아서 은은하게 다 보이고 있고 구에도 조명이 비추고있다.
큐브를 앞에서 봤을때도 현실세계에서 빛이 안비추는건 아닌데 아예 안보이는 것은 카메라에서 바라보는 방향의 큐브의 면이 노멀값이 다 하나의 같은 값이기 때문이다. 이는 나중에 노멀맵핑으로 해결가능하다.
이밖에도 DiffuseDemo에서 material값이나 lightDiffuse, lightDir값을 수정해서 여러가지 테스트가 가능하다.
'[DirectX11]' 카테고리의 다른 글
| Light #4 - Emissive (0) | 2024.09.09 |
|---|---|
| Light #3 - Specular (0) | 2024.09.02 |
| Light #1 - Ambient (0) | 2024.08.27 |
| 3D - Geometry, Sampling (0) | 2024.08.07 |
| 3D - 프로젝트 설정 (0) | 2024.08.03 |