블로그 이미지
재미있게 게임을 만들고 싶은 아저씨.. jorisma

카테고리

분류 전체보기 (17)
Game programing (13)
Diary (1)
Programming Tip (3)
Spring 2011 (0)
Total
Today
Yesterday

달력

« » 2024.12
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 31

공지사항

태그목록

최근에 올라온 글

HDR + Bloom

Game programing/Shader / 2013. 10. 28. 21:34


주의 사항 

1. Gamma correction

2. 렌더 타켓을 HDR 버퍼로 만들어야 한다.



Tone mapping 진행 순서 

기본적으로 DirectX Sample 에서 나오는 축소버퍼를 사용해서 화면 전체의 평균 휘도를 구하는 방식으로 진행 

1. 조명 처리가 끝난 후 HDR 버퍼에 64x64 사이즈의 렌더타켓으로 다운 샘플링 한다.

  - log( dot( vSample, LUMINANCE_VECTOR ) + 0.0001f )

2. 1에서 만들어진 렌더 타겟을 1x1사이즈가 될 때 까지 다운 샘플링을 계속 진행한다.

3. 1x1으로 만들어진 렌더 타켓을 통해서 평균 휘도를 구한다. 

 - exp( fResampleSum / 16 )

4. 알아낸 평균 휘도와 이전 프레임의 평균 휘도를 통해서 급격한 변화를 하지 않도록 현재 평균 휘도를 구한다. 

    float fNewAdaptation = fAdaptedLum + (fCurrentLum - fAdaptedLum) * ( 1 - pow( 0.98f, 0.5 ) );

      color0 = float4(fNewAdaptation, fNewAdaptation, fNewAdaptation, 1.0f);


5. 만들어진 평균 휘도를 가지고 알맞는 Tone mapping operator를 사용해서 전체 픽셀의 값을 변경한다.


Bloom 진행 순서

기본적으로 요즘 게임에서 많이 사용하는 MGF 방식으로 진행 

1. 조명 처리가 끝난 후 HDR 버퍼에서 원하는 휘도 이상의 값만 추출한다. 이때 적절한 다운샘플링해서 뽑아내도 좋다.

2. 추출한 다운 샘플링 원본을 가지고 1/2 x 1/2로 만든다. 

 - 1/4 x 1/4 사이즈로 축소 시켜도 된다 하지만 너무 작은 사이즈가 되어버린 렌더 타켓은 전체 품질저하를 이르킨다.

3. 2에서 만들어진 렌더 타켓을 Gaussian 필터나 다른 필터를 사용하여 blurring 한다. 

4. 2~3번을 반복하면서 하나의 렌더 타켓에 누적시킨다. 





< 왼쪽은 작은 1/4 x 1/4 로 축소된 버퍼를 Gaussian 필터를 사용하여 Blurring 하고선 다시 원래 사이즈 렌더타켓에다 누적 >


Kawase식 MGF방식

원문 : http://game.watch.impress.co.jp/docs/series/3dcg/20120217_512295.html





- 축소버퍼를 사용하여 Gaussian 필터를 적용한다. 그 이유는 원문에서 보다 시피 여러 표준편차( 표준 편차가 크면 클수록 한 픽셀의 색을 결정하기 위해 많은 픽셀을 Fetch해야 한다. )의 Gaussian 필터를 사용하고 사용하고 싶지만 표준편차가 클수록 부하가 크기 때문에 만들어진 방법이다. 실제로 축소버퍼를 사용한 결과랑 차이가 거의 없게 보인다. 하지만 문제는 너무 작은 렌더 타켓을 Blurring하여 사용하면 픽셀의 계단 현상이 발생하게 된다. 실제로 테스트해보면 Bloom 효과가 많이 일렁이는 부분이 생긴다. 



렌더타켓의 사이즈를 고려하여서 Bloom을 만들면 적절한 효과를 만들어 낼 수 있다.


결과 

MGF 방식을 사용해서 만들면 Bloom이 작은 상황에서 너무 크게 만들어지고 하얗게 타들어가는 느낌이 되는데 이부분이 맘에 안들어서 

Blurring 된 렌더타켓을 누적할때 5장이면 0.2 곱하여 처리해서 위와 좀 다르게 나오게 하였다. 




'Game programing > Shader' 카테고리의 다른 글

Deferred Rendering ( 지연렌더링 ) - FXAA  (0) 2012.11.28
Deferred Rendering ( 지연렌더링 ) - Edge blur  (0) 2012.11.18
Deferred Rendering ( 지연렌더링 )  (0) 2012.11.15
What is shader?  (0) 2012.10.31
Deferred Rendering  (0) 2012.10.22
Posted by jorisma
, |

SSAO를 만드는 방식은 생각보다 여러가지가 있는거 같다. 많은 분들이 구현하셔서 웹상에 공유해주신 덕분에 나도 한번 만들어 봤다.

엔진개발에서 렌더링 관련 쉐이더중 TA님이 필수로 요청한 부분이라 다른 님들의 공유 소스를 보고 분석해서 따라 만들어 봤다.

렌더몽키로 샘플을 만들어 봤다. 렌더몽키라 텍스쳐를 하나만 세팅했더니 이 꼴로 나온다. 


샘플링된 차폐요소를 차폐방향으로 스크린 AO맵을 만들어서 빛의 방향에 대해서 다시한번 Dot연산을 통해 차폐가 있다고 하더라도

빛이 직접적으로 들어오는 곳은 약하게 처리하게 만들었다. 또  Enlighten 처럼 거리에 따라서 AO맵의 적용을 다르게 하는 부분을 

추가하여 멀어 질수록 차폐값을 감쇠하게 만들었다. 하지만 여전히 허접하며 많은 관련 소스를 분석해서 다시 만들어 봐야겠다.


[ 렌더 타겟 ]

Diffuse  - 일차적인 전역 조명 처리를 한 버퍼



SSAO - 차폐에 대한 정보를 기록한 버퍼 



- 가우시안 필터링 적용 X축



- 가우시안 필터링 적용 Y축



[ 최종 이미지 ]





[ 문제점 ]



halo 현상이 심각하게 발생하고 있다.

구현하면서 뭔가 놓친 부분이 있는거 같기도 하고 아니면 방안을 조금 고민해서 덜하게 만들고 싶다.


http://www.gamedev.net/page/resources/_/technical/graphics-programming-and-theory/a-simple-and-practical-approach-to-ssao-r2753 

CryTech 문서에 보면 self-Occlusion이 발생하여 halo가 발생한다고 한다. 맞는 소리긴 한데 이상한건 CryTech에서 설명한 방법대로 하면 halo가 거의 일어나지 않는다는데 Crysys2에서 많이 발생하던데..



























Posted by jorisma
, |

간단한 에셈블리어 예제를 통해서 스택의 변화를 확인하며 구조를 이해하게 한다.

프로시저와 스택구조.pdf


Posted by jorisma
, |

메모리의 효율적인 관리와 사용을 위한 세그먼트와 페이징 기법

실제 메모리 접근 과정의 간략한 설명


실제 메모리의 값을 접근하기 위해서는 위에서 보여지는 작업이 이루어진다. 만약에 코드를 읽어야 한다면 Segment Selector라고 있는 부분이 CS(Code Segment)레지스터가 되며 Offset IP(Instruct Pointer )가 된다. CS레지스터를 Segment Selector라고 부르는 이유는 CS레지스터가 Descriptor 테이블의 인덱스만 가지고 있는 것이 아니라 다른 정보를 가지고 있기 때문이다. 그 외에도 Segment Descriptor를 요구 할 때의 특권 등이 들어가 있다. CS의 상위 13bit Descriptor 테이블의 인덱스가 되며 그렇게 가져온 Segment Descriptor 64bit의 세그먼트 정보가 들어가 있다. 일차적으로 Segment Base Address가 여러 비트열로 나눠 저장되어있으며 Segment Base Address와 위에서 말한 IP가 더해지며 선형 메모리 공간(Linear Address Space)의 주소가 된다. 거기서 나온 값은 또다시 PageDir, PageTable, Offset으로 구성되어있는데 위의 PageDir의 시작주소는 CR3레지스터가 가지고 있으며 PageDir의 값은 인덱스가 된다. 그 값은 다시 테이블의 시작주소가 되며 PageTable은 다시 인덱스가 된다. 거기서 나온 값은 실제 메모리 주소의 4Kb의 페이지의 시작주소가 되며 Offset을 더해 접근해야 하는 실제 메모리 주소가 된다.

 

 

세그먼트 레지스터(Segment Selector)


세그먼트 레지스터는 위의 그림과 같이 구성되어 있어서 세그먼트 셀렉터라고 불린다. 세그먼트 디스크립터의 인덱스 영역이 13bit, TI 1bit, RLP은 디스크립트를 통해 요구할 때의 특권레벨을 3bit를 사용한다. 개요에서 설명되어 있는 부분을 더 붙이면 GDT(Global Descriptor Table) LDT(Local Descriptor Table)을 선택할 기준이 TI값이 0이면 GDT 1이면 LDT가 선택된다.



세그먼트 디스크립터(Segment Descriptor)

세그먼트 레지스터를 통해서 세그먼트 디스크립터를 가져오며 64bit의 비트열로 구성되어 있으며 여러 플래그값과 Segment Base Address를 포함하고 있다.

 

 

 

Base두 부분으로 나눠져 있으며 합치면 32bit Segment Base Address가 된다.              

Limit두 부분으로 나눠져 있으며 합치면 20bit가 된다. 이것은 세그먼트의 허용하는 메모리 범위를 지정하게 하는데 20bit 1Mb밖에 지정할 수 없게 된다. 여기서 G 플래그가 사용되는 데 G 프래그가 1로 세팅되면 기본단위가 4Kb가 되며 4Gb를 모두 지정할 수 있게 된다. 즉 호환을 위해서 이렇게 처리되는 부분이다.

S(System) – 44번 비트인 S는 시스템 비트라고 하여 코드 세그먼트, 테이터 세그먼트, 스택 세그먼트 등으로 사용된다.

다른 플래그는 시스템 프로그래밍에 크게 중요하지 않아 생략한다.  


Offset

세그먼트 레지스터

Offset

CS( Code Segment )

IP

SS( Stack Segment )

SP, BP

DS( Data Segment )

AX, BX, CX, DX

ES( Extra Segment )

DI






각 세그먼트에 해당하는 Offset이 정해져 있다. Offset 또한 범용 레지스터를 사용해서 저장되며 어떤 스레드가 대기상태에서 실행상태로 넘어갈 때 이런 레지스터 정보를 프로세서에 넣어주게 된다.

세그먼트 기법

8086 아키텍쳐에서는 16bit 프로세스를 사용하지만 1Mb 메모리 주소를 접근하기 위해 만들어졌다. 세그먼트 레지스터의 Base Segment Address 4bit 시프트 연산을 한 뒤 Offset을 더하여 만들어진 20bit 주소를 만들어낸 것이다. 32bit 시스템이 일반화된 현재의 CPU 아키텍쳐에서는 불필요하게 보일지라도 시스템의 호환성과 다른 여러가지 정보를 사용하기 위해 지금까지 사용된다.세그먼트로 나누는 것은 위에서 보는 바와 같이 코드영역과 스택 등의 여러 영역을 일괄적으로 할당하고 사용되어지기  보다 유연하게 필요한 만큼 할당하여 사용되길 원했기 때문이다. 하지만 세그먼트로 나누어지면서 실제 메모리는 단편화를 피할 수 없다.

 

 

페이징 기법


세그먼트 기법과 페이징 기법을 통해서 가상 주소를 구현하였다. 실제 응용프로그램에서 제공되는 4Gb 메모리는 가상메모리며 그것을 구현하기 위해 세그먼트와 페이징 기법이 사용된다. 세그먼트로만은 메모리의 단편화 문제를 해결할 수 없으며 페이징을 같이 사용하여 응용프로그램의   메모리 요청을 그 시점에 물리주소를 매핑한 가상 메모리주소를 통해서 처리하게 된다. 이렇게 함으로써 응용프로그램에서는 연속되어있는 메모리 주소를 얻어 사용할 수 있지만 실제 메모리 주소는 그렇지 않을 수 있다. 메모리를 사용하기 위해 운영체제에 요청하면 적당한 위치에 실제 메모리를 할당하고 그것을 매핑하여 페이지를 만들게 된다. 그렇게 되면 사용자는 실제 메모리에 대해서 고민하며 설계하지 않아도 되고 운영체제는 페이지 관리에 집중할 수 있다. 만약 실제 물리적 메모리 사이즈가 2Gb이라면 나머지 2Gb는 하드디스크에 저장된다. 만약 페이지 사이즈가 2Gb이고 읽어오려고 하는 데이터가 하드디스크에 있다면 메모리에 있는 데이터를 다시 하드디스크로 옮기고 디스크에 있는 데이터를 메모리에 올리는 작업이 이루어진다. 이것은 엄청난 오버헤드이므로 페이지의 사이즈를 적정사이즈인 4Kb로 정해졌다메모리에 올라와있는 페이지인지 확인 후 없을 시 페이지 fault가 돌아오며 다시 페이지를 검색하여 디스크에서 가져오게 된다. 어떤 페이지를 올리고 내리는 결정은 OS에서 처리되며 이 페이지 교체방법은 이 시스템의 효율을 결정하게 된다

  

'Game programing > System Programing' 카테고리의 다른 글

Stack structure(스택구조)  (0) 2012.11.29
Posted by jorisma
, |

결국 AA를 직접 만들어서 사용하고 싶었는데 그냥 블러를 먹이면 되는줄 알았는데 알고 보니 그런 단순하게 해서는 퀄리티가 좋지

못해서 결국 회사형에게 물어가며 FXAA를 적용해 보았다. Deferred를 구현한 샘플 소스에다가 그냥 소스 받아와서 테그닉을 추가해서

구겨 넣었다. 아래 내용은 PDF에서 발췌한 것인데 대충 이해한 내용을 정리한다. 맞는지는 확실지 않다 ㅋㅋㅋ 나중에 소스 보고 확실히

정리하야겠다 .


 


FXAA는 인접해 있는 4개의 픽셀 인접값( 위에 그림에서 보면 NW, NE 등은 두개의 픽셀이 만나는 점을 뜻한다.) 를 통해서 휘도( luminance )가 가장 높은 값을 저장하고 픽셀쉐이더로 들어온 M 픽셀의 휘도를 포함한 휘도값들에서 최대값과 최소값을 뺀다.

이것은 대비( contrast )가 되며 이 대비값이 많이 발생하면 AA를 필요하다고 처리된다. 


위와 같은 방식으로 휘도값들의 차이를 통해 이차원 좌표계를 만들고 방향성을 통해서 블러( 여기서는 scale 값 )를 사용한다. 

저런식으로 처리하면 좀 될거 같다. 하지만 소스 자체를 보고 좀 더 연구해봐야 겠다.



실제 적용 모습


< Non AA >



< FXAA 적용 >

'Game programing > Shader' 카테고리의 다른 글

HDR + Bloom  (0) 2013.10.28
Deferred Rendering ( 지연렌더링 ) - Edge blur  (0) 2012.11.18
Deferred Rendering ( 지연렌더링 )  (0) 2012.11.15
What is shader?  (0) 2012.10.31
Deferred Rendering  (0) 2012.10.22
Posted by jorisma
, |

최근에 달린 댓글

최근에 받은 트랙백

글 보관함