IT 이모저모

고해상도 디스플레이 및 콘솔 게임을위한 렌더링 아키텍처

exien 2018. 4. 9. 09:54
"Triangle Visibility Buffer"는 2015 년 9 월부터 Confetti의 연구 프로젝트입니다.이 블로그 항목은 현재 상태를 간략하게 설명합니다.
이 렌더링 기술은 전체 렌더링 파이프 라인을 통해 삼각형을 추적하고 버퍼의 장면에있는 모든 불투명 삼각형의 가시성을 저장하기 때문에 Triangle Visibility Buffer라고 부릅니다. 이 기술은 렌더링 데이터를 저장하는 고속 메모리의 양이 제한되어 있지만 큰 해상도를 지원해야하는 대상 하드웨어 플랫폼에 매우 적합합니다. 또한 4k, 5k 또는 8k 해상도의 고해상도 디스플레이에서 렌더링 할 때 가장 적합합니다. 
이 블로그 항목과 함께 제공되는 소스 코드는에서 찾을 수 있습니다.




이 저장소에는 PC DirectX 12 / Vulkan과 macOS / Metal 2 구현이 있습니다. 요청시 다양한 콘솔 플랫폼 용 소스 코드도 있습니다. 다음의 텍스트는, 일관성을 유지하기 위해서 DirectX 12 구현만을 참조하고 있습니다. 코드베이스에서 Vulkan과 macOS를 찾는 것은 독자에게 맡겨져 있습니다. 

Triangle Visibility Buffer 렌더링 파이프 라인의 데이터 흐름에 이어 첫 번째 단계는 데이터 세트에서 보이지 않는 삼각형을 제거하는 삼각형 제거 단계입니다.


다중 뷰 삼각형 클러스터 컬링 / 삼각형 필터링

폴리곤 수는 게임에서 매년 증가합니다. 비어있는 드로우 호출이 생성 될 경우 명령 프로세서에서 하드웨어가 병목 될 수 있습니다. 정점 수를 가진 정점 셰이더에서 뒤쪽면 제거 및 클리핑과 함께 또는 래스터 라이저에서 병목 현상이 발생할 수 있습니다. 보이지 않는 작은 삼각형은 기본 경계 . 
일반적으로 그래픽 파이프 라인으로 들어가는 삼각형 수를 줄이기 위해 삼각형 제거 단계가 전체 렌더링 시스템의 첫 번째 단계로 추가됩니다. 이 방법을 사용하면 그래픽 파이프 라인을 가시적 인 삼각형으로 더 잘 활용할 수 있습니다.
삼각형 제거 (triangle removal)는 새로운 개념이 아니며 이미 10 년이나 이미 더 긴 플랫폼에서 활용되었습니다. 현대 게임의 삼각형 복잡성으로 인해 현대 하드웨어가 그로부터 이익을 얻는 것처럼 보이기 때문에 [Chajdas] [Wihlidal]와의 협상에서 최근 몇 년간 부활했습니다. 
데모에서 삼각형을 제거하는 데 사용되는 기술은 다음 연속 단계로 구성됩니다. 
- 클러스터 컬링 : CPU에서 비슷한 방향으로 256 개의 삼각형 그룹을 컬합니다. 
- 삼각형 필터링 : 비동기 컴퓨팅 쉐이더에서 개별 삼각형을 개별적으로 컬링합니다. 
- 콜 압축을 그립니다. 삼각형이없는 빈 그리기 호출을 제거하고 나머지 그리기 호출을 계산 쉐이더에서 순서대로 정렬합니다.

데모 구현은 메인 카메라 뷰와 섀도우 맵 뷰에 대해 위의 모든 것을 동시에 수행합니다. 이 다중 뷰 삼각형 제거라고합니다.



CPU에서 삼각형 클러스터 컬링  

삼각형 클러스터 컬링 -이 경우 CPU에서 실행 - 비슷한 방향으로 256 삼각형의 보이지 않는 덩어리를 제거합니다. 이것은 메쉬의 처음 256 개의 삼각형을 선택하고 가시성 테스트 콘에 대해 테스트함으로써 수행됩니다. 다음 이미지 1에서 삼각형과 그 삼각형의면 법선은 주황색 선으로 표시됩니다.



이미지 1 - 삼각형 클러스터 컬링 - 테스트 콘


밝은 파란색 삼각형이 테스트 콘입니다. 이 테스트 콘 내에 눈이나 카메라가있는 경우, 삼각형은 보이지 않는 것으로 간주됩니다. 콘의 중심을 찾기 위해, 우리는 이미지 2와 같이 삼각형 클러스터의면 법선을 부정적으로 누적하는 것으로 시작합니다.

이미지 2 - 삼각형 클러스터 컬링 - 원뿔 중심을 찾기 위해 음의 누적 삼각형


콘 개방 각을 계산하기 위해 가장 제한적인 삼각형 평면은 이미지 3과 같이 삼각형 클러스터에서 가져옵니다. 

이미지 3 - 삼각형 클러스터 컬링 - 테스트 콘 플레인 계산


카메라 또는 눈이 테스트 콘의 영역에 있으면 삼각형 클러스터가 보이지 않으므로 제거 할 수 있습니다. 
이 간단한 클러스터 컬링 메커니즘의 유효성은 장면 데이터에 따라 다릅니다. 비슷한 방향으로 향하는 많은 삼각형이있는 경우 삼각형이 다른 방향을 향하고있는 경우 더 효율적입니다. 
Visibility Buffer 데모에서 테스트 장면 -San Miguel-은 비슷한 방향을 향하는 삼각형의 클러스터가 많지 않으므로 삼각형 클러스터 컬링은 그리 효율적이지 않습니다. 이는 하드웨어로 테셀레이션되는 지오메트리와 다를 수 있습니다. 기본적으로 삼각형 클러스터 컬링은 데모에서 꺼지는 효율성이 낮기 때문입니다. 
클러스터 컬링 코드는 Visibility_Buffer.cpp 에서 찾을 수 있습니다., 거기에서 triangleFilteringPass ()를 호출 한 다음 cullCluster ()를 호출합니다.


GPU에서 삼각형 필터링

보이지 않는 삼각형을 제거하기 위해 비동기 컴퓨팅 쉐이더가 각 삼각형에서 실행됩니다. 하나의 배치에서 256 개의 삼각형을 실행하고 삼각형이
  • 감산하다 
  • 뒤 향함 
  • 뷰 frustum의 가까운 클리핑 평면을 통해 클립합니다. 
  • 뷰 프러스 텀을 통과하거나 뷰 파인더의 바깥쪽에 있음 
  • 픽셀 중심이나 샘플링 포인트를 커버하기에는 너무 작습니다. 
모든 테스트를 통과 한 삼각형이 인덱스 버퍼에 추가됩니다. 이 인덱스 버퍼는 필터링 된 인덱스 버퍼라고합니다. 
이 섹션의 모든 소스 코드는 쉐이더 triangle_filtering.comp 에서 찾을 수 있으며 그 안에는 FilterTriangle () 함수가 있습니다.


삼각형 변형

두 개의 삼각형 인덱스가 같으면 삼각형이 축퇴됩니다. 즉, 세 개의 꼭지점이 동일 선상에 있고 선상에 있습니다.  경우 삼각형 영역은 제로이므로 제거해야합니다.


// triangle_filtering.shd에서
#if ENABLE_CULL_INDEX
if (indices [0] == indices [1]
    || 인덱스 [1] == 인덱스 [2]
    || 인덱스 [0] == 인덱스 [2])
{
    컬 = 사실;
}
#endif
Degenerate 삼각형은 하드웨어 테셀레이션 또는 아트 자산 파이프 라인에 버그가있는 경우에 도입 될 수 있습니다.


뒤 향한 삼각형


뷰어에서 멀어지는 삼각형은 보이지 않으므로 선택해야합니다. 이 구현은 [Olano]가 설명하는 기술을 사용합니다. 그는 삼각형의 세 꼭지점으로 구성된 3x3 클립 공간 행렬의 행렬식을 계산합니다. 행렬식이 0보다 큰 경우 역행렬이 없기 때문에 역행렬이며 추려 질 수 있습니다. 
이미지 3 - 후면 컬링


#if ENABLE_CULL_BACKFACE
// 동질적인 좌표에서 도려내 기
// 읽기 : "2D 동차 좌표를 사용하여 삼각형 스캔 변환"
// Marc Olano, Trey Greer
// http://www.cs.unc.edu/~olano/papers/2dh-tri/2dh-tri.pdf
float3x3 m = float3x3 (정점 [0] .xyw, 정점 [1] .xyw, 정점 [2] .xyw);
if (cullBackFace)
  컬 = 컬 || (결정자 (m)> 0);
#endif


뒤면 컬링은 잠재적으로 지오메트리의 50 %를 제거 할 수 있습니다.


가까운 비행기 클리핑

시야 절두체의 가까운 클리핑 평면 앞에있는 삼각형을 제거해야합니다. 체크하기 위해, 삼각형이 니어 클리핑면의 앞에 있으면, 다음 코드는 각 꼭지점의 w 성분이 0 아래인지를 검사합니다. 이것이 사실 일 경우, w 컴포넌트를 뒤집어 스크린의 양면에 투영되지 않도록합니다.
for (uint i = 0; i <3; i ++)
{
if (vertices [i] .w <0)
{
++ verticesInFrontOfNearPlane;
  // 평면을   넘어 다니는 삼각형이 화면 양쪽면에 투영되지 않도록 w를 뒤집습니다.
  vertices [i] .w * = (-1.0); } ... }


삼각형의 세 정점이 모두 가까운 클리핑 평면 앞에 있으면 삼각형이 제거됩니다.


if (verticesInFrontOfNearPlane == 3)
참을 돌려라.



푸스 투움 컬링

정점이 모두 클립 공간 큐브의 음수쪽에있는 삼각형은 뷰 프러스 텀 외부에 있으므로 추려 낼 수 있습니다. 

다음 이미지 4는 산 미구엘 장면의 테이블 가까이에 위치한 카메라와 절두체 컬링 이후의 나머지 삼각형을 보여줍니다.

이미지 4 - 삼각형 추론 컬링
GitHub 저장소의 데모 코드를 사용하면 삼각형 절두체 컬링을 정지 한 다음 카메라를 멀리 이동하여 결과를 볼 수 있습니다. 
클립 공간 큐브에 대한 비교를보다 효율적으로 수행하기 위해 삼각형의 모든 꼭지점이 먼저 표준화 된 0..1 공간으로 변환됩니다. 
...
vertices [i] .xy / = vertices [i] .w * 2;
vertices [i] .xy + = float2 (0.5, 0.5);
...
삼각형의 어떤 꼭지점이이 0 .. 1 범위 밖에 있으면, 그것은 추려 질 것입니다.
...
float minx = min (min (vertices [0] .x, vertices [1] .x), vertices [2] .x);
float miny = min (min (vertices [0] .y, vertices [1] .y), vertices [2] .y);
float maxx = max (max (vertices [0] .x, vertices [1] .x), vertices [2] .x);
float maxy = max (max (vertices [0] .y, vertices [1] .y), vertices [2] .y);


if ((maxx <0) || (maxy <0) || (minx> 1) || (miny> 1))
참을 돌려라.
...


작은 기본 컬링


삼각형은 투영 후 픽셀 중심이나 샘플 점과 겹치지 않을 경우를 대비하여 너무 작다고 간주됩니다. 그럼에도 불구하고, 래스터 라이저는 보이지 않는 삼각형을 다루는 사이클을 소비 할 수도 있습니다 (일부 GPU는 타일 당 사이클 당 하나의 프리미티브 만 처리 할 수 ​​있음). 이는 낭비되는 노력입니다. 이것이 그래픽 파이프 라인에 충돌하기 전에 작은 삼각형을 제거해야하는 이유입니다.
다음 이미지는 샘플링 포인트 사이의 매우 작은 삼각형을 보여줍니다.
이미지 5 - 작은 기본 컬링


다음의 작은 삼각형 테스트 용 코드는 삼각형 주위의 스크린 공간에 경계 상자를 생성 한 다음이 경계 상자의 x 및 y 값을 사용하여 MSAA의 경우 픽셀 중심 또는 샘플링 지점과 겹치는 지 확인합니다. 
// 중심에서 msa 샘플까지의 거리에 따른 축척
int2 screenSpacePosition = int2 (screenSpacePositionFP * (SUBPIXEL_SAMPLES * samples));
minBB = min (screenSpacePosition, minBB);
maxBB = max (screenSpacePosition, maxBB);


if (any ((minBB & SUBPIXEL_MASK)> SUBPIXEL_SAMPLE_CENTER) &&
((maxBB - ((minBB & ~ SUBPIXEL_MASK) + SUBPIXEL_SAMPLE_CENTER)) <                        (SUBPIXEL_SAMPLE_SIZE))))
{
참을 돌려라.
}



다중 뷰 삼각형 제거

삼각형 제거는 모든 삼각형에 대해 인덱스 및 꼭지점 데이터를로드하고 정점을 변환 한 다음 나중에 필터링 된 인덱스 버퍼에 삼각형 데이터를 추가하는 비용입니다. 삼각형 데이터에 액세스하는 비용은 가시성 테스트를 실행하는 데 드는 비용보다 높은 것으로 보입니다. 
장면의 삼각형 수가 많으면이 비용은 최신 GPU의 이득으로 상쇄해야합니다. 
이 비용을 상각하는 한 가지 방법은 주 카메라보기, 그림자지도보기, 반사 그림자지도보기 등의 여러보기에 대해 보이지 않는 삼각형을 동시에 제거하는 것입니다.
즉, 주 카메라 뷰에서는 삼각형이 보이지만 그림자 맵 뷰에서는 삼각형이 보이지 않으면 두 뷰 모두에서 볼 수있는 것으로 간주됩니다. 다시 말해, 보이는 삼각형의 나머지 세트는 모든 뷰 사이의 최소 공통 분모가됩니다. 삼각형 제거의 효율성을 감소시킵니다. 
다중 뷰 삼각형 제거 단계에서 제거 된 전체 삼각형 수는 각 뷰에 대한 삼각형을 개별적으로 제거하는 것과 비교하여 더 작지만 삼각형 데이터를 한 번만로드하면 성능이 크게 향상됩니다. 
다음은 여러보기에 대해 FilterTriangle ()을 실행하는 triangle_filtering.comp 의 소스 코드입니다 .
 
for (uint i = 0; i <NUM_CULLING_VIEWPORTS; ++ i)
{
float4x4 worldViewProjection = uniforms.transform [i] .mvp;
float4 vertices [3] =
{
mul (worldViewProjection, vert [0]),
mul (worldViewProjection, vert [1]),
mul (worldViewProjection, vert [2])
};


CullingViewPort 뷰포트 = uniforms.cullingViewports [i];
cull [i] = FilterTriangle (인덱스, 정점,! twoSided, viewport.windowSize, viewport.sampleCount);
if (! cull [i])
InterlockedAdd (workGroupIndexCount [i], 3, threadOutputSlot [i]);
}



다중 뷰 삼각형 제거 - 결과

데모에서 사용 된 San Miguel 장면은 약 800 만 개의 삼각형을 가지고 있습니다. 데모가 기본 카메라보기에서 시작되면 (다중보기 삼각형 제거 후), 섀도우 맵보기의 필터링 된 색인 버퍼는 1.843 백만 개의 삼각형을 색인화하고, 기본보기의 필터링 된 색인 버퍼는 2.321 백만 색인 삼각형.
이미지 6 - 가시성 버퍼 데모의 기본 시작보기

여기에 설명 된 삼각형 제거 또는 유사한 접근법은 이제 차세대 렌더링 시스템의 모든 주요 개발자가 사용하며 미래의 그래픽 API 디자인은이 아이디어를 포착하고 더 많은 형상 처리를 향상시킬 수 있습니다. 
Confetti에서 우리는 StarVR SDK에서이 가시성 버퍼 구현과 다중 뷰 삼각형 제거를 사용합니다. StarVR은 매우 높은 해상도와 210 도의 FOV를 갖춘 아케이드 VR 헤드셋입니다. 이 넓은 FOV는 여러 뷰포트로 덮여 야합니다. 네 개의 뷰포트를 사용한다고 가정 할 때, 하나의 삼각형에서 모든 4 개의 삼각형을 컬링하는 것은 비동기 컴퓨팅 쉐이더를 컬링 (culling async compute shader)하여 중요한 성능을 얻는 것으로 나타났습니다. 
우리는 또한 높은 삼각형 수를 가진 Unreal Engine 4 기반 게임에서이 기능을 활용하기 시작했습니다.


통화 압축 압축하기

삼각형 필터링을위한 비동기 연산 쉐이더는 위에서 설명한대로 256 삼각형의 배치에서 실행됩니다. 보이는 삼각형을 "필터링 된 인덱스 버퍼"에 추가하여 보이지 않는 모든 삼각형을 제거한 후에는 일부 계산 쉐이더 배치가 비어있을 수 있습니다.

  1. 배치 0 - 인덱스 시작 : 0 | 지수의 수 : 12
  2. 배치 1 - 인덱스 시작 : 12 | 색인 수 : 256 
  3. 배치 2 - 인덱스 시작 : 268 | 지수의 수 : 120
  4. 배치 3 - 인덱스 시작 : 388 | num of indices : 0 (빈 배치) 
이 목록에서 Batch3은 비어있게됩니다. 
삼각형은 계산 쉐이더 일괄 처리뿐만 아니라 그리기 호출에도 속합니다. 일괄 처리 된 삼각형 세트가 반드시 그리기 호출 세트와 겹치지는 않습니다. 빈 계산 쉐이더 배치가 없더라도 삼각형 제거 후 빈 그리기 호출이있을 수 있습니다. 이 데모에서는 드로우 콜이 보유하고있는 인덱스의 수를 확인하여 콜백이 비어 있는지 확인할 수 있습니다. 
이 경우 빈 draw 호출을 제거하고 나머지 draw 호출을 순차적으로 정렬하여 ExecuteIndirect에서 제대로 실행되도록 정렬 할 수 있습니다. 이미지 7은 컬링 테스트로 제거 된 삼각형에서 나온 흐름을 압축해야하는 호출을 그리는 방법을 보여줍니다.


이미지 7 - 콜 컴팩 션 그리기




draw call compaction의 소스 코드는 batch_compaction.comp 에서 찾을 수 있습니다 
. 하이 레벨 뷰에서 Triangle Visibility Buffer 렌더링 시스템은 지금까지 다음 단계를 거쳤습니다.

  • [CPU] 조기 폐기 기하학은 클러스터 컬링을 사용하여 모든보기에서 볼 수 없습니다. 
  • [CS] N 개의 뷰 (계산 쉐이더 스레드 당 하나의 삼각형)에 대해 삼각형을 컬링 및 필터링하여 N 개의 인덱스 및 N 개의 ExecuteIndirect 버퍼 생성 
  • [CS] 그리기 전화 압축 
  • 각각에 대해 i 번째 인덱스 버퍼와 i 번째 간접 인수 버퍼를 사용합니다. 
사용을 위해 최적화 된 모든 그리기 데이터를 사용하여 다음 단계에서 실제 가시성 버퍼를 ExecuteIndirect로 채 웁니다.

가시성 버퍼 채우기 - ExecuteIndirect

Triangle Visibility Buffer는 [Burns] [Schied]와 비슷한 8 : 8 : 8 : 8 렌더 타겟의 삼각형 데이터에 인덱스를 저장합니다. 인덱스는 압축 된 32 비트 값으로 구성됩니다.
  • 1-bit Alpha-Masked 
    데모에서, 1 비트는 도형이 알파 마스킹을 필요로하는지 여부에 관한 정보를 가지고 있습니다. PC에는 각각에 대해 고유 한 ExecuteIndirect가있는 전용 코드 경로가 있습니다. 
  • 8 비트 drawID - 간접 그리기 호출 ID 
    8 비트 값은 삼각형이 속한 그리기 호출의 ID를 나타냅니다. 이 구현에는 256 개 이상의 그리기 호출을위한 공간이 있으며 이는 충분히 충분합니다. 
  • 23 비트 triangleID 
    23 비트 값은 draw 호출 내부의 삼각형 오프셋을 설명하는 id를 포함합니다. 즉, drawID를 기준으로합니다. 
이 데이터를 담고있는 렌더 타겟은 깊이 버퍼와 함께 ExecuteIndirect 호출로 채워진다. 
모든 ExecuteIndirect 호출은 다양한 재질을 적용하는 데 사용되는 정점 버퍼, 인덱스 버퍼 및 재질 버퍼를 읽습니다. 
4 개의 다른 정점 버퍼가 있습니다.

  • 위치 값 (삼각형 제거, 가시성 버퍼 채우기, 음영 처리에 사용됨) 
  • 텍스처 좌표 (알파 테스트를 거친 기하학, 음영으로 가시성 버퍼 채우기) 
  • 법선 (음영) 
  • 탄젠트 (음영) 
버텍스 데이터를 네 개의 버퍼 (비 인터리브 버텍스 데이터라고도 함)로 분리하면 위치와 텍스처 좌표가 법선과 접선보다 자주 사용되기 때문에보다 효율적인 것으로 나타났습니다. 
위치 데이터가 삼각형 제거에 사용되고 삼각형 인덱스 데이터와 음영으로 채워지는 동안 텍스쳐 좌표는 알파 테스트 된 지오메트리에 대한 삼각형 인덱스를 채우고 나중에 삼각형에 대한 것이 아닌이 유형의 지오메트리를 채우는 데 사용됩니다 제거. 일반 및 접선 데이터는 음영 단계에서만 사용됩니다.
ExecuteIndirect 호출은 또한 정점 버퍼로 색인하는 데 사용되는 인덱스 버퍼를 기대합니다. 이 데모에서는 삼각형 제거시 표시되는 삼각형 만 추가하여 생성 된 6 개의 "필터링 된"인덱스 버퍼를 사용합니다. 스왑 체인을 3 중 버퍼링하는 3 개의 인덱스 버퍼의 카메라 뷰와 섀도우 맵 뷰에는 두 세트가 있습니다. 삼중 버퍼 제거에는 삼중 버퍼가 비동기 컴퓨팅 쉐이더에 필요했습니다. 
또한 ExecuteIndirect 호출은 삼각형 제거 후 그리기 호출 압축 단계에서 생성 된 "필터링 된"간접 인수 버퍼를 기대합니다. 
ExecuteIndirect 호출에 공급 된 마지막 버퍼는 장면의 다양한 재질을 나타내는 데 사용되는 텍스처 ID 또는 재질 버퍼입니다.
모든 소스 코드는 Visibility_Buffer.cpp에서 찾을 수 있으며 drawVisibilityBufferPass () 와 visibilitybuffer_pass.frag에 있습니다. 

San Miguel 테스트 장면에서 네 개의 ExecuteIndirect 호출마다 간접적 인 그리기 호출 수는 다음과 같습니다. 

  • 그림자 불투명 : 163 
  • 그림자 알파 마스크 : 50 
  • 주 화면 불투명 : 152 
  • 메인 뷰 알파 마스크 : 50 
이러한 ExecuteIndirect 호출이 끝나면 Visibility 버퍼와 Depth 버퍼는 하나의 삼각형 레이어와 한 픽셀 레이어로 채워집니다. 즉, 불투명 기하학의 경우 삼각형과 픽셀의 오버 드로가 제거됩니다. 
이 데모에는 설명 된 Visibility Buffer 접근 방식과 G-Buffer 기반 Deferred Shading 방식에 대한 구현이 포함되어 있습니다. G-Buffer가 채워지는 방식은 Visibility Buffer가 채워지는 방식과 유사합니다. 주요 차이점은 메모리 사용 패턴입니다. 


메모리 사용 - 가시성 버퍼 대 G- 버퍼

메모리 대역폭은 게임의 성능을 제한하는 요인 중 하나입니다. 특히 로우 엔드 플랫폼 또는 4k 이상의 해상도를 지원해야하는 플랫폼의 경우 더욱 그렇습니다. 
지난 5 년 동안 G-Buffers의 크기가 커지면서 일반적으로 사용되는 Deferred Shading 기술은 더 많은 대역폭을 필요로합니다. 
버텍스와 인덱스 버퍼, 텍스처와 같은 다른 데이터, 인수, 유니폼, 설명자를 렌더링 한 다음 대상을 렌더링 할 수 있습니다. 렌더 타겟은 화면 크기와 함께 스케일을 조정하고 큰 화면 해상도는 렌더링하는 동안 차지하는 메모리의 매우 큰 부분을 차지합니다.
Visibility Buffer의 장점 중 하나는 두 개의 32 비트 렌더 타겟 (32 비트의 삼각형 가시성과 32 비트의 깊이 가시성)에 적합하다는 것입니다. 다음 텍스트는 Visibility Buffer 및 G-Buffer 구현의 데모 구현의 메모리 사용량을 비교합니다. 
ExecuteIndirect 호출을 제공하는 버텍스 및 인덱스 버퍼의 사용법은 이미지 8에 표시된 것처럼 가시성 버퍼 및 G- 버퍼 구현에서 동일합니다.

이미지 8 - 버텍스 및 인덱스 버퍼의 메모리 사용



또한 텍스처 (약 21MB), 인수, 유니폼, 설명자 등 (약 2MB)에 사용되는 데이터가 있습니다. 메모리 관점에서, 가장 흥미로운 메모리는 화면 공간 렌더 타겟에 사용되는 메모리입니다. 이미지 9는 1080p 해상도로 표시되는 렌더 타겟 메모리와 Visibility 버퍼에 대한 다양한 MSAA 설정을 보여줍니다.
이미지 9 - 1080p의 가시성 버퍼 메모리

이미지 10은 1080p의 해상도로 점유 된 렌더 타겟 메모리와 G- 버퍼에 대한 다양한 MSAA 설정을 보여줍니다 :
 
이미지 10 - 1080p의 G- 버퍼 메모리

1080p 메모리 수를 비교할 때 2x 및 4x MSAA가있는 G-Buffer는 두 개의 32 비트 렌더링 대상에서 5 개로 예상되는 크기보다 두 배 이상 커집니다.

4k (3840x2160)를 지원하는 모니터 또는 TV에서 가시성 버퍼와 비교 한 G- 버퍼 간의 델타는 이미지 11 및 12에 표시된 것처럼 커졌습니다.
이미지 11 - 가시성 버퍼 렌더링 4k의 대상 메모리

이미지 12 - 4k의 G- 버퍼 렌더링 타겟 메모리
제공된 숫자는 PC에서의 추정치 일뿐입니다. 왜냐하면 드라이버와 메모리가 단편화되어있어 하나의 렌더 타겟이 실제로 차지하는 정도가 바뀔 수 있기 때문입니다. 
이 수치는 Deferred Shading을위한 큰 화면 해상도의 G-Buffer를 채우고 읽는 것이 사용 된 GPU의 메모리 대역폭에 따라 메모리 대역폭 병목 현상이 될 수 있음을 보여줍니다. 이것은 5k 및 8k 디스플레이로 훨씬 더 극적입니다. 
즉, 가시성 버퍼와 같은 접근 방식을 구현하려는 동기 중 하나는 하드웨어 타일링 된 플랫폼이나 일부 콘솔 플랫폼과 같이 고속 메모리가 많지 않은 플랫폼에서 또는 고해상도 디스플레이에서 메모리 대역폭을 줄이는 것입니다.


농담

가시성 버퍼가 삼각형의 한 레이어로 채워지고 깊이 버퍼가 한 픽셀 레이어로 채워지면 장면을 음영 처리 할 수 ​​있습니다. 장면 음영 처리를 준비하기 위해 화면 공간 타일 당 조명 목록이 선행으로 생성됩니다 (조명 목록).


기와 목록

많은 수의 조명을 처리하기 위해 데모 구현은 화면 공간을 타일로 분할하고 해당 타일에서 렌더링되어야하는 광원을 식별합니다. 실제 쉐이딩 패스에서,이 라이트 목록은 불투명하고 투명한 오브젝트에 대한 모든 광원에 대해 하나의 스크린 공간 조명 패스를 수행하는 데 사용됩니다. 
이 목록을 생성하기 위해 계산 쉐이더는 타일 당 64 개의 조명으로 실행됩니다. 그것은 타일 공간에있는 경우에 빛의 바운딩 볼륨을 x 및 y 방향과 스크린 공간에서 타일의 x 및 y 방향과 비교하여 라이트 클러스터에 라이트를 추가하고 라이트를 증가시킵니다 해당 클러스터에 대해 계산하십시오. 카메라 뒤에있는 조명에 대한 조기 발견도 있습니다. 
이러한 타일에서 조명 목록을 생성하는 소스 코드는 cluster_lights.comp 에서 찾을 수 있습니다 .


포워드 ++

조명 기술은 가시성 버퍼를 한 화면 공간 패스에서 최적화 된 삼각형 데이터의 한 레이어와 함께 사용하기 때문에 Forward +와는 달리 Forward +를 여러 가지 그리기 호출을 사용합니다.

이미지 13 - Forward ++로 시정 버퍼 음영 처리




이미지 13은 상단의 가시성 버퍼와 깊이 버퍼를 보여줍니다. 오른쪽의 음영에 사용되는 다양한 버텍스 및 인덱스 버퍼입니다. 왼쪽에는 타일 당 많은 수의 조명을 적용하는 데 사용되는 타일 목록이 있습니다. 투명한 오브젝트의 경우 드로잉 호출을 실행하기 전에 드로어 호출을 정렬하여 전통적인 Forward +를 사용해야합니다. 

하이 레벨에서 쉐이딩 알고리즘은 다음 단계를 거칩니다. 
  • 화면 공간 픽셀 위치에서 drawID / triangleID 가져 오기 
  • IB와 3 개의 정점에 대한 데이터를로드 한 다음 VB 
  • 중력 좌표의 부분 파생물, 즉 삼각형 그래디언트를 계산합니다. 
  • 그라디언트를 사용하여 픽셀 위치에서 정점 속성 보간 
  • 방향성 광원 기여 계산 (Blinn-Phong 데모 코드에서만) 
  • 기와 목록의 타일을 통과하여 포인트 기부금을 추가합니다. 
광원을 적용하기위한 소스 코드는 visibilityBuffer_shade.frag에 있습니다. 

편미분을 계산하기 위해 부록 A의 식 (4)에서 [Schied]의 다음 방정식을 사용합니다.


방정식 1 - 부분 파생물


이 방정식의 구현은 다음과 같습니다.

// 투영 된 // 화면 공간 정점 에서 삼각형의 편미분도를 계산합니다.
DerivativesOutput computePartialDerivatives (float2 v [3])
{
DerivativesOutput 출력;
float d = 1.0 / 행렬식 (float2x2 (v [2] - v [1], v [0] - v [1])));
output.db_dx = float3 (v [1] .y - v [2] .y, v [2] .y - v [0] .y, v [0] .y - v [1] .y) * d ;
output.db_dy = float3 (v [2] .x - v [1] .x, v [0] .x - v [2] .x, v [1] .x - v [0] .x) * d ;

반환 산출;
}


이 코드의 부분 파생물은 가능한 한 많은 정밀도를 유지하기 위해 내장 함수없이 계산됩니다. 
실제 음영 코드는 다소 직관적입니다. 지향성 조명이 먼저 적용되고 포인트 조명은 화면 타일의 가시성에 따라 for 루프에서 나중에 적용됩니다 

.
// 지향성 광원
shadedColor = calculateIllumination (normal, uniforms.camPos.xyz, uniforms.esmControl,                  uniforms.lightDir.xyz, isTwoSided, posLS, position, shadowMap, diffuseColor.xyz, specularData.xyz, depthSampler);



// 점등
// 현재 픽셀에 대한 조명 클러스터를 찾습니다.
uint2 clusterCoords = uint2 (floor ((input.screenPos * 0.5 + 0.5) * float2 (LIGHT_CLUSTER_WIDTH, LIGHT_CLUSTER_HEIGHT))));


uint numLightsInCluster = lightClustersCount.Load (LIGHT_CLUSTER_COUNT_POS (clusterCoords.x,                             clusterCoords.y) * 4);


// 가벼운 기여금 누적
for (uint i = 0; i <numLightsInCluster; i ++)
{
 uint lightId = lightClusters.Load (LIGHT_CLUSTER_DATA_POS (i, clusterCoords.x, clusterCoords.y) * 4);
 shadedColor + = pointLightShade (light [lightId] .position, lights [lightId] .color, uniforms.camPos.xyz, position, normal, specularData, isTwoSided);

}


이 코드와 설정은 나중에 데모를 반복 할 때 변경 될 수 있습니다. Ray Tracing 코드는 부분 파생물의 존재와 삼각형의 가시성이 가시성 버퍼에서 최적화된다는 이점을 얻을 수 있습니다.



가시성 버퍼 - 이점

Visibility Buffer와 대형 G-Buffer가있는 Deferred Shading 시스템을 비교하면 다음과 같은 이점이 있습니다.


메모리 대역폭

렌더 타겟 메모리 풋 프린트가 더 작기 때문에 Visibility Buffer는 G-Buffer에 비해 메모리 대역폭 이점을 제공합니다. 이는 화면 해상도가 높거나 빠른 메모리 양이 제한되어있어 두 개의 32 비트 렌더 대상 또는 심지어 렌더링 대상의 타일 영역이 적합하지 않은 시나리오에서 두드러집니다.


메모리 액세스 패턴

음영이 발생하면 가시성 버퍼의 필터링 된 인덱스 버퍼에서 삼각형 데이터를 가져옵니다. 인덱스 / 버텍스 버퍼의 실제 데이터 가져 오기는 일반 그리기 호출과 유사하지만 화면 공간에서 한 번 연속적으로 발생합니다. 즉, 가시성 버퍼 (Visibility Buffer)를 통한 간접 지시와는 별도로 인덱스 및 정점 버퍼의 메모리 액세스는 GPU 설계자가 염두에 두었던 "최적의"액세스 패턴입니다. 일반 포워드 렌더러와 비교하면 화면 공간에서 한 번만 발생하며 여러 번의 그리기 호출에서는 발생하지 않습니다. 
텍스처, 정점 및 인덱스 버퍼에 대한 99 % L2 캐시 히트의 매우 일관된 캐시 적중률을 볼 수 있습니다. 그러므로 삼각형을 조명하는 것은 빠르다. 
G-Buffer에 조명을 적용하려면 화면 공간 데이터의 중복 성이 더 커야하기 때문에 더 큰 메모리 영역에 액세스해야합니다.
이 두 가지 이점은 아래 표시된 성능 측정치에 밑줄이 그어져 있습니다.


재료 다양성

가시성 버퍼는 재료 매개 변수가 G- 버퍼의 픽셀 단위로 저장 될 필요가 없으므로 훨씬 더 넓은 범위의 재료를 나타낼 수 있습니다. 앞으로 렌더러에서 머티리얼을 사용함을 통해 얻은 교훈은 가시성 버퍼가 동일해야한다는 것 외에는 묶지 않은 텍스처 배열을 사용한다는 아이디어에 따라 확장해야합니다.


여러 가지 잡다한

Triangle Visibility Buffer 구현에 대한 토론에서 주로 제기되는 몇 가지 질문이 있습니다.


왜 우리는 이것을 이전에 구현하지 않았습니까?

여기에 설명 된 것은 하나의 논문을 구현하는 간단한 과정이 아니 었습니다. 우리는 2015 년 9 월 [Schied]와 함께 시작했습니다. Christoph Schied는 우리 사무실에 와서 OpenGL에서 그 시점의 이전 렌더링 프레임 워크에서 자신의 접근 방식을 구현했습니다. 우리는 지난 2 년 반 동안 모든 것을 단순화하여 [Burns]에서 다루어 진 아이디어와 더 비슷하게 보입니다. [Burns]에는 DirectX 9 API 만 있지만 가시성 버퍼에서 삼각형을 실제로 저장하면 ExecuteIndirect가 CPU 오버 헤드를 줄이는 최적의 "마사지 된"작업량 집합으로 삼각형 제거 및 압축 단 계로 인해 발생합니다.
Visibility Buffer가 삼각형의 한 레이어로 채워지고 깊이 버퍼가 한 픽셀 레이어를 보유하면보다 나은 메모리 액세스 패턴으로 인해 Deferred Shading 및 Forward Renderer에 비해 한 번에 한 번 화면 공간 음영을 실행할 수 있습니다. 음영을 준비하려면 계산 쉐이더를 사용하여 조명을 타일로 정렬해야합니다. DirectX 9에서는 사용할 수 없었습니다.


애니메이션 오브젝트를 얼마나 자주 스키닝해야합니까?

삼각형 제거, 가시성 버퍼 작성 및 음영 처리를 위해 정점을 변환하는 3 단계가 있습니다. 삼각형 제거 단계 이후에 변환은 삼각형 제거와 비교하여 삼각형의 절반 이하에서만 발생합니다. 
삼각형을 변형해야하는 횟수를 줄이려면 향후 데모 응용 프로그램 반복에서 삼각형의 사전 변형이 구현됩니다.


지연 데칼은 어떻습니까?

데칼 시스템을 계속 사용한다면 비동기 컴퓨팅 구동 텍스처 합성 시스템으로 전환 할 때가 있습니다. 그것 이외의 Deferred Decals는 Visibility Buffer 채우기 후에 구현 될 수 있습니다. Visibility Buffer에서 삼각형과 일반 데이터를 가져 와서 Deferred Decal 시스템과 마찬가지로 백 버퍼에 최종 결과를 적용합니다.


실적 번호

수년 동안 콘솔 플랫폼에서 macOS에 이르는 다양한 플랫폼에서 성능 수치를 수집했으며 현재 DirectX 12 및 Vulkan이 적용된 PC에서 성능 수치를 수집했습니다. Visibility Buffer 데모는 게임에서 사용되는 것과 유사한 G-Buffer와 실제 Visibility Buffer 구현을 사용하여 Deferred Shading 구현간에 전환을 허용합니다. 
Visibility Buffer / Buffer에 데이터를 렌더링하는 방법은 비슷합니다. 따라서 위에서 설명한 ExecuteIndirect 설정을 모든 플랫폼에서 사용합니다. 주요 차이점은 G-Buffer의 사용법입니다. 
다음은 이전 버전의 코드 기반에서 4k로 실행되는 DirectX 12 구현의 숫자입니다. 

이미지 14 - 가시성 버퍼 성능 번호



이미지 15 - 지연 음영 성능 번호
"도려내 기"라는 열은 삼각형 컬링 및 필터링의 성능 비용을 보여줍니다. 대부분의 다른 열은 자명합니다. 
화면 해상도가 증가함에 따라 G-Buffer와 가시성 버퍼 간의 성능 차이가 분명 해집니다. 차이점은 1080p 및 4k 해상도의 콘솔 플랫폼에도 적용됩니다.


미래

Visibility Buffer의 향후 반복 작업을 위해 우리는 물리적 기반 자료, 광선 추적 및 객체 - 공간 음영을 검토하고 있습니다. 주목할만한 결과가 발견되면 다른 블로그 게시물에서 공유됩니다.


크레딧

Confetti에서의 모든 작업과 마찬가지로, 이와 같은 오랜 시간 동안의 연구 프로젝트는 많은 사람들에 의해 영향을 받았습니다. 특별한 순서없이, Leroy Sikkes, Jesus Gumbau, Thomas Zeng, Max Oomen, Jordan Logan, Marijn Tamis, David Srour, Manas Kulkarni, Volkan Ilbeyli, Andreas Valencia Telez, Eloy Ribera, Antoine Micaelian이있었습니다. 이 프로젝트에 내가 누군가를 잊어 버린 경우, 그 사람을 추가 할 것입니다 ... 저에게 알려주세요 :-) 
우리는 GeometryFX와 AMD 및 다른 많은 오픈 소스 라이브러리의 Vulkan Memory Manager를 사용하고 있습니다. 우리는 코드와 지식을 공유 한 모든 오픈 소스 기여자들에게 감사드립니다. 이러한 기여가 없다면 위조와 같은 프레임 워크로 자신의 게임 엔진을 작성하는 것이 그렇게 쉽지는 않을 것입니다. 우리는이 영혼이 살아 있고 다른 사람들도 똑같이하기를 바랍니다.


참고 문헌 

[Burns] Christopher A. Burns, Warren A. Hunt "가시성 버퍼 : 지연 쉐이딩에 대한 캐시 친화적 접근"Journal of Computer Graphics Techniques (JCGT) 2 : 2 (2013), 55-69. http : //jcgt.org/published/0002/02/04 
[Chajdas] Matthaeus Chajdas "GeometryFX" http://gpuopen.com/gaming-product/geometryfx/
[Engel2009] 볼프강 엥겔 (Wolfgang Engel), "라이트 프리 패스 (Light Pre-Pass)", "어드밴스 3D 그래픽 및 게임에서의 실시간 렌더링 ", SIGGRAPH 2009, http://halo.bungie.net/news/content.aspx?link=Siggraph_09 
[Lagarde] Sebastien Lagarde, Charles de Rousiers,"서리를 물리 기반으로 이동 렌더링 노트 ", 코스 노트 SIGGRAPH 2014 
[Olano] Marc Olano, Trey Greer,"2D 동차 좌표를 사용한 삼각형 스캔 변환 ",https://www.csee.umbc.edu/~olano/papers/2dh-tri/
[Schied2015] Christoph Schied, Carsten Dachsbacher "메모리 효율적인 지연 쉐이딩을위한 지연된 속성 보간법", http : //cg.ivd.kit .deu / publications / 2015 / dais / DAIS.pdf 
[Schied2016] Christoph Schied, Carten Dachsbacher "지연 속성 보간 쉐이딩", GPU Pro 7, CRC Press 
[Wihlidal] Graham Wihlidal, "Compute로 그래픽 파이프 라인 최적화하기", GDC 2016 , http://www.frostbite.com/2016/03/optimizing-the-graphics-pipeline-with-compute/