오다기리 박의 알고리즘 노트

[셰이더 프로그래밍 입문] 9. UV애니메이션 본문

컴퓨터 그래픽스/쉐이더

[셰이더 프로그래밍 입문] 9. UV애니메이션

오다기리 박 2018. 7. 1. 23:40

9. UV애니메이션


  • 시간이 흐름에 따라 U좌표에 더해주는 값을 서서히 증가시키거나 빼주면 텍스처 흐름효과를 낼 수 있다. (정점셰이더역할)

  • 정점위치에 Y값을 적당히 증감해주면 울렁효과를 낼 수 있다. (정점셰이더 역할)


기타 고급 정점셰이더 기법

  • 스키닝 : CPU에서 스키닝처럼, 정점솅더에서도 동일하게 할 수 있음. BLENDWEIGHT와 BLENDINDICES 시맨틱 이용하면 정점데이터에서 스키닝 정보를 가져올 수 있음. 이제 최종 애니메이션 행렬들을 저역변수로 건네 받으면 CPU에서 하는 스키닝과 다를게 없다. 전역변수를 이용하지 않고 최종 애니메이션 행렬들을 텍스처로 저장한 뒤에 정점셰이더에서 텍스처 샘플링을 하는 경우도 있음(그래픽 카드가 정점텍스처를 지원해 줘야만한다. D3DUSAGE_QUERY_VERTEXTEXTURE를 통해 지원여부 확인가능)

  • 지형 : 높이맵을 이용하여 CPU에서 지형을 만드는 기법처럼 셰이더에서도 동일하게 할 수 있다. 정점 텍스처 사용해야함

  • 표정 애니메이션 : 여러가지 표정들을 별도의 메쉬로 저장한 뒤에 정점셰이더에서 두 메쉬를 혼합하는 기법으로 표현가능. 요즘은 이것과 얼굴 스키닝을 함께 사용하는 추세.



▶렌더몽키 코드 

- 버텍스 셰이더
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
float4x4 gWorldMatrix;
float4x4 gViewMatrix;
float4x4 gProjectionMatrix;
float gTime;
float gWaveHeight;
float gSpeed;
float gWaveFrequency;
float gUVSpeed;
 
float4 gWorldLightPosition;
float4 gWorldCameraPosition;
 
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=gWaveHeight*cos(gTime*gSpeed+Input.mUV.x*gWaveFrequency);
   Input.mPosition.y+=cosTime;
   
   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+float2(gTime*gUVSpeed,0);
   
   return Output;
}
cs

- 픽셀 셰이더
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
sampler2D DiffuseSampler;
sampler2D SpecularSampler;
 
 
float3 gLightColor;
 
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 reflection=normalize(Input.mReflection);
   float3 viewdir=normalize(Input.mViewDir);
   float3 specular=0;
   
   if(diffuse.x>0)
   {
      specular=saturate(dot(reflection,-viewdir));
      specular=pow(specular,10.0f);
      
      float4 specularIntensity=tex2D(SpecularSampler,Input.mUV);
      specular*=specularIntensity.rgb*gLightColor;
   }
   float3 ambient=float3(0.1f,0.1f,0.1f)*albedo.rgb;
   
   return float4(diffuse+ambient+specular,1);
}
cs

- 결과

UB애니메이션으로 울렁이는 효과를 재현해낸 것입니다. 시간이 흐름에 따라 U좌표에 더해주는 값을 서서히 증가시키거나 빼주면 텍스처를 좌우로 움직일 수 있게 됩니다. 추가로 정점의 위치가 위아래로 출렁이게 하기 위해 정점위치에 Y값을 적당히 증감해주었습니다.

img