강물, 마그마 흘러가는 모습.
UV애니메이션을 사용한 것이다.
표면에 정점마다 UV 좌표를 지정한다.
이것을 한쪽으로 이동시킨다.
// 시간의 흐름에따라 UV를 변경
그렇다면 현재 시간을 알아야한다.
셰이더는 GPU상에서 실행되어 시간을 구하는 함수가 없다.
그래픽 카드에는 시스템 클럭이 안달려있기 때문이다.
현재시간은 CPU에서 계산한 값 넘겨 받는다.
정점셰이더
float4x4 gWorldMatrix;
float4x4 gViewMatrix;
float4x4 gProjectionMatrix;
float4 gWorldLightPosition;
float4 gWorldCameraPosition;
float gTime;
float gWaveHeight;
float gSpeed;
float gWaveFrequency;
float gUVSpeed;
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)
{
VS_OUTPUT Output;
float cosTime = cos(gTime * gSpeed + Input.mUV.x * gWaveFrequency) * gWaveHeight;
Input.mPosition.y += cosTime;
Output.mPosition = mul(Input.mPosition, gWorldMatrix);
float3 lightDir = normalize(Output.mPosition.xyz-gWorldLightPosition.xyz);
// viewDir
float3 viewDir = normalize(Output.mPosition.xyz-gWorldCameraPosition.xyz);
Output.mViewDir = viewDir;
// Position
Output.mPosition = mul(Output.mPosition, gViewMatrix);
Output.mPosition = mul(Output.mPosition, gProjectionMatrix);
// Diffuse & Reflection
float3 worldNormal = mul(Input.mNormal, gWorldMatrix);
Output.mDiffuse = dot(-lightDir, worldNormal);
float3 reflection = reflect(lightDir, worldNormal);
Output.mReflection = reflection;
Output.mUV = Input.mUV + float2(gTime * gUVSpeed, 0);
return Output;
}
// U좌표만 흐르도록 한다.
따라서 V 좌표에는 0을 더한다.
- 울렁효과
흘러갈 때 정점도 위아래로 울렁이는 효과를 넣는다.
Y값에 코사인을 활용한다.
Y값은 지역공간에서 적용한다.
코사인 함수의 인자는 호도법으로 정의된 각도를 받는다.
반환값이 -1 ~ 1이다.
정점마다 다른 높이를 주어야하므로 인자는 부드럽게 변화하는 값을 넣으면 된다.
UV를 인자로 사용한다.
여기서는 U를 사용하였다.
전역변수로 인자값들을 빼내면 다시 컴파일 하지 않아도 된다.
편리하다.
픽셀셰이더
float3 gLightColor; // *** Start Wool Lung Hyo Gwa !!!
sampler2D DiffuseSampler;
sampler2D SpecularSampler;
struct PS_INPUT
{
float2 mUV : TEXCOORD0;
float3 mDiffuse : TEXCOORD1;
float3 mViewDir : TEXCOORD2;
float3 mReflection : TEXCOORD3;
};
float4 ps_main(PS_INPUT Input) : COLOR
{
float4 albedo = tex2D(DiffuseSampler, Input.mUV);
float3 diffuse = gLightColor * albedo.rgb * saturate(Input.mDiffuse);
float3 viewDir = normalize(Input.mViewDir);
float3 reflection = normalize(Input.mReflection);
float3 specular = 0;
if(diffuse.x > 0)
{
specular = saturate(dot(reflection, -viewDir));
specular = pow(specular, 20.0);
float4 specularIntensity = tex2D(SpecularSampler, Input.mUV);
specular *= specularIntensity.rgb * gLightColor;
}
float3 ambient = float3(0.1, 0.1, 0.1) * albedo.rgb;
return float4(diffuse + specular + ambient, 1.0);
}
디퓨즈 / 스페큘러 매핑에 썼던 것과 동일하다.
- 기타 고급 정점셰이더 기법
- 스키닝 : BLENDWEIGHT와 BLENDINDICES 시맨틱을 이용하면 정점데이터에서 스키닝 정보 가져올 수 있다.
최종 애니메이션 행렬 전역변수로 건네 받는다. CPU의 스키닝과 동일해진다.
전역변수 사용하지 않고, 행렬들을 텍스쳐로 저장한 뒤 정점셰이더에서 텍스쳐 샘플링하는 경우도 있다.
- 지형: 높이맵을 이용한 기법. 정점 텍스쳐를 사용한다.
- 표정 애니메이션 : 여러가지 표정 별도 메쉬에 저장한다.
정점셰이더에서 두 메쉬를 혼합하여 표정 표현한다.
이것과 얼굴 스키닝 함께 사용 가능하다.
'셰이더 (Shader) > 셰이더 프로그래밍 입문 - Pope Kim (완)' 카테고리의 다른 글
[HLSL] 챕터11 - 흑백/세피아 사진 만들기 (0) | 2023.03.21 |
---|---|
[HLSL] 챕터10 - 그림자매핑 (0) | 2023.03.20 |
[HLSL] 챕터8 - 환경매핑 (0) | 2023.03.20 |
[HLSL] 챕터7 - 법선매핑 (1) | 2023.03.19 |
[HLSL] 챕터6 - 툰셰이더 (0) | 2023.03.19 |
댓글