2008년 12월 23일 화요일

HLSL알수없는 버그

HLSL 픽셀 쉐이더 코드중

첫번째:
vSample *= tex2D( SSAOSampler, TexCoord ).r;
vSample.x += 1.1f;
return vSample;

두번째:
float4 vSample2 = vSample * tex2D( SSAOSampler, TexCoord ).r;
vSample2.x += 1.1f;
return vSample2;

결과값의 차이는?
첫번째처럼 구현했다가 이상한결과가 나와 몇가지삽질중 두번째처럼 다른변수를 하나더만드니 제대로 나온다.. 뭥미.

개발환경: DirectX SDK April 2007 / VS2005 / WindowsXP

2008년 12월 3일 수요일

Ambient Occlusion 2 - SSAO

Screen Space Ambient Occlusion(SSAO) 은 깊이맵을 이용하여 음영을 계산하는 방법이다
한 픽셀의 음영값은 자신과 그 주변의 깊이값을 비교하여 주변의 깊이값이 작으면(카메라에 가까이있음) 어두워지게 처리한다. 이는 깊이값이 클경우 주변의 오브젝트에 둘러싸여 빛이 차단되었다고 가정하는것인데 실제적으로 3차원의 오브젝트에 적용하기에는 무리가 있다고 본다 (카메라방향의 깊이값 하나만으로는 오브젝트의 모양을 알수없고 빛의 차단여부또한 계산이 불가능)
하지만 그 결과값이 실제와 다르더라도 언뜻 보기에 그럴싸한 그림자가 생기며 계산과정이 단순하기 때문에 게임과 같은 실시간 랜더링을 필요로 하며 정확한 음영을 반영하지 않아도 무리가 없는 프로그램에는 매우 효과적인 방법이라 할수 있다

SSAO 처리과정

1. 재질이 포함된 일반적인 장면 랜더링

2. 깊이값을 텍스쳐로 생성
1번의 과정에서 생성된 Depth buffer를 텍스쳐로 가져오는 방법이 효율적이나 DX9의 경우 직접 가져올수 있는 방법을 제공하지 않으며 depth surface를 사용하더라도 하드웨어에 따라 다른 결과가 나올수있다(nVidia그래픽카드가 문제라는듯..;;)
현재로서는 깊이값 생성을 위한 쉐이더를 작성하여 한번의 패스를 도는방식으로 구현했다
DX10에서는 깊이버퍼를 직접 가져올수 있다하니 좀더 빠른 처리가 가능할것이다

3. 깊이값이 저장된 텍스쳐를 이용하여 음영값을 계산
깊이값 비교시 랜덤한 위치의 값을 취하기 위해 랜덤텍스쳐를 미리 만든뒤 랜덤텍스쳐의 값을 이용해 주위의 깊이값을 얻어온다

3.5 음영값에 블러 적용
선택적인 항목이라 3.5라 했으나 실제적으로 블러를 적용하지 않으면 노이즈가 낀거같은 음영이 생기므로 해야된다 --;

4. 계산된 음영과 1에서 그린 장면을 합성

SSAO 쉐이더 -------------------
(웹에서 찾은건데 출처는 잘 기억이 안난다 ;; 그리고 조금 수정했음)

float sampleRadius = 10.0f; // 주변값샘플링을 위한 반경값
float distanceScale = 500.0f; // Occlusion 강도
float4x4 Projection;

float3 cornerFustrum;

struct VS_OUTPUT
{
float4 pos : POSITION;
float2 TexCoord : TEXCOORD0;
float3 viewDirection : TEXCOORD1;
};

VS_OUTPUT VS(
float4 Position : POSITION, float2 TexCoord : TEXCOORD0)
{
VS_OUTPUT Out = (VS_OUTPUT)0;

Out.pos = Position;
Out.TexCoord = TexCoord;

// 카메라 fustrum의 값. 아래와 같이 계산해서 올려주면 된다
// float farY = (float)tan(m_pCamera->GetFov()*.5f) * m_pCamera->FarPlane();
// float farX = farY * m_pCamera->GetAspectRatio();
//
// D3DXVECTOR3 vCornerFustrum;
// vCornerFustrum.x = farX;
// vCornerFustrum.y = farY;
// vCornerFustrum.z = m_pCamera->FarPlane();
// m_pEffect->SetValue("cornerFustrum", &vCornerFustrum, sizeof(float)*3);

// 카메라에서 바라보는 방향
float3 corner = float3(cornerFustrum.x * Position.x,
-cornerFustrum.y * Position.y, cornerFustrum.z);

Out.viewDirection = corner;
return Out;
}


texture depthTexture;
texture randomTexture;

sampler2D depthSampler = sampler_state
{
Texture = ;
ADDRESSU = CLAMP;
ADDRESSV = CLAMP;
MAGFILTER = LINEAR;
MINFILTER = LINEAR;
};

sampler2D RandNormal = sampler_state
{
Texture = ;
ADDRESSU = WRAP;
ADDRESSV = WRAP;
MAGFILTER = LINEAR;
MINFILTER = LINEAR;
};

float4 PS(VS_OUTPUT IN) : COLOR0
{
float4 samples[16] = // 16개의 샘플링을위한 값들
{
float4(0.355512, -0.709318, -0.102371, 0.0 ),
float4(0.534186, 0.71511, -0.115167, 0.0 ),
float4(-0.87866, 0.157139, -0.115167, 0.0 ),
float4(0.140679, -0.475516, -0.0639818, 0.0 ),
float4(-0.0796121, 0.158842, -0.677075, 0.0 ),
float4(-0.0759516, -0.101676, -0.483625, 0.0 ),
float4(0.12493, -0.0223423, -0.483625, 0.0 ),
float4(-0.0720074, 0.243395, -0.967251, 0.0 ),
float4(-0.207641, 0.414286, 0.187755, 0.0 ),
float4(-0.277332, -0.371262, 0.187755, 0.0 ),
float4(0.63864, -0.114214, 0.262857, 0.0 ),
float4(-0.184051, 0.622119, 0.262857, 0.0 ),
float4(0.110007, -0.219486, 0.435574, 0.0 ),
float4(0.235085, 0.314707, 0.696918, 0.0 ),
float4(-0.290012, 0.0518654, 0.522688, 0.0 ),
float4(0.0975089, -0.329594, 0.609803, 0.0 )
};

normalize (IN.viewDirection);
float depth = tex2D(depthSampler, IN.TexCoord).a;
float3 se = depth * IN.viewDirection;

float3 randNormal = tex2D( RandNormal, IN.TexCoord * 200.0 ).rgb;

float finalColor = 0.0f;

for (int i = 0; i < 16; i++)
{
// 랜덤한 값 추출을 위해 샘플값에 랜덤텍스쳐에서 가져온 값을 이용하여 반사벡터를 취한다
// 꼭 반사벡터가 아니어도될거 같아서 몇가지 다른 방법을 취해봤으나 이게 제일보기좋음
float3 ray = reflect(samples[i].xyz,randNormal) * sampleRadius;

float4 sample = float4(se + ray, 1.0f);
float4 ss = mul(sample, Projection);

// 0~1 범위의 uv 공간으로 변환
float2 sampleTexCoord = 0.5f * ss.xy/ss.w + float2(0.5f, 0.5f);
float sampleDepth = tex2D(depthSampler, sampleTexCoord).a;

if (sampleDepth == 1.0)
{
finalColor ++;
}
else
{
// depth > sampleDepth 일 경우 occlusion 적용
float occlusion = distanceScale* max(depth - sampleDepth, 0.0f);
finalColor += 1.0f / (1.0f + occlusion * occlusion * 0.1);
}
}

return float4(finalColor/16, finalColor/16, finalColor/16, 1.0f);
}


technique SSAO
{
pass P0
{
VertexShader = compile vs_3_0 VS();
PixelShader = compile ps_3_0 PS();
}
}