[GPU프로그래밍] 9. Image Processing and Screen Space Techniques 2

jungizz_·2024년 4월 20일
0

GPU Programming

목록 보기
9/15
post-thumbnail
post-custom-banner

◾ Multisample anti-aliasing (MSAA)

Anti-aliasing

  • edge가 픽셀에 걸쳐졌을 때, 픽셀 중심의 한 점을 기준으로 컬러를 결정하며 생기는 계단효과를 없애는 것
  • 경계 부분에서 각 픽셀마다 여러개의 점을 sampling하고, sample들의 픽셀값을 평균내서 컬러 결정
  • OpenGL에서 MSAA를 지원하지만, 개발자가 크게 관여할 순 없고 활성화/비활성화 및 sample 개수 정도만 설정할 수 있음 (하지만 속도가 느림)
    • window system API와 관련 (GLFW)
    • 시스템마다 지원해주는 최대 샘플 개수가 다를 수 있다 (확인 가능)
  • sampling했다고해서 sample 개수만큼 fragment shader가 실행되지 않음
  • 원래는 배경에 해당하는 픽셀은 fragment shader가 적용되지 않는데, MSAA에서는 배경에 해당하는 픽셀이라도 그 픽셀의 sample이 오브젝트 안에 있다면 fragment shader가 적용된다
  • but, 오브젝트 안에 있는 sample은 픽셀의 중앙이 아니지만, fragment shader의 input인 vertex shader의 output은 각 버텍스의 값을 픽셀의 중심을 기준으로 Interpolation하여 넘겨주기 때문에.. 픽셀 중심을 기준으로 한 값을 가지게 됨
    -> Polygon 바깥의 값을 가지게 되며 아래와 같은 문제가 생길 수 있음
  • sample의 위치과 fragment shader의 값이 계산되는 위치가 일치하지 않아 생기는 문제

centroid/sample qualifier

  • 위의 문제점을 보완

centroid qualifier

  • polygon 안쪽을 기준으로 interpolation 해라
// vertex shader
centroid out vec2 TexCoord;

// fragment shader
centroid in vec2 TexCoord;

sample qualifier

  • sample을 기준으로 interpolation 해라
  • 각 sample마다 fragment가 실행되야하므로 느림
// vertex shader
sample out vec2 TexCoord;

// fragment shader
sample in vec2 TexCoord;
  • 퀄리티는 centroid < sample
  • 속도는 centroid > sample

◾ Deferred shading

  • 첫 pass에서 렌더링 후, depth test를 통과한 geometry 정보(color, normal, pos등)를 texture 형태로 저장 (Geometry buffer에 저장GBuffer)
  • 이후 pass에서 GBuffer의 데이터만을 사용하여 lighting/shading 연산
  • 보이는 부분에 대해서만 정보가 저장되므로 연산 절약

code

1. OpenGL Application 세팅 과정

  • FBO 생성 (이 예제에서는 1pass, 2pass의 결과를 한 FBO에 모두 담는다..)
  • Position, Normal, Color 버퍼 생성 및 FBO에 연결 (프레임버퍼에 채널을 지정해서 저장)
    -> 1pass에서 저장할 3개의 정보를 3개의 texture에 담기 위해 3개의 output
  • fragment output을 관리하는 배열에 GL-NONE(2pass 결과)을 포함한 3개의 프레임버퍼 채널을 등록하여 output 설정)

2. Fragment shader

  • pass1에서는 geometry 정보를 내보내 texture에 저장
  • pass2에서는 그 texture를 받아 texture로부터 geometry 정보를 읽어온다!!
    • 원래는 pos, normal 정보를 vertex shader에서 변환된 output을 받아서 사용하는데, deferred shading에서는 texture에서 정보를 받아옴

3. OpenGL Application 렌더링 과정

  • pass1: FBO 바인딩 및 depth test 활성화, uniform변수 넘겨주기, 오브젝트 그리기
  • pass2: default FBO로 설정하고 depth test 비활성화, 쿼드 그리기

👀 전체 흐름... (보라색 선)

  • deferred shading으로 Multi-sample anti-aliasing 가능 GL_TEXTURE_2D_MULTISAMPLE
  • 텍스처로 저장된 depth 정보를 활용하여 이미지 프로세싱 가능
    (depth of field(=depth blur), screen space ambient occlusion, volumetric particels, ...)
  • but, 보이는 것만 계산하기 때문에 투명한 것 다룰 수 X (blending, transparency)

◾ Screen space ambient occlusion

Ambient occlusion

  • Ambient light는 환경광으로 주변에서 균일하게 들어오는 빛
    • 하지만 가려진다면 (occlusion) 상대적으로 적은 빛을 받아 어두워진다
      (아래 그림의 화살표를 반대로 생각하자..)
    • 광원 없이 ambient occlusion만 사용해서 렌더링하면, 사방으로 균일하게 빛이 들어와 평평할수록 밝게, occlusion있는 곳은 어둡게 나타난다
    • 이는 형태로만 결정되는 값이라 모양이 변화하지 않으면 바뀌지 않아서 미리 계산해서 만들어 둘 수 있다

ambient occlusion factor

  • 가려진 정도에 따라 ambient occlusion factor 설정
    • 반구로 들어오는 빛 중 반이 가려졌다면 0.5정도..
  • 한 점에서 사방으로 ray를 쏴서(반사벡터) 만나는 점이 있으면 occlusion
  • 물체가 많고 복잡하면 계산이 많아져 real time에는 적절하지 않다..
    • 하지만 물체가 static하다면 미리 계산 가능! (그래도 많다 ㅠ,ㅠ)

Screen Space Ambient Occlusion (SSAO)

  • ambient occlusion을 screen space에서 계산하여 근사
    • SS는 depth test를 통과한 정보만 남아있어 모든 값을 계산하지 않기 때문에 계산 복잡도가 낮아 real time에 사용 가능
  • 2D상에서 3D물체의 occlusion을 찾겠다

Approximation

  • SS가 아닌 경우, 점P를 기준으로 ray를 쏴서 sample들이 보이는지 안보이는지 확인했다
    • 위 그림에서 점들: 반구 위 sample들 (occlusion을 확인하기 위해 ray를 쏘는 방향에 있는 점..)
      • 검정점: occlusion X
      • 하얀점: occlusion O
  • 특정 점이 카메라에서 보인다면, 그 점은 표면에서도 보인다고 가정한다면, 카메라에서 보이는 점은 occlusion 안된 검정점이고, 카메라에서 보이지 않는 점은 occlusion된 하얀점이다.
  • 특정 점이 카메라에서 보이는지 확인하기 위해 depth 사용
    • Sample점의 depth는 CC에서의 z값
    • 그 위치에서 표면의 depth는 depth buffer에 저장된 값
    • 두 값을 비교하여 표면보다 점이 아래에 있으면 occlusion이라고 판단 가능

Random point generation

  • 점P를 기준으로한 반구의 sample들을 랜덤하게 뽑아야함
    • 쉐이더 코드에선 랜덤 연산이 어려우므로, random point들의 set을 저장한 Random kernel을 생성
    • 그렇다고 모든 point마다 같은 sample을 사용하면 패턴이 생길 수 있으니, normal을 기준으로 kernel을 회전시켜 사용한다
      • normal기준 회전 또한 랜덤해야하므로,, random number texture를 만들어서 사용
  • 반구 안에서 random point를 생성하는 함수로 kernSize개의 sample을 뽑아 position값을 kernel에 넣는다
    • sample들은 기준 점과 가까운 곳에 많이 있을 수 있도록 weighting하는 과정 존재
  • 생성한 random kernel을 random rotation하여 재사용
    • 4x4의 아주 작은 텍스처 생성 (random rotation vector 포함)

Code

  • 1pass
    • 렌더링 및 g-buffer로 camera space로 변환된 데이터 저장
  • 2pass
    • 얼마나 가려졌는지 비율을 계산하여 각 픽셀마다의 AO factor 계산
    • random kernel 반구의 z축이 표면의 normal이 되도록 point들의 좌표계 변환 필요
      (1st pass에서의 normal(CC)을 기준으로한 tangent space로 random kernl을 옮겨주는 것)
    • normal nrandDir을 사용해 bitangent와 tangent를 구한다 -> CC기준의 tangent space 생성
      • randDir을 사용했기 때문에, normal 기준 랜덤 회전을 적용시킨 것
    • CC로 변환하는 행렬 생성
    • random kernel의 점들을 변환하고 projection 시켜 tangent coord를 얻는다
    • surfaceZsamplePos.z를 비교하여 Occlusion을 확인하고 비율을 따져 AOfactor 계산
  • 3pass
    • ambient occlusion 데이터의 튀는 값들을 제거하기 위해 simple blur 적용
    • 3x3 unweighted average
  • final pass
    • ambient occlusion으로 라이팅 계산~

Comparisons

  • 3pass에서 블러를 해준 다른 이유:
    • pass2에서 RandTex의 값을 가져올 때, texCoord에 randScale을 곱했는데, 이는 스크린 크기를 다 채울때까지 4x4 texture로 반복했다는 것
    • 이로인해 규칙적인 artifacts가 생길 수 있어서 블러
  • Without/with ambient occlusion

◾ Depth test

  • depth가 더 작은 픽셀만 프레임버퍼에 그린다
  • fragment shader까지 끝나면 depth test를 실행 -> 계산하는게 많다

Early depth test

  • fragment shader 실행 전에 depth test
    (vertex shader까지만 해도 depth는 미리 계산할 수 있으므로)
    • OpenGL pipeline에 계속 early depth test 하라고 명령
  • 하지만, fragment shader에서 depth값을 바꾸는 경우 사용할 수 없다 (ex-discard)
    • fragment shader에서 depth는 수정하지만 early depth test의 장점을 갖고싶다면
    • depth가 커지거나/작아지거나/바뀌지 않을 때만 빼고 early depth test를 해라라고 할 수 있음

◾ Order-dependent transparency

  • depth test로 물체를 그리는 경우, 모든 물체가 불투명하면 문제없지만 투명한 물체가 있는 경우에는 올바르게 그릴 수 없음
  • depth test하여 불투명한 물체를 먼저 그리고,
  • depth test 없이 투명한 물체들을 카메라 기준으로 sorting해서 뒤에서부터 앞으로 그린다 (sorting은 OpenGL Application에서)
  • 하지만, 카메라 달라질 때마다 매번 sorting하면 오래걸릴 수 있고, 물체/폴리곤 단위로 sorting이 애매하여 제대로 안되는 경우가 발생할 수 있음(왼-순서지킴, 오-순서안지킴)

◾ Order-independent transparency (OIT)

  • sorting없이 투명한 오브젝트를 그린다 (그래도 GPU에서는 sorting함)
  • fragment shader에서 픽셀 단위로 sorting
    • 한 픽셀에 그려지는 fragment가 여러개일 때, 이 픽셀에서 그려지는 fragment들만 sorting
    • 근데 이걸 GPU에서 계산하니까 모든 픽셀에 대해 병렬적으로 수행 가능!
  1. 각 픽셀 위치에 어떤 fragment가 그려질 것인지 list 설정
  2. 같은 픽셀 위치에 그려지는 fragment들을 sorting
    -> 물체 단위가 아닌 픽셀 단위 soring

memory objects

  • 3개의 memory object가 필요
  • fragment shader에서 같은 위치에 있는 fragment 데이터를 읽고 쓰기위해 정보를 저장할 버퍼
    • ex) 3x3 프레임버퍼에 대한 linked list
  1. atomic counter
    • linked list 버퍼의 크기 저장
  2. head pointer texture
    • linked list들의 head pointer값을 가질 텍스처 버퍼
    • screen size와 동일한 head-pointer texture
  3. linked list buffer
    • 각 노드는 한 fragment에 해당
    • 그 fragment의 color/depth값과 다음 fragment의 index값을 가짐

Implemention

OpenGL Application 세팅 과정

  • 3개의 메모리 오브젝트와 초기화를 위한 clear Buffer 생성
  • 매 씬마다(프레임마다) 메모리 오브젝트를 초기화해줘야하는데, head pointer texture를 초기화 하기 위해 사용할 clearBuf
    • clearBufheadPtrTex를 binding해서 초기화

Fragment shader

  • early depth test를 하는 이유는, 어짜피 불투명한 물체는 미리 그려놨기 때문에 불투명한 물체에 가려진 투명한 물체는 그릴 필요가 없기 때문
  • 1st pass
    • 3D씬 그리기 (이 코드에서는 불투명 오브젝트 그리는건 스킵함)
    • atomic counter의 값을 증가시키고, 현재 위치의 head pointer texture 값을 전 atomic counter값으로 수정
    • linked list에 node 추가
  • 1pass와 2pass 사이에서
    • 모든 데이터가 다 쓰일 때까지 다음 pipeline으로 넘어가지 않도록, OpenGL Application에서 모든 데이터가 버퍼에 쓰여지는 것을 보장하는 함수 사용
      (Linked list가 완성된 뒤에 2nd pass로 넘어가야한다는 뜻~)
  • 2nd pass
    • 현재 위치의 fragment에 해당하는 linked list만 사용하기 위해, 현재 위치의 head pointer값을 사용해 linked list의 head node에 접근
    • 그 head node의 next값을 따라가며 linked list를 다른 배열에 copy (마지막 노드까지)
    • depth에 따라 fragment insertion sort 진행
    • 정렬된 fragment들을 하나씩 읽어오면서 배경 color와 mix를 반복해서(color recursive) 최종 컬러 계산
  • 모든 fragment shader가 같은 버퍼를 병렬로 처리하기 때문에, 같은 위치에 동시에 node를 저장해서 충돌될 수 있다 -> 방지 필요
  • 방지하면 느려질 수 밖에 없다 (수행이 멈춰지니까)
  • 설계를 잘 해야한다!

걍 적어둔건데 아까워서

1st pass

  • 3D씬 그리기
  • 각 픽셀에 매핑될 fragment들의 linked list 만들기

2nd pass

  • 모든 픽셀에 대해서 fragment shader로 수행시키기 위해 full screen quad 그리기
  • fragment shader에서
    • Head Pointer texture의 본인 자리의 head pointer값을 얻어와서
    • 자기가 해당하는 위치의 정보를 가진 linked list buffer에 접근해서 fragment를 sorting
  • fragment shader에서 linked list를 얻어 depth에 따라 fragment를 sorting
  • sorting해서 큰것부터 작은순으로 그리고, blend
profile
( •̀ .̫ •́ )✧
post-custom-banner

0개의 댓글