🆕 HLSL
cos()
- 코사인 함수
UVAnimation
으로 수정한다.Torus.3ds
로 설정한다.(원환체)gTime
을 생성하고, Time0_X
로 변수 시맨틱을 설정한다.float4x4 gWorldMatrix;
float4x4 gViewMatrix;
float4x4 gProjectionMatrix;
float4 gWorldLightPosition;
float4 gWorldCameraPosition;
float gTime; //시간변수 선언
struct VS_INPUT{
. . .
};
struct VS_OUTPUT{
. . .
};
VS_OUTPUT vs_main(VS_INPUT Input){
. . .
Output.mUV = Input.mUV + float2(gTime * 0.25f, 0); //U좌표에만 시간변수를 더하기(0.25를 곱해 속도 조절)
return Output;
}
. . .
float gTime;
//속도를 조절하는 변수들
float gWaveHeight;
float gSpeed;
float gWaveFrequency;
float gUVSpeed;
struct VS_INPUT{
. . .
};
struct VS_OUTPUT{
. . .
};
VS_OUTPUT vs_main(VS_INPUT Input){
VS_OUTPUT Output;
//[시간+정점마다 높낮이를 다르게 해주기 위한 UV좌표]를 매개변수로 하는 cos값
float cosTime = gWaveHeight * cos(gTime*gSpeed + Input.mUV.x*gWaveFrequency);
Input.mPosition.y += cosTime;
Output.mPosition = mul(Input.mPosition, gWorldMatrix);
. . .
Output.mUV = Input.mUV + float2(gTime * gUVSpeed, 0); //U좌표에만 시간변수를 더하기
return Output;
}
gWaveHeight: 3
gSpeed: 2
gWaveFrequency : 10
gUVSpeed: 0.25
왜 잘리는 부분이 보일까??
ShadowMap
이라고 한다.✔️점선2와 물체B가 만나는 픽셀을 그리는 경우
- 물체B의 깊이(현재 깊이): 0.5
- 그림자 맵에서 물체B의 값(그림자 깊이): 0.2 (A가 첫번째 물체)
- 현재깊이 > 그림자깊이 이므로 그림자가 생겨야한다는 것을 알아낸다.
ShadowMapping
으로 설정한다.Torus.3ds
로 수정하고, Model 변수의 이름도 Torus
로 변경한다.CreateShadow
로 변경한다. (패스를 여러개 사용하기 때문)1. 입력데이터
struct VS_INPUT
{
float4 mPosition : POSITION;
};
2. 출력데이터
struct VS_OUTPUT
{
float4 mPosition : POSITION;
float4 mClipPosition: TEXCOORD1;
};
3. 전역변수
matViewProjectionMatrix
를 삭제하고 셰이더에 float4x4변수 gWorldMatrix
를 생성하고 World 변수 시맨틱을 대입한다.gLightProjectionMatrix
은 Projection 변수 시맨틱으로, gLightViewMatrix
는 정점셰이더에서 만든다. (좋은방법은 아님)gLightViewMatrix
를 위해 광원의 위치 float4 gWorldLightPosition
를 만들고 (500, 500, -500, 1)로 설정한다.float4x4 gWorldMatrix;
float4x4 gLightViewMatrix; //광원-뷰공간
float4x4 gLightProjectionMatrix; //광원-투영공간
float4 gWorldLightPosition; //광원의 위치
4. 함수
VS_OUTPUT vs_main( VS_INPUT Input )
{
VS_OUTPUT Output;
//광원-뷰행렬 생성 (억지로 만든거라 이해필요 X)
float4x4 lightViewMatrix = gLightViewMatrix ;
float3 dirZ = -normalize ( gWorldLightPosition. xyz );
float3 up = float3 ( 0, 1, 0 );
float3 dirX = cross ( up, dirZ );
float3 dirY = cross ( dirZ, dirX );
lightViewMatrix = float4x4 (
float4 (dirX, - dot ( gWorldLightPosition.xyz, dirX )),
float4 (dirY, - dot ( gWorldLightPosition.xyz, dirY )),
float4 (dirZ, - dot ( gWorldLightPosition.xyz, dirZ )),
float4 (0, 0, 0, 1));
lightViewMatrix = transpose ( lightViewMatrix );
//공간변환 (물체공간 > 월드공간 > 광원-뷰공간 > 광원-투영공간)
Output.mPosition = mul ( Input.mPosition, gWorldMatrix );
Output.mPosition = mul ( Output.mPosition, lightViewMatrix );
Output.mPosition = mul ( Output.mPosition, gLightProjectionMatrix );
Output.mClipPosition = Output.mPosition ;
return Output ;
}
1. 입력 데이터
struct PS_INPUT{
float4 mClipPosition: TEXCOORD1;
};
2. 함수
z
을 반환하는데, 원근투영을 하면 2D 이미지 위에 그려지는 물체들의 XY좌표 값이 잘못되므로 올바른 XY값을 구하기 위해 w
로 나눈다. (책 p.216 이해 잘 안됨!)w
로 나누면 XY값의 범위는 -1~1, Z는 0~1이 된다.float4 ps_main(PS_INPUT Input) : COLOR
{
float depth = Input.mClipPosition.z / Input.mClipPosition.w;
return float4(depth.xxx, 1);
}
ShadowMap
으로 설정한다.ShadowMap
을 더블클릭하여 포맷을 R32F로 수정한다. 충분한 많은 깊이를 표현하기 위해 32비트 값으로 바꾸는 것이다.CreateShadow
패스에 ShadowMap
렌더타겟을 추가하고 더블클릭하여 Clear color를 하얀색으로 수정한다. (그림자를 받지 않는 부분을 하얀색으로 설정)ApplyShadowTorus
로 설정한다.1. 입력데이터
struct VS_INPUT
{
float4 mPosition : POSITION ;
float3 mNormal : NORMAL ; //난반사광 계산을 위한 법선
};
2. 출력데이터
struct VS_OUTPUT
{
float4 mPosition : POSITION ;
float4 mClipPosition : TEXCOORD1 ;
float mDiffuse : TEXCOORD2 ; //난반사광 결과
};
3. 전역변수
gViewProjectionMatrix
행렬을 셰이더에 추가하고 ViewProjection 시맨틱을 대입한다.//광원으로부터의 깊이를 구하기 위한 변수
float4x4 gWorldMatrix ;
float4x4 gLightViewMatrix ;
float4x4 gLightProjectionMatrix ;
float4 gWorldLightPosition ;
//뷰행렬과 투영행렬
float4x4 gViewProjectionMatrix ;
4. 함수
VS_OUTPUT vs_main ( VS_INPUT Input ) {
VS_OUTPUT Output ;
//광원-뷰행렬
float4x4 lightViewMatrix = gLightViewMatrix ;
float3 dirZ = - normalize ( gWorldLightPosition.xyz );
float3 up = float3 ( 0 , 1 , 0 );
float3 dirX = cross ( up , dirZ );
float3 dirY = cross ( dirZ , dirX );
lightViewMatrix = float4x4 (
float4 ( dirX , - dot ( gWorldLightPosition.xyz , dirX )),
float4 ( dirY , - dot ( gWorldLightPosition.xyz , dirY )),
float4 ( dirZ , - dot ( gWorldLightPosition.xyz , dirZ )),
float4 ( 0 , 0 , 0 , 1 ));
lightViewMatrix = transpose ( lightViewMatrix );
//1. 물체를 그리기 위한 공간변환
float4 worldPosition = mul ( Input.mPosition , gWorldMatrix );
Output.mPosition = mul ( worldPosition , gViewProjectionMatrix );
//2. 깊이를 구하기 위한 공간변환
Output.mClipPosition = mul ( worldPosition , lightViewMatrix );
Output.mClipPosition = mul ( Output.mClipPosition , gLightProjectionMatrix );
//3. 난반사광 구하기
float3 lightDir = normalize ( worldPosition.xyz - gWorldLightPosition . xyz );
float3 worldNormal = normalize ( mul ( Input.mNormal , ( float3x3 ) gWorldMatrix ));
Output.mDiffuse = dot (- lightDir , worldNormal );
return Output ;
}
1. 전역변수
ApplyShadowTorus
패스에 ShadowMap
텍스처 오브젝트를 추가하고 이름을 ShadowSampler
으로 설정한다. CreateShadow
패스가 그린 그림자맵 텍스처를 읽어오는 것이다.gObjectColor
로 변경한다.sampler2D ShadowSampler ;
float4 gObjectColor ;
2. 입력데이터
struct PS_INPUT
{
float4 mClipPosition : TEXCOORD1 ;
float mDiffuse : TEXCOORD2 ;
};
3. 함수
float4 ps_main ( PS_INPUT Input ) : COLOR
{
float3 rgb = saturate ( Input mDiffuse ) * gObjectColor ; //난반사광 값을 물체에 색상에 곱하기
float currentDepth = Input.mClipPosition.z / Input.mClipPosition.w ; //현재 픽셀의 깊이
//그림자맵의 uv좌표 구하기
float2 uv = Input.mClipPosition.xy / Input.mClipPosition.w ;
uv.y = - uv.y ;
uv = uv * 0.5 + 0.5 ;
float shadowDepth = tex2D ( ShadowSampler , uv ).r ; //uv로 그림자 깊이 구하기
//그림자맵은 R32F텍스처이므로 r채널만 불러오면 그림자의 깊이를 얻을 수 있다.
//그림자의 깊이와 현재 깊이를 비교하여 그림자 유무를 확인한다.
if ( currentDepth > shadowDepth + 0.0000125f ) {
rgb *= 0.5f ; //그림자(조명을 20% 줄이기)
}
return ( float4 ( rgb , 1.0f ) );
}
shadowDepth
에 0.0000125를 더한 이유는 부동소수점 에러 문제를 해결하기 위해 'A와 B의 값이 완전히 똑같아야 같은 걸로 본다'가 아닌 'A와 B의 값 차이가 매우 작으면 같은 걸로 본다.' 라고 정의하기 위해서이다.ShadowMap
의 크기를 2048로 설정한다.ApplyShadowTorus
패스를 복사하여 이름을 ApplyShaodwDisc
로 수정한다.gWorldMatrix
을 생성하고 아래와 같이 설정한다.