프로젝트 : DirectX를 활용한 그래픽스 (13 - 조명 추가 (2) )
조명에 디테일을 더 넣기 위해 렌더러별로 Material을 보유하게 하였고 해당 Material을 상수버퍼로 전달하여 쉐이더에서 사용하도록 하였다.
struct Material
{
DirectX::SimpleMath::Vector3 Ambient = { 0.1f, 0.1f, 0.1f };
float Shininess = 50.0f;
DirectX::SimpleMath::Vector3 Diffuse = { 0.5f, 0.5f, 0.5f };
float Padding1;
DirectX::SimpleMath::Vector3 Specular = { 1.0f, 1.0f, 1.0f };
float Padding2;
};
Material구조체는 위와 같다.
오브젝트 별로 Ambient, Diffuse, Specular를 보유하고 있고, 이는 계산된 빛의 최종 색상에 영향을 주는 변수들이다.
상자의 왼쪽에는 빨간색 포인트라이트를 비추고, 위쪽엔 파란색 스팟라이트를 비추고, 정면엔 하얀색 디렉셔널 라이트를 비출 것이다.
{
float3 EyeDir = EyeWorld - _Input.WorldPos;
float3 LightStrength = Lights[0].Strength * CalDirectionalLight(Lights[0], _Input.WorldNormal);
LightSum += BlinnPhong(Lights[0], MaterialData, LightStrength, _Input.WorldNormal, EyeDir);
}
{
float3 EyeDir = EyeWorld - _Input.WorldPos;
float3 LightStrength = Lights[1].Strength * CalPointLight(Lights[1], _Input.WorldPos, _Input.WorldNormal);
LightSum += BlinnPhong(Lights[1], MaterialData, LightStrength, _Input.WorldNormal, EyeDir);
}
{
float3 EyeDir = EyeWorld - _Input.WorldPos;
float3 LightStrength = Lights[2].Strength * CalSpotLight(Lights[2], _Input.WorldPos, _Input.WorldNormal);
LightSum += BlinnPhong(Lights[2], MaterialData, LightStrength, _Input.WorldNormal, EyeDir);
}
픽셀 쉐이더에선 이렇게 3개의 빛을 계산하도록 하였다.
WorldLight.Lights[0].Direction = { 0.0f, 0.0f, 1.0f };
WorldLight.Lights[1].Position = { -2.0f, 0.0f, 1.0f };
WorldLight.Lights[1].Direction = { 0.0f, 1.0f, 0.0f };
WorldLight.Lights[1].Strength = { 1.0f, 0.0f, 0.0f };
WorldLight.Lights[1].FallOffStart = 1.0f;
WorldLight.Lights[1].FallOffEnd = 5.0f;
WorldLight.Lights[2].Position = { 0.0f, 2.0f, 1.0f };
WorldLight.Lights[2].Direction = { 0.0f, -1.0f, 0.0f };
WorldLight.Lights[2].Strength = { 0.0f, 1.0f, 0.0f };
WorldLight.Lights[2].FallOffStart = 1.0f;
WorldLight.Lights[2].FallOffEnd = 2.0f;
WorldLight.Lights[2].SpotPower = 10.0f;
C++ 코드에선 빛을 이렇게 세팅해주었다.
float3 BlinnPhong(Light _Light, Material _Mat, float3 _LStrength, float3 _Normal, float3 _EyeDir)
{
float3 LightStrength = _LStrength;
float3 Specular = _Mat.Specular * CalSpecular_BlinnPhong(_Light, _Mat, _Normal, _EyeDir);
float3 ReturnValue = _Mat.Ambient + (_Mat.Diffuse + Specular) * LightStrength;
return ReturnValue;
}
BlinnPhong함수 내부 코드이다.
외부에서 DiffuseLight를 구한 뒤 해당 값을 _LStrength로 보내주면 BlinnPhong에서 최종 빛을 결정하여 반환해준다.
float3 CalSpecular_BlinnPhong(Light _Light, Material _Mat, float3 _EyeDir, float3 _Normal)
{
float3 LightVec = normalize(-_Light.Direction);
float3 EyeDir = normalize(EyeDir);
float3 HalfWay = normalize(_EyeDir + LightVec);
float3 Normal = normalize(_Normal);
float HalfDotN = dot(HalfWay, Normal);
float3 Specular = max(HalfDotN, 0.0f);
Specular = pow(Specular, _Mat.Shininess);
Specular *= _Mat.Specular;
return Specular;
}
위 코드는 Specular을 계산해주는 함수이다. BlinnPhong 공식을 사용하였다.
EyeVector와 Light벡터의 중간벡터를 구한 뒤, 해당 벡터를 Normal과 내적하여 Specular를 구하였다.
이제 적용된 결과물을 확인해보자.
먼저 정면에 적용된 디렉셔널 라이트이다.
시야를 바꾸면 SPecular가 사라지는 것도 볼 수 있고, 우측이 정면보다 어두운 것도 확인할 수 있다.
(좌측의 빨간 빛은 포인트 라이트이다. 아래 영상에서 확인하자.)
다음은 좌측에 적용된 포인트라이트이다.
광원의 위치를 옮길수록 빛이 적용되는 위치가 변경되는 것을 확인할 수 있다.
다음은 스포트라이트이다.
SpotFactor를 키울수록 중심에 빛이 더 집중되고, 줄일수록 빛이 확산되는 것을 확인할 수 있다.
이로서 간단한 조명효과는 적용 끝이다.