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();
}
}

2008년 11월 20일 목요일

KGC2008

지난주에 KGC2008에 하루 참가했었다
기대는 그닥 안했지만 GSTAR도 볼겸해서 갔었는데
IK / Visual material tool / Scaleform 에 관한 발표를 듣고왔다

IK 의 경우 모션 블랜딩/ 애니메이션합성 / 몇가지 기본적인 IK에대한 설명이 있었다

Visual material tool
툴에서 시각적으로 머터리얼을 구성할수 있게한다
구성된 머터리얼에 관한 쉐이더파일을 자동으로 생성해주며 이를 이용한 각종 효과를 만들어내는것을 볼수 있었다

Scaleform
스케일폼은 플래쉬UI를 게임에서 사용할수 있게 해주는 미들웨어이다
기본적인 UI및 채팅, Drag & Drop, 3D UI, 미니게임등을 플래쉬를 이용하여 제작할수있다
동영상부분도 곧 지원할 예정

미들웨어 사용은 개인적으로도 필요하다고 생각하는편인데 꽤 쓸만한거 같다
모든걸 다 만들수는 없으니 미들웨어를 적극사용하고 랜더링 엔진에 집중하는게 낫다고 보는데
랜더링 엔진도 워낙 뛰어난게 많이 나와있어서 ㄷㄷㄷ

관심있거나 사용해본 미들웨어/라이브러리
OpenAL : 사운드 라이브러리
Zlib : zip 압축
Scaleform : UI
PhysX : 물리엔진
Speedtree : 나무생성

2008년 11월 19일 수요일

Singleton

아래와 같은 Meyers 아저씨의 싱글톤을 별 문제없이 사용 중 싱글톤 소멸에 관련한 에러 발생.
DXUT 구조에서 OnCreateDevice()에서 처음으로 접근(생성) 하고 OnDestroyDevice() 에서 다시 접근시
이미 싱글톤객체가 소멸되어 있음.

template
class Singleton
{
public:
static T* Instance()
{
if (!ms_pkInstance)
CreateInstance();
return ms_pkInstance;
}

static void CreateInstance()
{
static T kInstance;
ms_pkInstance = &kInstance;
}

private:
static T* ms_pkInstance;
};


template
T* Singleton::ms_pkInstance = 0;

해결방안..

모던 C++에서 소개한 방법 중 불사조 싱글톤의 구현
destroyed_ 변수를 추가하여 소멸되었는지를 판단 후 소멸되었을 경우 다시 생성
부활한 싱글톤은 atexit() 함수를 사용하여 소멸 예약

atexit() 문제발생 : C++표준의원회에서 고칠거라고는 하지만 WindowsXP/VS2005 에서 문제가 있으므로 일단 아래 방법제시

1.메모리 누수 방치 --;; : 누적되지 않고 프로그램 종료 시에만 발생되므로 OS에서 알아서 회수할 거임.
2.싱글톤 생성 시기 조절 : 프로그램 시작부근에서 CreateInstance()를 호출해주면 오래 살아남음 ;;
3.강제 소멸 : 더 이상 필요 없을 경우 KillPhoenixSingleton()함수로 강제 소멸
4.??

참고자료 : 모던 C++디자인

-> 그 외 싱글톤의 수명제어 및 멀티스레딩에 관련된 부분. 각각의 기능을 템플릿으로 나누어 조합할 수 있게 하는 것 등 소개

template
class PhoenixSingleton
{
public:
static T& Instance()
{
if (!ms_pkInstance)
{
if (destoryed_)
{
// Dead Reference
CreateInstance();
new(ms_pkInstance) T;
#ifdef ATEXIT_FIXED
atexit(KillPhoenixSingleton);
#else
destoryedOnce_ = true;
#endif // ATEXIT_FIXED

destoryed_ = false;
}
else
{
CreateInstance();
}
}
return *ms_pkInstance;

}

static void CreateInstance()
{
static T kInstance;
ms_pkInstance = &kInstance;
}

static void KillPhoenixSingleton()
{
#ifndef ATEXIT_FIXED
if(destoryedOnce_)
#endif // ATEXIT_FIXED
ms_pkInstance->~T();
}
protected:
virtual ~PhoenixSingleton()
{
ms_pkInstance = 0;
destoryed_ = true;
}

static T* ms_pkInstance;
static bool destoryed_;

#ifndef ATEXIT_FIXED
static bool destoryedOnce_;
#endif // ATEXIT_FIXED

};

template
T* PhoenixSingleton::ms_pkInstance = 0;
template
bool PhoenixSingleton::destoryed_ = false;

#ifndef ATEXIT_FIXED
template
bool PhoenixSingleton::destoryedOnce_ = false;
#endif // ATEXIT_FIXED

2008년 11월 18일 화요일

억새풀

2008/10 명성산 억새풀밭

Tone mapping

톤맵핑은 전체신의 현재 밝기를 계산하여 전체적인 톤을 조절하는것. .끝 -_-;;
- 전체신의 밝기계산은 다운샘플링을 이용한다
- 기존의 값과 변경되는 값의 변화를 서서히 적용하면 사람 눈의 암(명)적응과 같은 효과를 나타낼수있다
- 사실 구현한지 좀 되서리 구체적인내용까지는 생각나지 않는다 조만간 다시봐야되므로 그때 좀더 추가할것임.

Ambient Occlusion

SHADOW 기법의 하나로 주변광의 적용
주변광의 차단을 계산하여 음영값을 구한다

------------------------------------------------------

GPU Gems Chapter 17
- AO 기본 개념 설명
- 오브젝트의 각 포인트에 대해 랜덤하게 생성된 조명(레이)과의 충돌 검사를 통해 음영계산
  (OUTPUT :수정된 노말 벡터)
- 하드웨어가속  : 실질적인 충돌검사대신 조명의 방향에서 생성된 깊이맵을 이용해 차단판단
하드웨어의 발달은 깊이맵을 빠르게 생성할수 있게 해준다(nVidia예제참고)

-> 실시간(게임)에 사용하기에는 느리다

------------------------------------------------------

GPU Gems 2 Chapter 14

- 메쉬의 각 포인트를 디스크로 단순화한 모델
- 차단(충돌) 검출이 단순화 된다
- nVidia 예제참고

-> GPU Gems 에 소개된 방법보다 빠른 수행속도를 보여준다
    구형의 그래픽카드에서는 여전히 느리다

------------------------------------------------------

Screen Space Ambient Occlusion(SSAO)

- 시각좌표에서의 깊이맵을 이용하여 AO과정 단순화
- 깊이맵은 시각좌표에서의 높이값이므로 주변값들과의 비교를 통해 차단상태를 판단한다
- 근사화 기법으로 오류가능성이 있어보이나 나쁘지 않은 결과을 보여주는 듯하다
- 크라이2엔진, 스타크래프트2등 게임에 적용되고 있음

-> 현재 분석중.. 차후 분석및 구현이 끝난후 추가사항 기록할것