Chapter 5 | 물체에 색을 입히는 diffuse/specular 매핑
◾DiffuseMap
- 표면이 빛을 흡수하는 성질을 셰이더에서 표현하기 (색 입히기)
- 텍스쳐 이미지를 픽셀 셰이더에서 읽어와 각 픽셀마다 색을 정해주면 된다.
- 전체적인 빛을 결정하는 난반사광의 결과에 텍스쳐를 적용한다. 이렇게 난반사광에 적용하는 텍스쳐를 DiffuseMap이라고 한다.
◾SpecularMap
- 난반사광이 반사하는 빛과 정반사광이 반사하는 빛의 스펙트럼이 다른 경우
- 각 픽셀이 반사하는 정반사광의 정도를 조절하는 경우 (이마, 코에 듬성듬성 보이는 정반사광 효과 등)
❗ 따라서
- 난반사광 = 빛의 색상 X 난반사광의 양 X 디퓨즈맵의 값
- 정반사광 = 빛의 색상 X 정반사광의 양 X 스페큘러맵의 값

- 스페큘러맵 틈새의 검정색은 정반사광을 전혀 반사하지 않음을 나타낸다. 스페큘러맵은 색상정보가 아닌 각 픽셀이 반사하는 정반사광의 양이다. (픽셀 수준에서 제어하고 싶은 경우 사용한다.)
📝 기초설정
- 이전 프로젝트의 사본을 만든 후 셰이더의 이름을
SpecularMapping으로 수정한다.
- 텍스쳐맵으로 사용할 이미지를 추가하고,
DiffuseMap으로 이름을 수정한다.
- Pass0에 DiffuseMap을 추가하고
DiffuseSampler로 이름을 수정하여 텍스처 개채를 생성한다.
- 부록으로 제공하는 Samples폴더에서 스페큘러맵(Fieldstone_SM.tga)을 렌더몽키에 드래그하여 가져온 뒤
SpecularMap로 이름을 수정한다. 텍스처 개체도 위와 동일한 방법으로 SpecularSampler로 생성한다.

- 빛의 색을 추가하기 위해
gLightColor를 생성하고 더블클릭하여 변수값(0.7, 0.7, 1.0)을 대입한다.
- 정점데이터에서 UV좌표를 읽어오기 위해
StreamMapping을 더블클릭하여 TEXCOORD(float2)를 추가한다.
📝 정점셰이더
- 텍스처매핑을 위한 UV좌표 변수를 선언하고 정점버퍼에서 UV좌표를 가져온다. (픽셀셰이더로 전달될 것)
. . .
struct VS_INPUT{
float4 mPosition : POSITION;
float3 mNormal : NORMAL;
float2 mUV: TEXCOORD0;
};
struct VS_OUTPUT{
float4 mPosition : POSITION;
float2 mUV: TEXCOORD0;
float3 mDiffuse : TEXCOORD1;
float3 mViewDir : TEXCOORD2;
float3 mReflection : TEXCOORD3;
};
VS_OUTPUT vs_main(VS_INPUT Input){
. . .
Output.mUV = Input.nUV;
return Output;
}
📝 픽셀셰이더
struct PS_INPUT{
float2 mUV : TECOORD0;
. . .
};
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 specular = 0;
if(diffuse.x > 0){
specular = saturate(dot(reflection, -viewDir));
specular = pow(specular, 20.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);
}

Chapter 6 | 만화 같은 명암을 입히는 툰셰이더
🆕 HLSL
ceil - 무조건 올림 함수
tex2D() - 텍스처 샘플링에 사용하는 HLSL 함수
swizzle - 벡터 성분의 순서를 마움대로 뒤섞을 수 있는 방법
🆕 수학
- 여러 개의 행렬을 미리 곱해 놓은 뒤, 그 결과를 정점 변환에 사용해도 결과는 동일하고 속도는 더 빠르다.
- 반대방향으로 공간변환을 할 때 역행렬을 사용한다.
◾ Toon Shading
- 명암을 단계적으로 끊어서 만화같은 명암을 나타낸다.
- 난반사광의 값을 무조건 0.2단위로 올림을 하여 단계를 나타낼 것이다.


📝 기초설정
ToonShader이름을 가지는 DirectX 이펙트를 추가한다. (matViewProjection 행렬은 삭제한다.)
- Model은 Teapot 모델로 바꿔준다.

- 난반사광 계산을 위해 빛의 위치와 정점의 법선정보가 필요하다.
- 빛의 위치 변수는 ToonShader에 float4 변수
gWorldLightPosition를 추가한 뒤 (500, 500, -500)으로 설정
- 법선정보는 StreamMapping을 더블클릭하여 Normal필드 추가 (FLOAT3, Index: 0)
- 주전자의 전체 표면의 색을 지정하기 위한 float3 전역변수
gSurfaceColor를 ToonShader에 추가한 뒤 (0, 1, 0)으로 변경한다.
- 공간변환을 더 빠르게 할 수 있도록 월드, 뷰, 투영행렬을 미리 곱해둘 것이다.
- 합쳐진 행렬을 받을 Float4X4 전역변수
gWorldViewProjectionMatrix를 생성하고 WorldViweProjection으로 시맨틱을 설정한다.
- 하지만 난반사광 계산을 위해서는 월드공간에 정의된 빛의 위치를 사용해야되기 때문에 월드행렬이 필요하긴하다. 그래서 번거로움을 줄이기 위해 그냥 빛의 위치를 지역공간으로 변환해버린다.
- 월드행렬의 역행렬 Float4x4전역변수
gInvWorldMatrix를 생성하고 WorldInverse로 시맨틱을 설정한다.
📝 정점셰이더
1. 입력데이터
struct VS_INPUT
{
float4 mPosition : POSITION;
float3 mNormal: NORMAL;
};
2. 출력데이터
struct VS_OUTPUT
{
float4 mPosition : POSITION;
float3 mDiffuse : TEXCOORD1;
};
3. 전역변수
float4x4 gWorldViewProjectionMatrix;
float4x4 gInvWorldMatrix;
float4 gWorldLightPosition;
4. 함수
VS_OUTPUT vs_main( VS_INPUT Input )
{
VS_OUTPUT Output;
Output.mPosition = mul( Input.mPosition, gWorldViewProjectionMatrix );
float3 objectLightPosition = mul(gWorldLightPosition, gInvWorldMatrix);
float3 lightDir = normalize(Input.mPosition.xyz - objectLightPosition);
Output.mDiffuse = dot(-lightDir, normalize(Input.mNormal));
return( Output );
}
📝픽셀셰이더
1. 전역변수와 입력데이터
float3 gSurfaceColor;
struct PS_INPUT
{
float3 mDiffuse : TEXCOORD1;
}
2. 픽셀셰이더 함수
- 난반사광의 양을
0~1의 범위로 만든 뒤, 0.2단위로 자를 수 있도록 5를 곱하여 올림ceil을 적용한다. 그 결과 값은 0, 1, 2, 3, 4, 5 중 하나가 될 것이며 결과 값을 5로 나누어 최종 결과 값이 0, 0.2, 0.4, 0.6, 0.8, 1 이 되도록 한다.
float4 ps_main(PS_INPUT Input) : COLOR
{
float3 diffuse = saturate(Input.mDiffuse);
diffuse = ceil(diffuse * 5) / 5.0f;
return float4 : gSurfaceColor * diffuse.xyz, 1;
}
