이번에 공부한 내용은 디퓨즈 맵과 스페큘러 맵을 이용하여 물체 표면의 특성에 따라 반사량이 달라지는 스페큘러 맵핑입니다.
예를 들어 디퓨즈 스페큘러맵핑을 이용하면 얼굴을 렌더링 한다고 할때 이마나 코는 기름기 때문에 좀 더 반짝거리며 다른 부분은 덜 반짝 거리는 것을 표현할 수 있습니다.
이러한 정반사량과 난반사량을 부분에 따라서 조절하기 위해 스패큘러맵 텍스처와 디퓨즈맵 텍스처를 사용한다고 합니다. 즉 텍스처가 두개가 필요합니다.

구현에 필요한 텍스처 리소스는 아래 git레포지토리에서 구 할 수 있습니다.
https://github.com/popekim/ShaderPrimerKR/tree/master/05_DiffuseSpecularMapping
popekim/ShaderPrimerKR
Source Code for the Korean version of Shader Programming Primer Book. - popekim/ShaderPrimerKR
github.com
정점 쉐이더
정점쉐이더는 지난번 난반사, 정반사 쉐이더와 크게 다를게 없습니다.
float4x4 gWorldMatrix;
float4x4 gViewMatrix;
float4x4 gProjectionMatrix;
float4 gWorldLightPosition;
float4 gWorldCameraPosition;
struct VS_INPUT
{
float4 mPosition : POSITION;
float3 mNormal: NORMAL;
float2 mUV : TEXCOORD0; //텍스쳐 좌표
};
struct VS_OUTPUT
{
float4 mPosition : POSITION;
float3 mDiffuse : TEXCOORD1;
float3 mViewDir: TEXCOORD2;
float3 mReflection: TEXCOORD3;
float2 mUV : TEXCOORD0; // 텍스처 좌표
};
VS_OUTPUT vs_main( VS_INPUT Input )
{
VS_OUTPUT Output;
Output.mPosition = mul( Input.mPosition, gWorldMatrix );
float3 lightDir = Output.mPosition.xyz - gWorldLightPosition.xyz;
lightDir = normalize(lightDir);
float3 viewDir = normalize(Output.mPosition.xyz - gWorldCameraPosition.xyz);
Output.mViewDir = viewDir;
Output.mPosition = mul( Output.mPosition, gViewMatrix );
Output.mPosition = mul( Output.mPosition, gProjectionMatrix );
float3 worldNormal = mul( Input.mNormal, (float3x3)gWorldMatrix );
worldNormal = normalize(worldNormal);
Output.mDiffuse = dot(-lightDir, worldNormal);
Output.mReflection = reflect(lightDir, worldNormal);
Output.mUV = Input.mUV;
return Output;
}
픽셀 쉐이더
struct PS_INPUT
{
float2 mUV: TEXCOORD0;
float3 mDiffuse : TEXCOORD1;
float3 mViewDir: TEXCOORD2;
float3 mReflection: TEXCOORD3;
};
sampler2D DiffuseSampler; //디퓨즈 맵 텍스처 샘플러
sampler2D SpecularSampler; // 스페큘러 맵 텍스처 샘플러
float3 gLightColor; // 빛의 색깔
float4 ps_main(PS_INPUT Input) : COLOR
{
float4 albedo = tex2D(DiffuseSampler, Input.mUV); //디퓨즈맵 값
float3 diffuse = gLightColor * albedo.rgb * saturate(Input.mDiffuse);
//난반사광 = 빛의 색 * 디퓨즈맵 값 * 난반사량
float3 reflection = normalize(Input.mReflection);
float3 viewDir = normalize(Input.mViewDir);
float3 specular = 0;
if ( diffuse.x > 0 )
{
specular = saturate(dot(reflection, -viewDir ));
specular = pow(specular, 50.0f); // 정반사 량 계산
float4 specularIntensity = tex2D(SpecularSampler, Input.mUV); //스페큘러 맵 값 가져오기
specular*= specularIntensity.rgb * gLightColor; // 정반사 = 정반사량* 빛의 색* 스패큘러맵 값
}
float3 ambient = float3(0.1f, 0.1f, 0.1f) * albedo;
return float4(ambient + diffuse + specular, 1);
}
픽셀 쉐이더에서는 디퓨즈맵과 스페큘러맵을 사용합니다. 픽셀쉐이더에서 최종 난반사량과 정반사량은 다음과 같이 구해집니다.
난반사광 = 빛의 색상 * 난반사광의 양 * 디퓨즈맵의 값
정반사광 = 빛의 색상 * 정반사광의 양 * 스페큘러맵의 값
결과

벽돌 사이사이에 스페큘러맵의 영향을 받아 반사량이 감소한 것을 볼 수 있습니다.
DirectX 11 구현
DirectX11 에서 구현하여 출력한 결과입니다.

https://github.com/lucidmaj7/directx11test/tree/SpecularMappingShader/MyShaderTest
lucidmaj7/directx11test
directx11test. Contribute to lucidmaj7/directx11test development by creating an account on GitHub.
github.com
'ComputerGraphics > HLSL' 카테고리의 다른 글
쉐이더프로그래밍 / 툰(toon) 쉐이딩 (0) | 2020.06.24 |
---|---|
정반사 (Phong model) HLSL 구현 / 렌더몽키(RenderMonkey) (0) | 2020.06.01 |
난반사 HLSL (람베르트 반사) 쉐이더 / 렌더몽키(RenderMonkey) (0) | 2020.05.28 |
쉐이더프로그래밍 / 텍스처 맵핑 HLSL / 랜더몽키(RenderMonkey) (1) | 2020.05.25 |
쉐이더프로그래밍 / 빨강 쉐이더 만들기 (렌더몽키) (1) | 2020.05.16 |
댓글