hwi's laboratory  
Front Page
Tag | Location | Media | Guestbook | Admin   
 
'works/old'에 해당하는 글(24)
2010.09.01   Deferred Rendering
2010.08.30   ing2
2010.07.14   ing...
2010.05.11   3D 입체 영상의 기본 원리
2010.05.05   간만에 ray-tracing
2010.01.22   Photon Mapping 3
2009.05.19   lightmap generation, radiosity
2009.04.16   Verlet Integration
2009.04.09   강체 시뮬레이션(Rigid Body Simulation) 4
2009.01.28   skinned instancing 3


Deferred Rendering
처음에는 Light-Pre Pass 방식으로 Deferred Rendering을 구현했었는데...

Normal

Depth

Diffuse

Specular

Final



Normal, Depth용으로 한번, 재질처리용으로 한번 총 2번의 DrawCall 그리고
Diffuse, Specular 따로 처리하려면 2개의 HDR 포맷 렌더타겟이 필요하다. 비용이 생각보다 꽤 들고, 기존의 Deferred 방식보다 더 복잡하다.

물론 최적화로 Diffuse(RGB), Specular(A) 이렇게 Specular 세기만 저장할 수도 있겠지만 그럼 색상이 있는 Specular를 표현하지 못하니까 별로 맘에 들지 않아 기존의 Deferred Rendering 방식으로 처리하기로 결정

Global Light Only


Deferred로 처리된 Local Light 포함

G-Buffer의 구성은 아래와 같다. 

MRT0 : Albedo(RGB), Specular(A)
MRT1 : Normal(RGB)
MRT2 : Depth(R32F)
MRT3 : Global Lighting(LogLUV)

Emissive나 환경맵 반사, SH를 이용한 Ambient 라이트 같이 객체별로 다른 특성을 가질 수 있는 조명 환경/특성이나 Directional Light같이 전체에 적용되어야하는 조명을 위해서 마지막에 LogLUV로 인코딩되는 버퍼를 하나 두었다. 

GPU PRO를 보니까 Call of Juarez2에서 MRT1의 Normal를 제외한 나머지 Alpha 성분에  SSS 특성을 저장했는데 그런 재질 특성을 추가적으로 하나 더 저장해놓으면 좋을것 같다. 

CPU로 스키닝 중이라 GPU로 하게하면 더 최적화 시킬 수 있을 것 같다. 둠3 모델들이 뼈대가 많아서 자동으로 나눠주는 알고리즘을 보고 있다. 


ing2




ing...





3D 입체 영상의 기본 원리
3D 입체 영상의 기본원리는 동일하다. 

한 화면에 좌우 각기 다른 이미지를 동시에 출력하고 안경을 통해서 동시에 출력된 이미지를 분리해서 각각의 눈에 전달하는 방식이다. 

이러한 기본원리를 어떻게 구현하느냐에 따라 3가지 방식으로 나뉜다. 

1. 색차(Anaglyph) 방식
색으로 좌우 이미지를 구분한다.

가장 흔하게 볼 수 있는 방법이 왼쪽 눈에 들어올 이미지는 0xff0000(Red)을 곱하고 오른쪽 눈에 들어올 이미지는 0x00ffff(Cyan)을 곱한 이미지를 더해서 화면에 출력하는 방식이다. 

색 자체를 조절하기 때문에 색감이 상당히 떨어진다. 

2. 편광 방식
왼쪽은 수평 파장(또는 수직) 오른쪽은 수직 파장(수평 파장)으로 이미지를 보내고 안경을 통해 이미지가 걸러지게 된다. 

색차 방식과 다르게 편광을 이용하기 때문에 색감이 많이 유지된다.  영화 아바타를 상영할 때 이러한 방식을 사용했다.

디스플레이 비용이 비싸다는 단점이 있다.

3. 셔터 글라스 방식 
최근 3D TV의 경향으로 빛의 잔상 효과를 이용한 방식이다. 앞의 두 방식과 다른게 한 프레임에 좌우 이미지가 동시에 출력되지 않고 아주 빠르게 번갈아 가면서 좌우 이미지를 출력한다.  

좌우 이미지를 교대로 좌우 눈에 전달하기 위해서 안경이 좌우 번갈아 가면서 검은색이 되어 들어와야 하는 이미지를 선별한다. 

안경의 한쪽을 검은색으로 만들기 위해 LC(Liquid Crystal)을 이용하는데 LC는 전기가 들어오게 되면 검정색으로 변하는(빛을 차단하는) 성질을 가진다. 

디스플레이가 빠른 속도로 좌우 이미지를 번갈아 출력해야하기 때문에 화면의 출력속도가 상당히 빨라야하고 안경과 동기화를 맞춰주어야한다. 

디스플레이 비용은 편광 방식보다 싸지만 안경이 비싸다. 






간만에 ray-tracing
학교수업에서 병렬처리에 대해 발표를 준비하면서 좀 더 빠르게 kd-tree를 처리할 수 없을까 해서 ray-tracing에 대한 최적화 기법을 정리한 Ingo Wald의 Realtime Raytracing and Interactive Global Illumination 문서(6,7장)를 보고 간략하게 만들었다. 

간만에 ray tracing 작업을 해서 그런지 밤새가면서 정말 잼나게 작업한 것 같다. SIMD로 Ray  4개를 하나로 묶어 하는건 Global Illumination 환경에서는 그다지 효율적이지 않을 것 같아서 일단 패스. 멀티 스레드 환경에서 mailboxing 하는 것을 좀 더 보려고한다. 

길성이 조교실 컴이 i7이라고 해서 성능을 비교 했는데 내 구닥다리 듀얼코어 2.0GHz 보다 약 4배 빠르다;;;


요건 샘플개수 4096개. 둠3 모델은 역시 유용하다. 

덧. mailboxing을 사용해봤는데 애초에 kd-tree를 잘 짜면 교차되는 삼각형의 수가 적기 때문에 크게 효율이 오르진 않았다. 멀티 스레드 환경에서는 hash table을 이용해서 mailboxing을 수행한다. 

너무 어둡게 나와서 directional light 하나  추가하고 노출값 조절함.





Photon Mapping
Henrik Wann Jensen이 1996년에 개발한 알고리즘. 
이 분이 쓰신 Realistic Image Synthesis Using Photon Mapping 을 참고해서 구현했다. 부록으로 뒤쪽에 소스코드까지 있으니 가지고 노는 것이 그리 어렵지는 않았다. 

포톤 맵핑은 크게 2개의 패스로 나뉜다.
첫번째 패스에서는 조명에서 포톤들을 뿌리는 것이다. (photon tracing)
포톤은 radiance가 아니라 flux를 전달하는데 포톤이 반사되는 것은 ray tracing과 똑같이 재질에 따라서 반사 방향을 정하게 된다. 

그런데 그 크기는 BRDF가 아니라 반사도 값으로 반사하게 되는데, 이렇게 모델링한 정확한 수학적 근거는 잘 모르겠다. 

Pbrt에서는 Veach가 고안한 방법으로 설명을 하는데 importance 값에 대해서 이해를 하지 못하고 있다. 

또, 고민이 되는 부분은 이미지 기반 조명을 어떻게 해야할까 하는 것이다. 

암튼, 두번째 패스에서는 뿌려진 포톤을 이용해서 렌더링을 하는데, 
direct 조명과 specular 반사는 기존의 path tracing 방법을 사용하고 caustics는 caustics photon map에서 그대로 눈으로 가는 radiance를 추정한다. 

diffuse-diffuse 반사는 global photon map을 사용하는데 caustics와 같이 photon map에서 바로 radiance를 구할 수도 있지만 이렇게 하면 적절한 파라메터가 전달되지 않으면 radiance 추정 방법에 따른 곰팡이 같은 원형 모양의 패턴이 나타나기 쉽다.

이런 패턴이 보이지 않도록 곧바로 표면에서 반사되는 radiance를 구하는 것이 아니라 그 표면에 들어오는 radiance를 추정해서 반사되는 radiance를 구한다. 이것을 final gather 라고 한다. 

radiance estimate




샘플개수는 1024





lightmap generation, radiosity




Lightmap Generation
1. 1 texel 당 unit을 정한다. 이 값으로 폴리곤에 대한 라이트맵의 크기를 계산할 수 있다.
2. polygon들 마다 lightmap을 생성하고 조명을 계산한다.
3. lightmap을 큰 텍스쳐에 packing 한다. 큰 면적 순으로 라이트맵을 정렬한 다음에 2D BSP Tree를 이용하면 쉽게 Lightmap을 Packing 할 수 있다.

참고: 


Radiosity
1. 기본 알고리즘
Radiosity 알고리즘은 패치 단위로 계산을 한다. 
특정 패치를 떠나는 에너지 = 패치의 색상

i패치를 떠나가는 에너지 = i에서 방출되는 에너지 + i에서 반사되는 에너지
i에서 반사되는 에너지 = i의 반사율 * SUM_{NOT(j=i)} { [j를 떠나가는 에너지] * [i를 떠나가는 에너지 중 i에 도착하는 에너지 비율] }

A_i : i패치의 영역
e_i : i패치에서 방출되는 에너지
p_i : i패치의 반사율
b_i : i패치의 radisotiy
F_{ji} : j패치에서 i패치에 이르는 형식계수(form factor).
          j패치에서 떠나는 전체 radiosity가 i패치에 직접 닿는 비율

b_i = e_i + p_i SUM_{j=1}^{n} {b_j * F_{j-i} * A_j / A_i}

Form Factor의 특성
A_i * F_{ij} = A_j * F_{ji}

b_i = e_i + p_i SUM_{j=1}^{n} {b_j * F_{ij}}

2. Form Factor
F_{ji} = 1 / A_i INTEGRAL{INTEGRAL{cos(theta_i) * cos(theta_j) / (pi * r^2) * V_{ij} * dA_i * dA_j }}
V_ij : 패치 i와 j의 가시성 판단(1:보임, 0:안보임)
dA_i, d_Aj : 극소 영역
r : i와 j를 잇는 선분의 길이
theta_i, theta_j : i와 j를 잇는 선분과 i와 j의 법선 사이의 각


3. Iterative Radiosity(단계적 Radiosity)
i를 떠나가는 에너지를 계산하려면 j의 떠나가는 에너지를 알아야 하고, 동시에 j를 떠나가는 에너지를 알아내려면 i를 떠나가는 에너지가 필요하기 때문에 계산하기 어려운 점이 있다. 
기본 정석대로라면 매우 큰 n*n 행렬을 만들어서 계산한다.

단계적 radiosity와 행렬 Radiosity와 큰 개념적 차이는 빛을 쏘아보내서 계산을 한다는 것이다. shooting radiosity 라고 한다.

1. 처음에 emissive light 값을 방출한 에너지 값으로 설정한다.(조명에만 설정)
2. 방출해야 할 에너지가 가장 높은 패치를 하나 선택한다. 이것을 src라고 한다. 
3. src의 방출할 에너지 값이 일정값 이하이면 계산을 멈춘다.
4. src의 에너지를 다른 모든 패치에 방출시킨다. 
   src의 에너지를 받는 패치를 dest라고 하면, 
   4.1 dest가 src에서 오는 에너지에 대해 방출시킬 에너지양 = dest의 반사율 * src와 dest의 form factor
   4.2 4.1에서 계산한 값을 dest의 radiosity(최종 에너지양)과 dest의 방출시킬 에너지에 더한다. 
5. src의 에너지를 모두 방출시켰으므로 src의 방출 에너지를 0으로 설정한다.
6. 2번으로 돌아간다. 
   
   
참고:
Advanced 3D Game Programming using DirectX 8.x (Peter Walsh, 영진닷컴)



Verlet Integration


위치와 가속도만으로 물리 시뮬레이션을 해야할 때 유용. 게임에서 Cloth를 표현하기 위해서 구현해보았다. 

Verlet 적분의 원리를 정리하면(참고:게임 & 인터랙티브 애플리케이션을 위한 수학)
y(t+h) + y(t-h) = y(t) + hy'(t) + h^2 / 2 y''(t) + ...
                       y(t) - hy'(t) + h^2 / 2 y''(t) - ... 

y(t+h)에 대해 풀면 
y(t+h) = 2y(t) - y(t-h) + h^2 y''(t) + O(h^4)

간격형식으로 다시 정리하면 
y_(i+1) = 2y_i - y_(i-1) + h_i^2 y_i''

Cloth를 시뮬레이션 하는 방법은 Verlet 방법 이외에도 Particle과 Spring-Damper를 이용하는 방법이 있는데, Verlet 방법은 충돌반응을 신경쓰지 않고 충돌에 대해 위치만 제대로 잡아주면 되기 때문에 더 간단히 시뮬레이션 할 수 있다.

복잡한 Cloth 자체의 충돌처리는 게임에서는 하지않고 Cloth와 Cloth가 서로 겹쳐지지 않게 충돌박스나 구를 적절하게 배치하는 것이 중요하다. 


참고:



VerletIntegration.exe

W, A, S, D로 Cloth 이동


강체 시뮬레이션(Rigid Body Simulation)

공개물리엔진과 책들을 참고해서 강체 시뮬레이션을 구현했다.

충돌 검출 부분은 ODE, 물리 시뮬레이션 부분은 JigLib를 참고했다. 

박스와 박스, 박스와 삼각형 충돌의 경우 SAT 알고리즘을 사용하는 것이 효율적이다. 
박스와 구, 삼각형 이외 더 복잡한  Convex 객체들끼리의 충돌을 구하는 효율적인 알고리즘은 GJK + EPA 알고리즘이 있다.  Bullet이 GJK, EPA 부분에 대해 잘 나와있다.

서적은 국내에서 나온 게임 물리관련 여러 서적이 있지만, 그런 게임 물리 서적 보다는 '게임 인터렉티브 애플리케이션을 위한 수학' 책 뒷부분에 나오는 강체 처리 부분이 더 정리가 잘 되어 있다고 생각한다. 

전체적으로 설명과 소스까지 한번 쉽게 살펴보려면 Game Physics Engine Development(millington, morgan kaufmann) 이 책을 추천한다. 

사원수로 방위를 표현할 때 각속도로 방위를 갱신하는 식은 
q' = q +  dt  *1/2 wq
인데, 이 식의 증명은 Game Physics(David H. Eberly)에 나와있다. 

간단히 강체 시뮬레이션에서 몇가지 중요한 점만 살펴보자.

1. Collision Detection
충돌을 검출하는 부분으로, 단순히 충돌의 유무 뿐만 아니라
충돌점, 충돌법선, 투과깊이를 구해야한다. 이 3가지 정보로 충돌 처리를 수행할 수 있다.

2. Collision Resolve
충돌 해소에서 가장 직관적이고 간단한 방법은 impulse를 도입하는 것이다.
impulse는 아주 짧은 시간에 적용되는 큰 힘이라고 생각하면 된다. 시간에 상관없이 곧바로 강체의 속도를 바꾼다. 

impulse로 충돌을 처리하는 방법은 여러 방법이 있는데 충돌을 하나 하나씩 처리하는 방법이 가장 간단한다. 

충돌한 두 객체의 충돌정보(충돌점, 충돌법선, 투과깊이)를 이용해서 두 객체에 적용해야할 impulse 크기를 계산하고 impulse를 적용시키면 된다. 

impulse 크기 구하기.
1. unit impulse가 적용되었을 때 강체의 속도가 얼마나 변화하는 지를 계산한다.
2. 반발계수를 이용하면 충돌점에서의 상대 속도가 얼마나 변화해야하는 지를 알 수 있다.
u_rel' = -e * u_rel (u_rel : 충돌점에서의 상대속도)

3. [ u_rel' - u_rel ] 이 식은 우리가 구하고자하는 속도 변화량의 크기이다. 그러므로 impulse의 크기는 [(u_rel' - u_rel) / unit impulse당 속도변화량]이 된다. 

3. Contact Resolve
물리 시뮬레이션에서 제일 중요한 부분이 이 부분이다. 
강체를 계속 움직이게 하는건 쉬우나 가만히 있어야 하는 강체를 가만히 있게 하는 것이 더 어렵다. 
Contact는 반발이 되지 않는 Collision 이다. 즉 반발계수가 0이 되어야 한다. 
이 부분에서 강체가 서로 투과하지 않도록 하는 처리를 해주어야 한다. 

처리하는 알고리즘 자체는 Collision Resolve와 크게 다르지 않다.
impulse로 반발계수가 0인 충돌을 처리해야 한다. 

impulse는 속도변화와 관련되어 있으므로 투과깊이를 속도로 표현해야 한다. 시뮬레이션의 시간 변화량이 dt라고 할 때,

minSeparateVelocity = contact.penetrationDepth / dt

minSeparateVelocity가 투과 깊이를 속도로 표현한 양이다. 
즉, 강체가 서로 곂치지 않게 하려면 충돌점에서의 상대 속도가 최소한 저 속도의 크기 보다는 커야한다. 

그리고 Contact를 처리해야 하므로 두 점의 상대 속도가 minSeparateVelocity의 크기가 되도록 해야한다.

따라서 impulse의 크기는 (minSeparateVelocity - u_rel)  / unit impulse당 속도변화량) 가 된다. 

주의할 점은 이렇게만 처리하면 작은 떨림이 지속적으로 발생한다. 

예를들면, 박스와 평면의 충돌시 박스 하단의 4점이 충돌점이 되는데, 충돌점 하나를 처리하면 나머지 3개의 충돌점의 상대 속도가 바뀌고, 원래 적용해야할 impuse보다 더 큰 impulse가 필요해진다. 이러한 현상이 계속되어 떨림이 멈추지가 않는다.

위 현상을 최소화 하기위해서는 적용시킬 impulse를 작게해서 한번에 밀어내지 말고 여러번에 걸쳐 조금씩 밀어내는 방법이 있다.(JigLib 참고) 

참고 : 
4. Game Physics Engine Development(millington, morgan kaufmann)



skinned instancing

사용자 삽입 이미지

한꺼번에 애니메이션 되는 캐릭터 200마리를 그리고 있다.

현재 프레임에서 필요한 애니메이션을 위한 스키닝 행렬을 텍스쳐에 넣은 후에 instance data에 World Matrix와 필요한 애니메이션 인덱스를 넣어서 인스턴싱을 수행한다.

스키닝 처리는 instance data에 저장된 애니메이션 인덱스로 vertex shader에서 스키닝 행렬이 들어간 텍스쳐를 참조해서 수행한다.(VTF)


참고 :
http://http.developer.nvidia.com/GPUGems3/gpugems3_ch02.html










BLOG main image
 Notice
 Category
분류 전체보기 (27)
life (0)
project (0)
works (24)
1. realtime GI (0)
old (24)
misc (0)
study (1)
scrap (1)
review (0)
... (0)
study_cg (0)
 TAGS
CEDEC
 Calendar
«   2024/04   »
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
 Recent Entries
 Recent Comments
 Recent Trackbacks
 Archive
 Link Site
바보가 아닌 Mr
V i n t e r s o r g
 Visitor Statistics
Total :
Today :
Yesterday :
rss