2009년 3월 26일 목요일

Occlusion culling

occlusion culling

HW query 이용한 방식 : 하드웨어로 부터 그려진 픽셀의 개수를 얻을수 있음을 이용
- Occluder 바운딩박스 draw
- 오브젝트 바운딩을 그린후 query. 값이 0 이 아닐경우 실제 오브젝트 그린다.

쿼리를 위한 바운딩을 그릴때에는 zwrite & color off.


Ref.
http://www.gamasutra.com/features/19991109/moller_haines_01.htm
http://www.gamedev.net/reference/programming/features/occlusionculling/default.asp

2009년 1월 29일 목요일

Global terrain

행성(또는 지구) 전체의 지형을 표현하기위해선 구형태에 맞는 메쉬및 텍스쳐링이 필요하다
여러 방법이 있겠지만 기존 mmorpg에서 많이 사용되고 있는 하이트필드 & 쿼드트리 구조를 사용하고자 하면 지구를 위도/경도 방식으로 분할하여 각 지역에 기존의 맵을 매치 시키는것이 적합하다고 생각된다
하지만 이방법의 가장 큰 단점은 극지방으로 갈수록 왜곡이 심해지며 극지방의 표현이 어렵다는 것이다
위도/경도분할이 아닌 3ds max 지오스피어같은 삼각형 메쉬를 이용하면 왜곡의 정도를 줄이고 극지방또한 표현에 문제가 없겠지만 구조가 복잡해지고 이는 구현의 어려움과 실시간 게임, 특히 mmorgp에 적용하기에는 적합하지 않다고 생각된다.
위도/경도 방식을 사용하면서 극지방에 대한 보완방법을 찾아보려했으나 아직까지는 별다른 해법을 찾지못했다. 극지방을 사용하지 않고 그냥 텍스쳐로 처리하는 방법이 현재로선 최선이며 차후 좀더 자료를 찾아보고 생각해봐야겠다.
아울러 지형에서 우주로 나갈때 실제 메쉬 오브젝트 LOD -> 텍스쳐로 변경하여야 될듯한데 되도록 표가안나게 하는법, 지형 LOD방식에는 현재 구현된 미리계산된 정해진크기의 LOD 레벨및 연결부위사용 대신 실시간적으로 분할하는 ROAM과 같은 방식도 고려해 봐야 한다. 뭐 역시 나중에 좀더 알아봐야될듯..;;

Reference

http://download.microsoft.com/download/5/6/f/56f5fa07-51a4-4c0d-8a9a-2e8539214f2e/GDC2006_Szofran_Adam_Terrain_v1.doc

2009년 1월 28일 수요일

deprecated

라이브러리 버전 관리를 하다보면 더이상 필요되는 부분이 발생하거나 임시로 추가되는 부분이 생길수있는데 이러한 차후 삭제될 부분에 대한 표시가 필요하다
컴파일러에서 deprecated 키워드를 지원하지만 거의 사용하지 않았었는데(MS에서 C함수를 많이 고쳐놔서 기존소스에서 엄청난 경고가 뜨므로 꺼놓고 사용) 앞으로는 MS경고메세지도 잡고 사용하는게 좋을것 같다

두가지 사용예
#pragma deprecated(func1)
C4995 경고메세지가 뜬다. 단순히 선언되었음을 알리고 해당소스 컴파일시 메세지출력

__declspec(deprecated("this is a deprecated function"))
C4996 경고 메세지가 뜬다
다음에 오는 함수가 deprecated 되었음을 알리고 위에 메세지를 같이 표시해준다
함수를 실질적으로 사용할경우에 메세지가 출력된다. MS에서는 대부분 이렇게 사용되어있음

-----------
덧붙임.
* ms에서 만든 secure버전 함수 경고의 경우 _CRT_SECURE_NO_WARNINGS 을 선언해주면 발생되지않는다
* _CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES 을 1 로 선언하면 자동으로 secure버전으로 변환해준다. 단 정적배열을 사용하는 경우에만 가능

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