Skip to content

언리얼 렌더링 시스템 분석(14) - 확장: 현대 렌더링 엔진의 진화사 2부(성장기)

🌐 원문 링크: 剖析虚幻渲染体系(14)- 延展篇:现代渲染引擎演变史Part 2(成长期) (cnblogs.com/timlly)
📅 원문 발행일: 2022-04-15 | ✍️ 저자: Timlly (00 / )

💡 시리즈 분류: History of Rendering국내 업계 표준 용어 감수 및 수식/도해 복원 적용 완료


2000년대에 DirectX는 4개의 주요 버전인 8, 9, 10, 11을 출시했으며 각 주요 버전에는 여러 개의 부 버전이 포함되어 있습니다.

구체적인 설명은 다음과 같습니다.

버전 시간 셰이딩 모델 특징

다이렉트X 8 2000 셰이더 모델 1.0 -1.4 프로그래밍 가능한 셰이더, 테셀레이션

다이렉트X 9 2002년 셰이더 모델 2.0 - 3.0 4K 텍스처, 3D 텍스처, 이벤트 쿼리, BC1-3, 교합 쿼리, 부동 소수점 형식(혼합 없음), 확장 기능, MRT(4), 부동 소수점 혼합(제한적) 등

다이렉트X 10 2006년 셰이더 모델 4.0 - 4.1 통합 셰이더 모델, 지오메트리 셰이더, 스트리밍 출력, 알파-커버리지, 8K 텍스처, MSAA 텍스처, 양면 템플릿, 범용 렌더 타겟 뷰, 텍스처 배열, BC4/BC5, 전체 부동 소수점 형식 지원, 큐브 맵 배열, 확장 MSAA

다이렉트X 11 2009년 셰이더 모델 5.0 헐 및 도메인 셰이더, DirectCompute(CS 5.0), 16K 텍스처, BC6H/BC7, 확장된 픽셀 형식, 논리적 블렌딩 작업, 대상 독립적 래스터라이제이션, 파이프라인 단계당 UAV 증가 슬롯 수, UAV 렌더링 전용 강제 샘플 수, 상수 버퍼 오프셋 및 부분 업데이트

기존 프로그래밍 가능 아키텍처(왼쪽), 새로운 테셀레이션 아키텍처(오른쪽). 새로운 아키텍처는 회색으로 표시된 GPU 파이프라인에 두 단계를 추가합니다.

DX1.0에서 10.0까지의 진화 차트.

DirectX10 프로그래머의 관점(위), 시스템 아키텍처(가운데) 및 구성 가능한 파이프라인(아래).

DirectX10 출력 병합 단계.

DirectX10의 고정 버퍼에 대한 지침 및 제안입니다. 업데이트 빈도에 따라 매개변수를 서로 다른 버퍼로 분할하고 캐싱을 활성화하는 것이 좋습니다(1부). 고정 버퍼는 다양한 셰이더(아래)에서 공유하고 액세스합니다.

DirectX10 로직 파이프라인의 발전.

DirectX10의 새로운 크로스 스테이지 연결 방식입니다. "이름으로 바인딩" 모델은 "위치로 바인딩"으로 분류될 수 있는 모델을 위해 폐기되었습니다. 모델은 이를 각 단계 사이의 레지스터 세트로 처리하며, 각 단계는 특정 순서로 데이터를 출력하고 다음 단계에서는 이를 해당 순서로 사용합니다. 그리고 레지스터 라이브러리의 위치에 따라 바인딩됩니다. 링크는 물리적 위치로 식별됩니다. 즉, 그리기 시 매핑되는 대신 셰이더 작성 시 응용 프로그램에서 순서가 유지됩니다. 즉, 이 작업은 외부 루프로 푸시되었으며 이러한 링크는 서명(위)이라는 구성을 통해 유지되고 셰이더에 바인딩됩니다. 서명된 사용 사례(아래).

DX9와 DX10의 화면 비교.

2009년에 출시된 DirectX 11은 확장성 향상, 개발 경험 향상, GPU 적용 범위 확장 및 성능 향상에 중점을 둡니다. Direct3D 11은 Direct3D 10 및 10.1의 엄격한 상위 집합으로, 새로운 기능에 대한 지원을 추가합니다. DirectX 11의 새로운 기능에는 테셀레이션, 컴퓨팅 셰이더, 멀티스레딩, 동적 셰이더 체이닝, 향상된 텍스처 압축 등이 포함됩니다.

DX11의 렌더링 파이프라인에는 표면 세분화 및 계산 셰이더가 추가되었습니다.

DX11의 표면 세분화 작업의 개략도.

DX11의 새로운 자산 생성 파이프라인.

DX11의 컴퓨팅 셰이더는 그래픽 처리, 포스트 프로세스, A-버퍼, OIT, 레이 트레이싱, 방사성 측정, 물리학, AI 등에 사용할 수 있습니다.

DX11은 또한 멀티스레딩, 비동기 리소스 로딩(리소스 업로드, 셰이더 생성, 상태 객체 생성), 병렬 및 동시 렌더링, 멀티스레드 그리기 및 상태 제출, 여러 스레드에서 렌더링 작업 분산, 객체별 표시 목록에 대한 제한된 지원을 허용합니다.

D3D 장치의 기능은 Device, Immediate Context, Deferred Context의 세 부분으로 나누어집니다. 장치는 유휴 스레드 리소스로 생성되고, 즉시 컨텍스트는 상태, 그리기 및 쿼리를 위한 단일 마스터 장치이고, 지연된 컨텍스트는 상태 및 그리기를 위한 스레드별 장치입니다.

DirectX11 멀티스레딩 모델.

비동기 리소스의 경우 Device의 인터페이스를 사용하여 리소스를 생성합니다. 모든 인터페이스는 스레드 독립적이며 우수한 세분성을 사용하여 기본 요소를 동기화합니다. 자산 업로드와 셰이더 컴파일이 동시에 발생할 수 있습니다. 상태 및 도면 제출에는 두 가지 우선순위가 있습니다. 우선순위가 높은 것은 다중 스레드 제출 및 전용 표시 목록이고, 우선순위가 낮은 것은 여러 번 재사용할 수 있는 개체별 표시 목록입니다. 또한 DX11의 표시 목록은 변경할 수 없습니다.

DirectX11 멀티스레드 리소스 생성 및 기본 그리기.

DX11은 여러 개의 지연된 컨텍스트를 생성할 수 있으며, 각 컨텍스트는 스레드에 바인딩될 수 있습니다(스레드가 안전하지 않음). 지연된 컨텍스트는 표시 목록을 업로드하고, 표시 목록은 직접 컨텍스트 또는 지연된 컨텍스트에서 사용됩니다. 지연된 컨텍스트는 GPU에서 데이터(예: 쿼리, 리소스 잠금)를 다운로드하거나 다시 읽을 수 없으며 DISCARD를 사용한 잠금을 지원하지 않습니다.

DX11 시대의 셰이더는 점점 더 크고 복잡해졌으며 광범위한 하드웨어 플랫폼과의 호환성이 필요하고 다양한 셰이더 구성 드라이버의 전문화를 최적화해야 합니다. 두 가지 솔루션이 있습니다.

  • 우버 셰이더. 모든 결합 사례에 대한 논리는 동일한 셰이더에 있습니다.
cpp
foo(...)
{
    if(m == 1)
    {
        // do material 1
    }
    else if (m == 2)
    {
        // do material 2
    }
    
    (...)
    
    if(l == 1)
    {
        // do light model 1
    }
    else if (l == 2)
    {
        // do light model 2
    }
    
    (...)
}

장점:

하나의 셰이더가 모든 셰이딩 코드를 제어합니다.

  • 모든 기능이 하나의 파일에 있습니다.

  • 런타임 상태 변경을 줄입니다.

  • 컴파일 단계는 단 하나입니다.

  • 더 많이 사용되는 인코딩 방법입니다.

  • 단점:

복잡합니다.

  • 조직력이 부족합니다.

  • 레지스터 사용은 항상 최악의 경로에 있습니다.

  • 전문화. 각 조합은 전용 셰이더를 생성합니다.

장점:

항상 최고의 레지스터 사용.

  • 타겟 방식으로 최적화하기가 더 쉽습니다.

  • 단점:

폭발적인 조합으로 이어지는 대규모 셰이더. (아래 사진)

  • 런타임 관리가 골칫거리입니다.

해결책은 동적 셰이더 체이닝과 객체 지향 프로그래밍(OOP)입니다. 원하는 특정 클래스 인스턴스를 선택하면 런타임이 클래스의 메서드를 인라인 처리합니다. 이는 전용 셰이더에 클래스를 등록하는 것과 같습니다. 인라이닝은 기본 어셈블리 빠른 작업에서 수행되며 모든 후속 Draw() 호출에 적용됩니다.

유니버설 셰이더와 동적 링크의 비교.

텍스처 압축 측면에서 이전 블록 팔레트 보간법이 너무 단순하여 블록 결함이 너무 뚜렷하고 HDR을 지원하지 않았기 때문에 DX11은 BC6 및 BC7을 추가했습니다. BC6은 고품질(무손실) 시각 효과를 위해 1/6(16bpc RGB)의 압축률에 도달하는 HDR을 지원합니다. BC7은 Alpha의 LDR, 1/3 압축률 및 높은 시각 효과를 지원합니다. 새로운 형식은 여전히 ​​블록 압축을 사용합니다. 각 블록은 독립적이며 고정된 압축 비율을 갖지만 부드러운 그라디언트 및 노이즈가 있는 노멀 맵, 알파 및 상수 알파 변경 등과 같은 다양한 콘텐츠 유형에 맞게 조정된 다양한 새로운 블록 유형이 추가되었습니다.

BC 텍스처 형식에 대한 블록 파티션 테이블 범례입니다.

기존 BC 텍스처 형식과 새 BC 텍스처 형식 비교.

DX11은 다음 기능도 지원합니다.

-주소 지정이 가능한 스트림 아웃

  • 간접 그리기

  • 풀 모드 속성 평가

-개선된 Gather4

  • 최소 LOD 텍스처 클램프

  • 16K 텍스처 제한

  • 필수 8비트 서브텍셀, 서브밉 필터링 정밀도

  • 보수적인 oDepth

  • 2GB 리소스

  • 기하학 셰이더 인스턴스 프로그래밍 모델

  • 선택적인 이중 지원

  • 읽기 전용 깊이 또는 스텐실 보기

14.3.1.2 OpenGL

OpenGL은 2000년대에 다음 버전을 출시했습니다.

버전 시간 특징

오픈GL 1.3 2001년 8월 멀티 텍스처링, 멀티 샘플링, 텍스처 압축

오픈GL 1.4 2002년 7월 깊이 질감, GLSlang

오픈GL 1.5 2003년 7월 VBO(버텍스 버퍼 개체), 폐색 쿼리

오픈GL 2.0 2004년 9월 GLSL 1.1, MRT, NPOT 텍스처, 포인트 스프라이트, 양면 템플릿

오픈GL 2.1 2006년 7월 GLSL 1.2, 픽셀 버퍼 객체(PBO), sRGB 텍스처

오픈GL 3.0 2008년 8월 GLSL 1.3, 텍스처 배열, 조건부 렌더링, 프레임 버퍼 객체(FBO)

오픈GL 3.1 2009년 3월 GLSL 1.4, 인스턴스화, 텍스처 버퍼 개체, 균일한 버퍼 개체, 기본 다시 시작

오픈GL 3.2 2009년 8월 GLSL 1.5, 지오메트리 셰이더, 멀티샘플링 텍스처

OpenGL이 GPU에서 실행되는 그래픽 파이프라인의 확장 버전으로, 고정 기능 파이프라인의 일부가 프로그래밍 가능한 단계로 대체되었습니다.

동시에 임베디드 및 모바일 장치용 경량 그래픽 API인 Open ES도 개발 중입니다. OpenGL과 OpenGL ES의 눈에 띄는 차이점은 OpenGL ES에서는 더 이상 glBegin 및 glEnd를 사용하는 주변 OpenGL 라이브러리 호출이 필요하지 않으며, 원시 렌더링 함수의 호출 의미가 버텍스 어레이을 위해 변경되었으며, 버텍스 좌표에 대해 고정 소수점 데이터 유형이 도입되었으며, 종종 부동 소수점 단위(FPU)가 부족한 임베디드 프로세서의 컴퓨팅 성능을 더 잘 지원하기 위해 속성이 추가되었다는 것입니다. 다음 표는 2000년대 OpenES가 출시한 버전과 설명을 보여줍니다.

버전 시간 특징

OpenGL ES 1.0 2003년 7월 쿼드 및 폴리곤 렌더링 프리미티브, texgen, 라인 및 폴리곤 메쉬, 좀 더 기술적인 드로잉 모드 제거, 표시 목록 및 피드백, 상태 속성에 대한 푸시 및 팝 작업, 일부 재질 매개변수(예: 뒷면 매개변수 및 사용자 정의 클리핑 평면)

OpenGL ES 1.1 알 수 없음 다중 텍스처(결합기 및 내적 텍스처 작업 포함), 자동 밉맵 생성, 버텍스 버퍼 객체, 상태 쿼리, 사용자 클리핑 평면, 향상된 포인트 렌더링 제어.

OpenGL ES 2.0 2007년 3월 프로그래밍 가능한 파이프라인, 셰이더 제어 흐름

14.3.2 하드웨어 아키텍처

새로운 세기가 시작되면서 Sony는 인기 있는 PS2 게임 콘솔을 출시했습니다.

PS2 콘솔 외관(위) 및 내부 하드웨어 구성 요소 다이어그램(아래).

PS2 호스트의 하드웨어 아키텍처 상호 작용 및 관계 다이어그램은 다음과 같습니다. 하드웨어 아키텍처와 내부 구조에는 EE Core, VIF0, VIF1, GIF, DMAC 및 경로 1, 2, 3이 포함됩니다. 각 데이터 버스에는 너비와 속도가 표시되어 있습니다. 실제로 이 아키텍처는 많은 수정을 거쳤습니다.

PS2 하드웨어 아키텍처를 단순화한 그림입니다. Emotion 엔진, 그래픽 신디사이저, RAM, IO 프로세서, 오디오 프로세서 및 기타 구성 요소가 포함되어 있습니다. 각 구성 요소 사이의 대역폭은 선으로 표시됩니다.

위 그림의 Emotion 엔진에는 CPU, DMA, 메모리 인터페이스 및 2개의 16M 메모리가 포함되어 있습니다.

Emotion 엔진은 VPU0 및 VPU1이라는 두 개의 벡터 처리 장치(VPU)를 사용하며 하드웨어 구조와 상호 작용이 다릅니다. VPU0 및 VPU1 지원을 통해 PS2에는 일부 병렬 처리 기능이 있습니다. 다음은 직렬과 병렬의 비교 차트입니다.

PS2 이미지 합성기는 상대적으로 완전한 렌더링 파이프라인(전처리, 래스터라이제이션, 텍스처 매핑, 픽셀 테스트, 포스트 프로세스), 레지스터 및 4M의 비디오 메모리를 갖추고 있습니다. 이들의 상호 작용 및 흐름도는 다음과 같습니다.

PS2 아키텍처는 또한 멀티 버퍼링 메커니즘(이중 버퍼링, 삼중 버퍼링, 쿼드 버퍼링)을 지원합니다. 이는 각 인스턴스에 대해 서로 다른 변환 매트릭스를 사용하여 단일 기본 요소의 여러 인스턴스를 렌더링하고 상당한 속도 향상과 대기 시간 감소를 보여줍니다.

위에서 아래로: PS2 단일 버퍼링, 이중 버퍼링, 삼중 버퍼링, 쿼드 버퍼링 상호 작용 및 타이밍 다이어그램.

다중 캐시 메커니즘과 두 개의 VPU 및 기타 구성 요소를 결합하면 GS, DMA 및 VU의 병렬 처리가 달성될 수 있습니다. 다음은 데이터 흐름 및 타이밍 다이어그램입니다.

새로운 이미지 합성기, 병렬 컴퓨팅 아키텍처 및 다중 버퍼 기술의 지원으로 PS2 플랫폼 게임의 이미지 품질도 크게 향상되었습니다. 다음은 각각 2001년에 출시된 Crash Bandicoot: The Wrath of Cortex 및 Final Fantasy X의 게임 스크린샷입니다.

2000년 Nvidia는 Voodoo 칩을 개발한 3dfx를 인수하여 GPU 개발의 급속한 한 해를 시작했습니다. GPU에는 더 빠른 속도로 계산할 수 있는 능력을 갖춘 다수의 ALU(산술 논리 장치)가 포함되어 있지만 각 처리 장치는 서로 다른 데이터(예: 데이터 병렬 처리)에 대해서만 동일한 명령을 실행해야 합니다. GPU와 CPU 사이에는 물리적, 대화형 거리가 있습니다. 이들은 시스템 버스를 통해 연결되는데, 이는 메인 메모리에서 GPU 메모리로 데이터를 전송하는 데 많은 시간을 소비하게 하며 렌더링 병목 현상의 주요 원인 중 하나입니다.

CPU와 GPU는 PCI-E 버스를 통해 연결되는데, 이는 렌더링 병목 현상의 주요 원인 중 하나입니다.

초기에는 다양한 버스 유형이 개발되었습니다(원래 표준: ISA, MCA, VLB, PCI 및 AGP(Accelerated Graphics Port) 표준은 1997년에 제정되었습니다). 당시 주요 솔루션은 고속 직렬 컴퓨터 확장 버스 표준을 제공하는 PCI Express 표준이었습니다. 다음 표는 다양한 PCI Express 버전에 대한 속도 표입니다.

PCIe 1.0 PCIe 2.0 PCIe 3.0 PCIe 4.0 PCIe 5.0 PCIe 6.0

2.5GT/초 5.0GT/초 8.0GT/초 16.0GT/초 32.0GT/초 64.0GT/초

자세한 PCIe 속성 및 설명은 아래 표를 참조하세요.

PCIe의 발전으로 렌더링 성능이 많이 향상되었습니다. 또한 GPU 하드웨어 아키텍처도 빠르게 발전하고 있습니다. ATI Radeon HD 3800 GPU를 예로 들면, 320개의 스트림 프로세서, 6억 개 이상의 트랜지스터, 1테라플롭스 이상의 컴퓨팅 속도를 갖추고 있습니다.

Geforce 8800 하드웨어 아키텍처 다이어그램.

Geforce GTX 280 하드웨어 아키텍처 다이어그램.

GPU 하드웨어의 개선으로 컴퓨팅 속도가 무어의 법칙보다 더 큰 곡선으로 발전할 수 있게 되었습니다.

2003년부터 2013년까지의 Intel CPU 개발 동향 차트.

2006년 단위 프로세서 모델은 다음과 같습니다.

GPU 칩의 설계 초점은 CPU의 설계 초점과 다릅니다. 가장 큰 차이점은 GPU가 대기 시간을 허용하기 위해 멀티스레딩을 사용한다는 것입니다. 읽기를 기다릴 때마다 다른 스레드를 시작하기만 하면 됩니다. 스레드가 많으면 코어의 작업 부하를 유지할 수 있습니다(자세한 내용은 아래 표 참조).

CPU GPU

더 많은 지침, 더 적은 데이터 비순차적 실행 분기 예측 몇 가지 지침, 더 많은 데이터 심드 하드웨어 스레드

재사용과 지역성 거의 재사용되지 않음

작업 병렬성 데이터 병렬성

운영 체제가 필요합니다 운영 체제 없음

복잡한 동기화 간단한 동기화

지연 기계 처리량 기계

지침은 동일하게 유지됩니다. 지침이 계속 바뀌어요

GPU 명령어 아키텍처 세트(ISA)의 명령어가 급격히 변하는 이유는 다음과 같습니다.

  • 새로운 게임이 인기를 얻습니다.

  • API 디자이너(Ms)는 게임 작성을 더 쉽게 해주는 새로운 기능을 추가합니다.

  • 하드웨어 제조업체는 게임에 중점을 두고 새로운 하드웨어를 추가하여 게임을 더 빠르게 실행할 수 있습니다.

  • 새로운 하드웨어.

  • 게임 개발자는 새로운 하드웨어를 살펴보고 누구나 지침이 할 수 있다고 생각했던 것 이상을 수행하여 흥미롭고 새로운 효과(보다 현실적인)를 생각합니다.

유사한 코드의 경우 CPU와 GPU의 성능 차이는 무엇입니까? 구체적인 예를 들기 위해 CPU와 GPU가 모두 스레드에서 다음 코드를 실행한다고 가정합니다.

cpp
// load
r1 = load (index)
// series of adds
r1 = r1 + r1
r1 = r1 + r1
......

// Run lots of threads

일반적인 CPU 작업은 단일 CPU 장치의 한 번의 반복이며 100% 코어 활용도를 달성할 수 없습니다. 데이터를 미리 가져오기 어렵고, 멀티 코어도 도움이 되지 않고, 클러스터링도 도움이 되지 않으며, 미해결 가져오기 수가 제한되어 있습니다.

GPU 스레드는 처리량(낮은 클럭, 다양한 규모)에 관한 것이며, ALU 장치는 100% 활용도에 도달하고, 최종 출력의 하드웨어 동기화, 대규모 스레드, Fetch 장치 + ALU 장치, 빠른 스레드 전환, 순차적 완료:

위 그림의 Fetch와 ALU는 겹치고 있으며 완료되지 않은 가져오기가 많이 있습니다. 상단의 큰 막대는 ALU가 실행 중일 때를 표시하며 스레드가 충분하면 100% 활성 상태입니다.

Wavefront는 64개의 스레드 단위입니다. 워프(Warp)라고도 합니다. 모든 리소스는 시작 시 할당되므로 교착 상태가 발생하지 않습니다.

실행 큐의 스레드 수는 다음과 같이 계산됩니다.

  • 각 SIMD에는 256개의 레지스터 세트가 있습니다.

  • 각 레지스터 세트에는 64개의 레지스터(각각 128비트)가 있습니다.

256 64 10개의 128비트 레지스터 = 163840개의 128비트 레지스터.

  • 256 64 10 * 32비트 레지스터 4개 = 32비트 레지스터 665360개.

  • 각 스레드에 5개의 128비트 레지스터가 필요한 경우:

실행 대기열에 들어갈 수 있는 웨이브프런트는 256 / 5 = 51개입니다.

  • 51개의 Wavefront = SIMD당 3264개의 스레드 또는 32640개의 실행 중이거나 대기 중인 스레드.

따라서 CPU의 로드가 성능을 결정합니다. 컴파일러는 ALU 코드를 최소화하고 메모리 오버헤드를 줄이며 프리페치 및 기타 기술을 사용하여 메모리를 기다리는 시간을 줄이려고 합니다. GPU 스레드가 성능을 결정하는 동안 컴파일러는 ALU 코드를 최소화하고, 스레드를 최대화하고, 동기화를 줄이기 위해 명령을 재정렬하고 스레드를 기다리는 시간을 줄이기 위한 기타 트릭을 시도합니다.

2008년 주류 GPU 프로그래밍 모델은 다음과 같습니다.

둥근 상자는 프로그래밍 가능하고, 사각형 상자는 고정된 기능이며, 다른 모든 항목은 라이브러리에 떠 있습니다.

위 다이어그램의 모든 병렬 작업은 도메인별 API 호출을 통해 숨겨져 있으며 개발자는 순차적 코드 + 커널을 작성하고 커널은 하나의 버텍스 또는 픽셀에서 작동하며 개발자는 병렬 처리를 직접 처리하지 않으며 자동 병렬 컴파일러가 필요하지 않습니다. 이 메커니즘에서 개발자는 프로그램의 작은 부분만 작성하면 되며 나머지 코드는 일반적으로 각각 100줄 미만의 코드로 구성된 200~300개의 작은 커널인 라이브러리에서 제공됩니다. 경쟁 조건이나 오류 보고가 있을 수 없으며 프로그램은 직렬(각 버텍스/모양/픽셀)로 처리될 수 있습니다. pthread와 달리 프로세서 수를 아는 개발자는 없습니다. 결과는 매우 성공적이었고 프로그래밍도 충분히 간단했습니다.

ATI Radeon HD 4870(상단) 및 4800 시리즈(하단) 아키텍처 및 매개변수.

일부 ATI GPU의 프로그래밍 가능 영역 비율 비교.

2000년대 GPU 렌더링 파이프라인의 변화는 아래 그림과 같습니다.

2009년에 Nvidia는 다양한 콘텐츠 제작, 소프트웨어 개발 SDK, 성능 디버깅 및 기술 서적 등을 포함한 완전한 개발자 키트를 제공했습니다.

14.3.3 엔진 진화

14.3.3.1 포괄적인 진화

2000년대에는 렌더링 기술의 발전과 하드웨어 성능의 향상으로 게임 엔진에 대한 강력한 지원이 가능해졌으며, 게임 엔진이 더욱 강력한 기능을 제공할 수 있게 되었고, 게임 개발자는 다양한 유형의 게임을 개발할 수 있게 되었습니다.

2000년대의 다양한 게임 유형. 위에서 아래로 실시간 전략, 격투, 레이싱, 멀티플레이어 온라인 게임이 있습니다.

2000년대 초반에 게임 엔진이 구체화되기 시작했으며 아래 그림과 같이 핵심 모듈에 오디오, AI, 스트리밍, 스레딩, 물리, 렌더링, 충돌, 애니메이션 네트워크 등과 같은 많은 공통 기능이 도입되었습니다.

장면의 복잡성이 점점 더 높아지면서 장면 관리 기술도 변화하고 있습니다. 예를 들어, 다음 두 그림은 각각 BVH(계층적 경계 상자) 가속 구조와 공유 데이터의 장면 노드 구조를 보여줍니다.

리소스를 분리하기 위한 링크 노드(아래 그림 상단), 장면 노드 및 구성 요소 기반 장면 노드 구조(아래 그림 하단)와 같은 공유 데이터를 기반으로 하는 구조의 다른 변형이 있습니다.

장면 그래프 노드의 UML 다이어그램은 다음과 같습니다.

엔진에 일반적으로 사용되는 디자인 패턴에는 추상 팩토리 패턴, 프로토타입 패턴, 싱글톤 패턴, 어댑터 패턴, 브리지 패턴, 프록시 패턴, 명령 패턴, 관찰자 패턴, 템플릿 메소드 패턴, 방문자 패턴 등이 있습니다.

위의 디자인 패턴을 사용하면 엔진을 효과적으로 계층화하여 순환 종속성 문제를 해결할 수 있습니다. 다음은 최신 렌더링 엔진 설계에서 YARE 엔진의 계층화된 아키텍처와 그래픽, 핵심 하위 모듈 다이어그램입니다.

애플리케이션 계층에서 YARE 엔진의 렌더링 파이프라인은 다음과 같습니다.

셰이더에서 YARE 엔진은 당시 인기 있는 효과 및 기법 프레임워크 + CG 셰이더 언어를 채택했습니다.

효과 프레임워크 UML 다이어그램은 다음과 같습니다.

)

렌더링 파이프라인에서 YARE는 장면 노드에서 다음 처리를 수행합니다.

  • 먼저 메쉬 객체의 각 형상에 대해 Renderable 클래스의 인스턴스를 만듭니다.

  • 둘째, 각 Renderable 인스턴스에 대해 다음 단계를 수행합니다.

렌더링 가능한 객체에는 해당 형상이 할당됩니다.

  • 메시 객체에서 렌더링 가능한 경계 볼륨을 검색합니다.

  • 렌더러블의 구조 컨텍스트는 장면 데이터베이스(구조 변수 및 효과 포함)에서 검색되어 렌더러블에 할당됩니다.

  • 장면 데이터베이스에서 모든 전역 변수와 효과를 가져와 렌더링 가능한 개체에 할당합니다.

  • 장면 데이터베이스에서 렌더링 가능한 개체와 교차하는 모든 볼륨 변수 및 효과를 가져와 렌더링 가능한 개체에 할당합니다.

  • 이번 업데이트 중에 렌더링 가능한 인스턴스가 생성된 경우 도면에 추가됩니다.

YARE 엔진의 렌더링 가능한 개체는 장면 그래프 지오메트리, 변수 및 효과로 구성되며 도면에 삽입됩니다.

YARE 엔진의 렌더링 효과.

2003년 A Framework for an R to OpenGL Interface for Interactive 3D Graphics에서는 R에서 대화형 3D 그래픽을 제공하는 프레임워크에 대해 설명합니다. 그 중 RGL1(R이라고 함)은 대화형 시점 탐색 기능이 있는 3D 시각화 장치 시스템을 사용하는 확장 라이브러리입니다. R은 OpenGL을 실시간 렌더링 백엔드로 사용합니다. 핵심 구성 요소는 3D 그래픽 시각화를 가능하게 하는 개체 및 작업을 지정하기 위한 명령 집합을 제공하는 R과 OpenGL 간의 인터페이스입니다.

디자인의 중요한 목표는 객체 지향 접근 방식을 사용하여 해당 공간을 둘러싼 구 표면에서 뷰어를 이동하고 구의 중심에 뷰를 집중함으로써 포인팅 장치를 사용하는 3D 탐색을 위한 간단하고 직관적인 사용자 인터페이스를 제공함으로써 다양한 운영 체제로의 이식성을 촉진하는 것이었습니다.

이 프레임워크 구현의 초점은 3D "기본 요소"를 작동하여 보다 복잡한 3D 객체를 형성하는 것입니다. 모양과 모양을 제어하는 기능은 R에서 직접 액세스하여 여러 조명, 안개, 텍스처 매핑, 알파 블렌딩, 측면 종속 렌더링은 물론 제어 장치 및 장면 관리, 환경 설정(조명 설정, 경계 상자, 뷰포인트) 및 내보내기(스냅샷 만들기 및 내보내기) 등과 같은 많은 매력적인 OpenGL 기능을 구현할 수 있습니다.

R 시스템에 완벽하게 통합되기 위해서는 소프트웨어 설계가 매우 추상적이어야 하며, 이를 통해 크로스 플랫폼 이식성이라는 전반적인 설계 목표를 더욱 달성해야 합니다. R에는 창 시스템과 OpenGL에 대한 이식 가능한 인터페이스가 없기 때문에 이 아키텍처는 이러한 기능을 제공합니다. (아래 사진)

아키텍처와 관련된 소프트웨어 모듈에 대한 간략한 개요입니다. 기본 계층은 플랫폼 추상화를 나타내고 5가지 핵심 서비스를 포함합니다.

장면 설명은 렌더링 엔진에 의해 매우 빈번하게 계산되는 복합 개체 모델에 저장됩니다. 아래 그림의 논리적 데이터 모델은 스택 의미론을 사용하여 여러 모양과 조명을 동시에 관리하고, 3개의 추가 개체 슬롯을 관리하고, 한 번에 하나의 개체를 저장하고, 슬롯 개체를 교체하고, 스택 개체를 즉시 팝하거나 선택적으로 지울 수 있습니다.

GUI 레이어의 경우 추상 레이어와 구현 레이어가 분리되어 있으며 팩토리 모드는 다양한 드로잉 라이브러리를 캡슐화하는 목적을 달성하기 위해 특정 장치 인스턴스를 만드는 데 사용됩니다.

렌더링 엔진과 데이터 모델은 아래 이미지에 표시된 클래스 계층 구조를 사용하여 구현되었습니다. 렌더링은 다형성을 사용하여 수행됩니다. 모양 정보는 Shape 및 BBoxDeco 클래스로 집계된 Material 클래스에서 구현됩니다. Scene이라는 중앙 클래스는 데이터 모델을 관리하고 전반적인 렌더링 전략을 구현합니다.

2004년에 유한 상태 머신을 넘어 복잡한 행동 계층을 혼합하는 관리는 행동 기반 에이전트 아키텍처를 제안했습니다.

행동 기반 에이전트의 아키텍처 다이어그램.

FSM과 비교하여 행동 기반 코딩은 혼합을 지원하고(동시에 여러 "상태"에 있을 수 있음) 계층 구조는 플랫 FSM보다 표현력이 뛰어나며 목표와 행동 간의 동적 결합은 행동 트리의 첫 번째 프로토타입으로 간주될 수 있습니다.

게임 이동성에는 코드 이식성이 필요합니다. 더 많은 크로스 플랫폼 코드를 작성하고 이를 다양한 버전의 모바일 플랫폼에서 사용하는 기술에 대해 설명합니다. 이 기사에서는 프로젝트 디렉터리 구성, 폴더 구조, 프레임워크 디자인, 코딩 스타일, 매크로 정의, 컴파일러 경고 주의 등의 측면에서 제안이나 예를 제공합니다. 해당 프레임워크 디자인은 후크 및 유한 상태 머신을 사용하여 이벤트 중심 애플리케이션 디자인을 만들고 이러한 후크를 사용하여 애플리케이션의 핵심을 구동합니다. 구현된 의사코드는 다음과 같습니다.

cpp
static boolean EventHandler( ... Parameters ... )
{
    switch ( eventCode )
    {
        case EVT_APP_START:
            ClearScreen( curApp )
            MemInit( curApp ); // Initialize memory manager
            GameStart( curApp );
            GamePostfix( curApp );
            break;
        case EVT_APP_STOP:
            GameEnd( curApp );
            MemExit( curApp ); // Exit the memory manager
            break;
        case EVT_APP_EVENT: // Special treatment may be
            GameEvent( curApp ); // required here depending on
            break; // the actual platform
        case EVT_APP_SUSPEND:
            SuspendGame( curApp );
            break;
        case EVT_APP_RESUME:
            ResumeGame(curApp );
            break;
        case EVT_KEY_PRESS:
            keyPressed(curApp, parameter );
            break;
        case EVT_KEY_RELEASE:
            keyReleased( curApp, parameter );
            break;
    }
}

위의 내용은 게임 엔진을 구동하는 플랫폼 독립적 코드에 대한 후크입니다. 이 인터페이스를 통해 핵심 플랫폼 종속성이 추상화됩니다. 이 커널은 당시 거의 모든 환경에 맞게 생성할 수 있으며 스타일러스와 같은 기능을 추가하도록 쉽게 확장할 수 있습니다.

메모리 관리를 위해서는 new, delete 및 new[], delete[]를 재정의하고 경계 검사, 누수 감지 및 기타 통계 정보와 같은 디버깅 도구를 추가하는 것이 좋습니다.

cpp
void *operator new( size_t size );
void operator delete( void *ptr );
void *operator new[]( size_t size );
void operator delete[]( void *ptr );

GUI에서는 접합을 통해 다양한 크기의 배경이 동적으로 생성됩니다.

또한 더 나은 크로스 플랫폼 이식성을 위해 기사에서는 다음 사항에 주의할 것을 권장합니다.

  • 코드를 문서화하고 이해하기 쉽고 설명이 필요한 변수 및 함수 이름을 사용하십시오.

  • 가능하다면 어설션을 사용하세요. 프로그램 로직의 오류를 감지할 수 있을 뿐만 아니라 잘못된 바이트 순서, 정렬 문제, 손상된 데이터 등과 같은 휴대폰과 플랫폼 간의 이식 문제도 밝힐 수 있습니다.

  • 플랫폼 및 장치 종속성을 의식적으로 찾아 격리합니다.

  • 데이터 구조가 변경되면 모든 구성에서 올바르게 조정되었는지 확인하고 코드를 열어두거나 오류가 발생하기 쉬운 상태로 두지 마십시오.

  • 가능한 한 많은 장치에 대한 코드를 작성하십시오.

  • 신청을 완료한 후에는 향후 코드가 깨지는 것을 방지하기 위해 게임 엔진 코드를 잠급니다. 버전 제어 시스템을 사용하거나 파일을 읽기 전용으로 만들어 이를 수행할 수 있습니다.

  • 애플리케이션을 구축하는 데 필요한 단계를 올바르게 문서화합니다.

  • 새로운 모바일 버전을 구현하는 데 필요한 단계를 문서화합니다.

  • 코드에 #ifdef를 사용하지 마세요. 본질적으로 해킹인 이식성이 가장 낮은 솔루션은 이전에 잘 작동했던 버전과 빌드에 버그를 일으키는 경우가 많습니다.

  • 이식 시 코드 수정은 최대한 적게 해주세요. 변경하는 코드가 적을수록 새로운 버그가 발생할 가능성이 줄어듭니다!

스시 엔진에 구형 고조파 조명 추가하기에서는 스시 엔진에 조명으로 구형 고조파(Spherical Harmonic)를 추가하는 기술과 경험을 공유합니다. 알려진 렌더링 방정식은 다음과 같습니다.

모델이 강체이고 움직이지 않고 광원이 먼 구라고 가정하면 V 및 H 항을 제한한 후 적분은 상수가 됩니다. 이 경우 이러한 항(사전 계산된 복사 에너지 전송, PRT)을 사전 계산하여 모든 P 지점에 저장할 수 있습니다. 전달 함수의 공식과 다이어그램은 다음과 같습니다.

전달 함수는 P 지점에서 눈에 보이는 빛의 양과 빛이 반사되는 양을 인코딩합니다. 저장 시에는 SH(Spherical Harmonic)가 사용됩니다. 입사광을 통합하면 두 벡터의 내적만 남습니다. 구체적인 단계는 다음과 같습니다.

  • 오프라인 단계, 확산 복사 에너지 전송을 미리 계산하고 버텍스 또는 텍셀별로 구조를 저장합니다.

  • 실행 단계에서 엔진은 광원을 SH에 투사합니다.

  • 픽셀 및 버텍스 셰이더는 들어오는 빛을 확산 전달 기능과 통합하여 전역 디퓨즈를 계산합니다.

구현된 워크플로는 다음과 같습니다.

이 문서에서는 자세한 구현 세부 정보와 단계를 제공하지만 여기서는 자세히 설명하지 않습니다. 관심 있는 학생들은 원본 논문을 읽어볼 수 있습니다.

아래 그림은 Sushi 엔진의 PRT 렌더링입니다.

3D 애플리케이션 제작 파이프라인 속도 향상에서는 RenderWare 엔진의 아키텍처, 렌더링 파이프라인 및 지원되는 기능을 설명합니다. RenderWare는 게임 프레임워크, 작업 공간 및 관리자의 3가지 구성 요소로 구성됩니다. 이들의 관계는 다음과 같습니다.

RenderWare의 그래픽 아키텍처는 아래와 같습니다. 확장성과 분리를 달성하기 위해 플러그인, 제품군 및 일부 엔진 API를 통해 애플리케이션과 상호 작용합니다.

RenderWare에는 일반, 특수 케이스, 플랫폼 최적화, 맞춤형 파이프라인 등 4가지 유형의 렌더링 파이프라인이 있습니다. 일반 모드는 정적 메시, 스킨 처리된 메시 및 다중 텍스처를 지원하고 특수 사례는 복제, 파티클, 최적화된 조명 설정, 단순화된 가중치 스키닝, 베지어 배치 프리미티브, 라이트맵 등을 지원합니다. 플랫폼 최적화에는 DX9 및 XBOX용으로 최적화된 버텍스/픽셀 셰이더, PS2용 150개 이상의 핸드 VU 파이프라인, 수동으로 최적화된 GameCube 어셈블러 파이프라인, 하드웨어 스키닝(사용 가능한 경우)이 포함됩니다.

그래픽 측면에서 RenderWare는 API, 텍스처 캐시 관리, 파이프라인 구성 제품군, PDS 파이프라인 전송 시스템, 동적 버텍스 버퍼 관리, 렌더링 상태 캐시, 기본 형상/텍스처 인스턴스화 등을 갖춘 DMA 관리자를 지원합니다. 또한 장면 관리, 플랫폼 최적화 파일 시스템, 스트리밍 로딩, 비동기 로딩, 파티클 시스템, 파일 패키징, 지능형 파이프라인 선택 및 기타 기능도 지원합니다.

RenderWare 렌더링 렌더링. (2004)

Doom 3의 저장소 스타일 아키텍처에 대한 설명에는 Doom 3의 아키텍처 다이어그램이 언급되어 있습니다. Doom 3에는 장면 그래프, 게임 로직 프레임워크, 물리 및 충돌, 뼈대 스키닝, 저수준 렌더링, 리소스 관리, 타사 라이브러리, 핵심 시스템 및 기타 모듈이 포함되어 있음을 알 수 있습니다. 핵심 시스템에는 어설션, 수학 라이브러리, 메모리 관리(Zone Memory) 및 사용자 정의 데이터 구조가 포함됩니다. (아래 사진)

Doom 3의 엔진 모듈 아키텍처 및 종속성 그래프. (2004)

Doom 3의 LLR(Low Level Rendering)은 게임 엔진 중 가장 크고 복잡한 구성 요소 중 하나이며 3D 그래픽에 적용할 때 매우 중요합니다. Low Level Render에는 엔진의 원래 렌더링 도구가 모두 포함되어 있으며 게임에 존재하는 많은 전처리 작업을 처리합니다. LLR은 기본 데이터 구조, 게임 운영 및 전처리 레벨 설계를 담당합니다. 레벨은 2D로 설계되었으므로 여러 레벨이 있을 수 없습니다. 게임에서는 바닥을 2개의 별도 레벨로 나눕니다.

리소스 관리 측면에서 Doom 3는 3D 개체 모델링, 개체 충돌 해결 등을 지원하는 패키지가 포함된 DeePsea라는 리소스 관리자를 사용합니다. 게임 정보/데이터 패키지는 WAD, IWAD(내부 WAD) 또는 PWAD(패치 WAD)라는 특수 파일에 저장됩니다. 관련 데이터를 포함하는 WAD는 블록으로 조립됩니다. 예: "BLOCKMAP"이라는 이름의 IWAD 블록은 지도의 두 객체가 서로 접촉하는지 여부를 나타내는 정보를 저장합니다.

특수 효과 렌더링 측면에서 Doom 3는 이미 라이트 매핑, HDR 조명, PRT 조명, 파티클 및 데칼 시스템, 포스트 효과, 환경 매핑 등을 지원합니다.

위: Doom 3 조명 비교 차트; 하단: Doom 3의 특수 효과 시스템.

Doom 3의 장면 그래프에는 장면/영역의 모든 렌더 모델과 텍스처가 포함되어 있으며 프런트엔드 구성 요소와 상호 작용하여 백엔드에 필요한 모든 것이 GPU 메모리에 업로드되었는지 확인하고 컬링을 처리하고 백엔드로 명령을 보냅니다.

Doom 3 장면 그래프에서 광원 자르기의 개략도.

위 기술의 지원으로 같은 기간의 장면 효과가 크게 향상되었습니다.

2004년 실내 게임 장면 스크린샷.

3D 게임 엔진 개발 - OpenCommons에서는 2000년대 중후반의 엔진 설계 아이디어를 언급했습니다. 이 엔진을 스파크 엔진이라고 합니다. Spark Engine 게임 엔진의 주요 논리 의사 코드는 다음과 같습니다.

cpp
OnDraw()
    Frustum cull check
    If inside or intersects, call Draw()
Draw()
    If Node, call OnDraw() for children
    If Mesh, add to render queue to be processed
        If set to skip, call Render() immediately
Render() (Mesh only)
    Apply render states (checked against renderer’s enforced states)
    Apply material
        Update material definitions (sets shader constants)
        Evoke device draw call

씬 렌더러의 경우 렌더링 도구이자 엔진의 브릿지 역할을 하며, 드로잉 과정에서 각 공간에 전달되는 객체로서, 객체를 실제로 화면에 렌더링하는 기능을 제공합니다. 장면 렌더러는 완전히 독립적으로 설계되었으며, 각 렌더러에는 카메라가 있으므로 뷰포트가 있습니다. 이는 전체 화면일 수도 있고 화면의 작은 부분일 수도 있습니다. 여러 렌더러가 서로 다른 상황에서 동시에 활성화될 수 있습니다. 예를 들어 두 플레이어가 자신의 카메라를 사용하여 동일한 장면 그래프를 렌더링하고 장면 그래프 데이터가 렌더러와 완전히 독립적인 분할 화면 게임을 예로 들 수 있습니다. 그래픽 장치와 상호 작용하고 그리기 호출을 시작하는 기능을 포함하는 것 외에도 렌더러는 렌더링 성능을 향상시키는 몇 가지 추가 기능을 제공합니다. 렌더러는 다음 두 가지 방법으로 이 작업을 수행합니다.

  • 렌더링 상태 캐시. 렌더러는 상태 전환을 줄이기 위해 그래픽 장치에 적용된 마지막 렌더링 상태를 추적합니다. 상태 전환은 비용이 많이 들 수 있으므로 중복성을 줄이는 것은 렌더러가 채택할 수 있는 유용한 전략입니다.

  • 렌더링 대기열. 렌더러에는 버킷 세트를 관리하는 렌더링 대기열이 있어 개발자가 객체가 렌더링되는 순서를 제어할 수 있습니다. 기본적으로 엔진은 Pre-Bucket, Opaque, Transparent 및 Post-Bucket의 네 가지 버킷을 지원하며 이 순서대로 렌더링됩니다. 투명한 객체를 올바르게 그릴 수 있도록 형상이 렌더링되는 순서를 제어하는 ​​것이 유용합니다. 아래 이미지는 투명 큐브와 불투명 큐브를 사용하여 이 기술적 기능을 보여줍니다. 왼쪽 이미지에는 큐브가 올바르게 배열되어 있지만 오른쪽 이미지에는 그렇지 않습니다.

렌더링 상태는 렌더링 파이프라인을 통해 버텍스 및 픽셀 흐름으로 형상이 처리되는 방식에 영향을 줍니다. Microsoft Game Development Kit XNA에서 상태는 엔진에서 사용하는 열거형으로 정의됩니다. 엔진이 이러한 열거를 Spatial과 같은 클래스에 직접 연결할 수 있는 별도의 클래스로 그룹화하는 XNA와 달리 장면 그래프를 사용하면 렌더링 상태를 상속하고 결합할 수 있습니다. 또한 엔진은 렌더링 상태를 정의할 때 재료 색상 정보, 텍스처 및 조명을 포함하여 기하학적 데이터와 관련된 모든 정보를 참조하는 약간 다른 접근 방식을 취합니다. 렌더링 상태는 크게 두 가지 범주로 나뉩니다.

  • 전역 렌더링 상태. 알파 블렌딩, 트라이앵글 컬링, 깊이 버퍼링 등과 같은 렌더링 상태의 일반적인 정의와 유사한 XNA의 열거형을 포함합니다. 이러한 상태는 해당 정보가 Spatial 클래스의 속성과 무관하기 때문에 전역 상태로 정의됩니다. 따라서 이러한 상태는 본질적으로 XNA의 렌더링 상태 열거에 구성 요소화된 형태의 인터페이스를 제공하는 래퍼에 지나지 않습니다.

  • 데이터 렌더링 상태. 엔진의 재질 시스템에 음영 처리, 텍스처링 및 조명 정보를 제공하는 특수 목적 클래스입니다. 재료와 셰이더에서 데이터를 분리하면 유연성과 재사용이 가능합니다. 데이터 렌더링 상태에는 어느 하나의 셰이더에 해당하지 않는 텍스처와 조명 개체가 포함됩니다. 엔진의 TextureState는 다양한 재질로 해석될 수 있는 텍스처를 얼마든지 보유할 수 있습니다. 예를 들어 일부 셰이더는 확산 색상에 첫 번째 텍스처만 사용할 수 있고 다른 셰이더는 일반 및 반사 맵에 두 번째 및 세 번째 텍스처를 사용할 수 있습니다.

모든 Spark 엔진 렌더링 상태는 AbstractRenderState라는 추상 클래스에서 상속됩니다. 현재 엔진에서 지원하는 렌더링 상태는 다음과 같습니다:

  • BlendState: 투명도 옵션의 알파 및 색상 혼합을 제어합니다.

  • CullState: 시계 반대 방향, 시계 방향 또는 컬링 없음 등 삼각형 컬링을 제어합니다.

  • FillState: 와이어프레임, 솔리드, 점 등 개체를 채우는 방법을 제어합니다.

  • FogState: DirectX9.0c의 고정 기능 안개 기능을 제어합니다.

  • ZBufferState: 깊이 버퍼를 제어합니다.

  • MaterialState: 확산 색상, 주변 색상, 방출 색상, 반사 색상 등 객체의 균일한 재질 색상을 제어하는 ​​데 사용되는 데이터 렌더링 상태입니다.

  • TextureState: 개체의 텍스처에 적용되는 데이터 렌더링 상태이며 텍스처 필터링 및 래핑 모드에 대한 샘플러 상태도 관리합니다.

  • LightState: 공간에 부착된 광원 목록을 추적하는 조명 개체의 데이터 렌더링 상태입니다.

재료 시스템의 경우 재료는 객체를 음영처리하는 방법과 음영의 특성을 정의합니다. 렌더링된 각 개체에는 XNA의 Effect API와 직접적으로 관련된 관련 자료가 있어야 합니다. 머티리얼 시스템은 Effect API를 직접 사용할 때 발생하는 몇 가지 단점을 해결하도록 설계된 간단하지만 매우 유연한 시스템입니다. 그리기 호출의 효과에 대한 일반적인 호출 프로세스 의사 코드는 다음과 같습니다.

cpp
Load the shader effect file
Set shader constants
Call Effect.Begin()
For each EffectPass do
    Call EffectPass.Begin()
    Draw geometry
    Call EffectPass.End()
Call Effect.End()

Spark Engine은 점 광원, 스포트라이트, 방향 조명 및 기타 광원 유형도 지원합니다. 다양한 유형의 광원을 추상화하는 구조는 다음과 같습니다.

cpp
//Light struct that represents a Point, Spot, or Directional
//light. Point lights are with a 180 degree inner/outer angle,
//and directional lights have their position's w = 1.0.
struct Light {
    //Color properties
    float3 Ambient;
    float3 Diffuse;
    float3 Specular;
    //Attenuation properties
    bool Attenuate;
    float Constant;
    float Linear;
    float Quadratic;
    //Positional (in world space) properties
    float4 Position; //Note: w = 1 means direction is used
    float3 Direction;
    float InnerAngle;
    float OuterAngle;
};

렌더링 효과는 다음과 같습니다.

Spark Engine 광원 렌더링 효과. 위에서 아래로 점광원, 스포트라이트, 방향성 조명이 있습니다.

또한 Spark Engine은 조명, 노멀 맵, 하이라이트 맵, 등고선 조명, 큐빅 환경 맵, 지형 편집, 라이트 맵 및 다양한 셰이딩 단계의 기타 효과도 지원합니다.

Spark Engine의 큐빅 환경 맵.

네트워크 동기화 측면에서 QUAKE III ARENA는 CS 아키텍처를 채택하고 네트워크를 통해 서버를 연결하므로 서버는 다양한 클라이언트 간의 작업을 동기화할 수 있습니다.

QUAKE III ARENA의 게임 하위 시스템에는 게임의 서버 구현과 게임 서버 간에 공유되는 일부 라이브러리 및 클라이언트가 포함되어 있습니다. PMOVE 모듈은 게임 전체에 대한 플레이어의 상태 정보를 업데이트하는 주요 모듈입니다. 입력되는 내용은 현재 플레이어 상태(player_state_t)와 클라이언트에서 받은 사용자 작업(usercmd_t)입니다. 새로 생성된 player_state_t는 게임에서 현재 플레이어의 상태를 나타냅니다. (아래 사진)

위 그림은 QUAKE III ARENA의 클라이언트와 서버 동기화 통신 모델이고, 아래 그림은 서버와 클라이언트 통신의 동기화 모델입니다.

같은 기간 동안 ORGE는 장면 관리자, 리소스 관리자, 렌더링 및 플러그인과 같은 모듈을 도입했습니다. 아키텍처 다이어그램은 다음과 같습니다.

OGRE의 루트 개체는 진입점이며, 생성된 첫 번째 개체여야 하고, 마지막으로 삭제된 개체여야 하며, 시스템 구성이 활성화되고 연속 렌더링 루프가 있습니다. 장면 관리자에는 화면에 나타나는 모든 것과 지형(하이트맵), 외부 및 내부 장면에 대한 다양한 관리자가 포함되어 있습니다. 엔터티는 메시(플레이어, 지면...)로 표현되는 모든 것을 포함하여 장면에서 렌더링할 수 있는 객체 유형이지만 조명, 광고판, 입자, 카메라 등과 같은 객체는 엔터티가 아닙니다. SceneNode는 연결된 모든 객체의 위치와 방향을 추적합니다. 엔터티는 SceneNode 개체에 연결된 경우에만 화면에 렌더링됩니다. 장면 노드의 위치는 항상 상위 노드를 기준으로 합니다. 장면 관리자에는 다른 모든 장면 노드가 연결된 루트 노드가 포함되어 있습니다. 최종 구조는 장면 그래프입니다. ORGE의 렌더링 메인 루프는 다음과 같습니다:

cpp
void Root::startRendering(void) 
{
    // ... Initialization ...
    mQueuedEnd = false;
    while( !mQueuedEnd ) 
    {
        //Pump messages in all registered RenderWindow windows
        WindowEventUtilities::messagePump();
        if (!renderOneFrame()) 
            break;
     }
}

bool Root::renderOneFrame(void) 
{
    if(!_fireFrameStarted()) 
        return false;
    if (!_updateAllRenderTargets()) // includes _fireFrameRenderingQueued()
        return false;
     return _fireFrameEnded();
}

ORE의 자원 관리 전략은 다음과 같습니다.

  • 각 리소스에는 4가지 상태가 있습니다.

알 수 없음: Ogre는 리소스에 대해 모르고 해당 파일 이름이 저장되어 있지만 Ogre는 리소스로 무엇을 해야할지 모릅니다.

  • 선언됨: 생성 표시가 되어 있습니다. 오우거는 그것이 어떤 유형의 자원인지, 그것을 생성할 때 어떻게 처리해야 하는지 알고 있습니다.

  • 생성됨: 오우거가 리소스의 빈 인스턴스를 생성하여 해당 관리자에 추가합니다.

  • 로드됨: 생성된 인스턴스가 완전히 로드되어 리소스 파일에 접근하는 단계입니다.

  • Ogre의 기본 ResourceManager는 Root::Root에 생성됩니다.

  • 호출하여 리소스 위치를 지정합니다.

  • 리소스를 수동으로 선언합니다.

  • 스크립트 구문 분석은 자동으로 리소스를 선언합니다.

  • 특정 조건을 만족하는 리소스가 로드되도록 설정됩니다.

2006년 UE3는 64비트 HDR 색상, 픽셀당 조명, 고급 동적 조명(동적 스텐실 버퍼 그림자 볼륨, 부드러운 그림자, 미리 계산된 그림자, 미리 계산된 다수의 조명), 머티리얼 시스템, 볼륨 포그, 물리적 및 환경적 상호 작용을 지원하는 파티클 시스템, 그리고 기타 여러 렌더링 기능(예: 노멀 맵, 매개변수화된 퐁 조명, 이방성 효과를 포함한 각 머티리얼 조명 모델에 대한 사용자 지정 아티스트 제어, 가상 변위 매핑, 빛 감쇠 기능, 가상 변위 매핑, 조명 감쇠 기능, 3D 렌더링)을 지원합니다. 미리 계산된 섀도우 마스크, 방향성 라이트맵, 구형 조화 맵을 사용한 미리 계산된 범프 그레인 셀프 섀도우잉 등). 이러한 기능 덕분에 UE3의 렌더링 품질이 크게 향상되었습니다. (아래 사진)

UE3의 일부 렌더링 기능. 왼쪽 상단부터 오른쪽 하단까지 셀프 섀도잉이 포함된 캐릭터 동적 소프트 그림자, 동적 소프트 그림자, 픽셀별 조명 및 그림자, 노멀 매핑된 반투명 개체 왜곡 및 감쇠 프레임 버퍼, 클라우드 동적 그림자, 볼륨 안개, 64비트 HDR 색상, 노멀 맵 확산 및 흐릿한 그림자와 반사 조명 상호 작용입니다.

UE3 캐릭터 및 장면 객체 렌더링 효과.

CryEngine3에서는 더욱 새로운 기술의 도입으로 인해 렌더링 화면이 새로운 수준에 도달했습니다(아래 그림).

CryEngine 3 게임 스크린샷.

2006년에는 사실적인 조명을 갖춘 실시간 몰입형 응용 프로그램: 파르테논 및 파르테논 데모: 대규모 데이터 세트를 위한 전처리 및 실시간 렌더링 기술에서 Siggraph를 실시간으로 재현할 수 있는 대화형 시스템의 설계 및 구현을 설명했습니다. 2004년에 상영된 단편 영화 "파르테논"의 핵심 시퀀스 중 하나인 데모 프로그램은 사용자가 가상 환경을 인식할 수 있도록 특정 몰입형 현실 시스템에서 실행되도록 설계되었습니다. 영화에 가까운 시각적 품질.

라이브 데모 스크린샷. 새벽부터 황혼까지 시간이 흐르고, 건물 위로 그림자가 움직이며, 하늘 조명에 따라 장면의 전체적인 톤이 달라집니다. 사진은 매번 건물에 더 가까운 다양한 위치에서 촬영되었습니다.

이 기사에서는 데이터 세트의 크기 및 필요한 조명 계산의 복잡성과 관련된 몇 가지 기술적 문제에 대해 설명합니다. 이러한 문제를 해결하기 위해 스캔된 3D 모델을 실시간 렌더링 응용 프로그램에 더 적합하게 만드는 방법이 제안되고, 직접 조명과 간접 조명의 분리와 광범위한 사전 계산을 기반으로 하는 조명 알고리즘이 설명됩니다. 직접광과 간접광의 공식은 다음과 같습니다.

L= 람베르트식L=i=14CoeffiSkySHi

또한 이 기사에서는 기존 렌더링 엔진을 사용하여 조명 불변성을 미리 계산하는 방법과 최신 GPU를 사용하여 이러한 음영 처리 알고리즘을 구현하는 방법을 보여줍니다. 그 결과 기술은 실시간 애플리케이션에 정확하고(렌더링 결과 측면에서) 합리적인 가격(시간 측면에서)임이 입증되었습니다. 게다가 알고리즘 실행은 하드웨어 셰이더로 제한되므로 이러한 계산을 어떻게 기존 애플리케이션 프레임워크에 통합할 수 있습니까? 데모에 사용된 기술은 매우 일반적이므로 동일한 유형의 계산을 기존의 다른 시각화 시스템에 통합할 수 있습니다.

실시간 셰이딩 예: 그림자가 형상(왼쪽 상단 및 하단)에서 어떻게 정확하게 이동하는지, HDR 조명 계산을 통해 그림자 아래의 세부 사항을 더 잘 인식하기 위해 노출을 조정하는 방법(오른쪽)에 유의하세요.

확산 조명 사전 계산. 왼쪽에는 조명에 사용되는 구형 고조파 베이스가 있으며, 양수 값과 음수 값은 빨간색과 녹색으로 코딩되어 있습니다. 오른쪽에서는 고조파를 스카이돔 라이트로 사용하여 장면을 비추면 해당 고조파에 의해 개체의 각 부분이 얼마나 영향을 받는지 확인할 수 있습니다.

최신 GPU의 하드웨어 셰이더에서 모든 계산이 완료되면 추가 데이터 및 셰이더 관리를 수용하도록 기존 렌더링 엔진을 확장하는 것이 가능합니다. 이 방법을 사용하면 대규모 3D 데이터 세트 시각화 도구에도 사실적인 조명을 추가할 수 있습니다.

또한 이 기사에서는 모델을 전처리하고 모델을 클러스터, 매개변수화된 클러스터 및 샘플 텍스처로 분할해야 하는 프로그레시브 버퍼 기술에 대해서도 언급합니다. 자세한 내용은 14.3.4.4 프로그레시브 버퍼 섹션을 참조하세요.

또한 이 문서에서는 그림자 맵, PCF 소프트 그림자, SH 사전 계산, 인스턴스화, 하늘 렌더링(하늘 주변 조명, 직접 조명) 및 폐색 선별과 같은 기술에 대해 설명합니다.

SH 추정. 장면 렌더링을 위한 확산 조명 정보를 제공하는 데 사용되는 3차 SH(구형 고조파)를 사용하여 각 프레임에 대해 HDR 조명 정보를 내보내고 기록합니다.

하늘 주변광 렌더링. 정점당 굽힘 법선은 데카르트 SH 평가, 3차 명령 12개, 주변광 감쇠를 위한 앰비언트 오클루전(AO) 텍스처(절반 해상도)를 사용하여 SH 표현을 찾는 데 사용됩니다.

스카이 다이렉트 라이트 렌더링. 스카이박스에서 각 프레임에 대한 태양의 색상, 강도 및 위치를 추출하면 범프 맵은 디테일 텍스처로만 필요합니다.

4개 샘플에서 최적화 전후 PCF 효과 비교.

폐색 쿼리 기하학 컬링. 그려진 각 클러스터는 폐색 쿼리로 테스트되어 현재 프레임에 그려진 픽셀 수를 확인합니다. 픽셀이 그려지면 복셀은 다음 프레임에 그리기 위해 표시되고, 픽셀이 표시되지 않으면 다음 프레임에서는 저렴한 "프로빙"을 활성화하여 복셀 대신 색상으로 쿼드를 렌더링하고 Z 쓰기를 비활성화합니다.

2006년에는 매우 상세한 표면 렌더링을 위한 Practical Parallax Occlusion Mapping(Practical Parallax Occlusion Mapping For Highly Detail Surface Rendering)에서 물체 표면의 디테일과 신뢰성을 향상시키는 시차 폐색 매핑 기술에 대해 이야기했습니다.

병렬 시차 폐색 매핑(왼쪽)과 일반 매핑(오른쪽)의 비교.

시차 폐색 매핑은 일반 맵과 높이(변위) 맵이라는 두 가지 리소스를 사용합니다. 계산은 탄젠트 공간에서 완료되므로 모든 표면에 적용할 수 있습니다. (아래 사진)

)

시차 효과를 계산할 때 표면의 동작 시차 효과는 높이 맵을 적용하고 기하학 법선과 뷰 벡터를 사용하여 높이 맵의 각 픽셀을 오프셋하고 높이 필드를 통해 광선을 추적하여 표면에서 가장 가까운 가시 지점을 찾는 방식으로 계산할 수 있습니다. 알고리즘의 핵심 아이디어는 실제 변위 지오메트리가 실제로 사용된 경우 높이 맵의 어느 텍셀이 렌더링된 픽셀 위치를 생성할지 결정하기 위해 높이 맵에서 현재 렌더링된 픽셀을 역으로 추적하는 것입니다. 입력 메시는 표면을 아래로 이동하기 위한 참조 평면을 제공하고, 하이트 필드는 올바른 광선 하이트 필드 교차 계산을 위해 정규화됩니다(0은 참조 폴리곤 표면 값을 나타내고 1은 함몰을 나타냄).

구현 과정에는 정점별 방법과 픽셀별 방법의 두 가지 방법이 있습니다. 하이트 필드 프로파일 추적(Height Field Profile Tracing)을 사용하고 적절한 광선 교차 감지 및 동적 채택 비율을 선택하여 셀프 섀도잉 및 소프트 섀도잉과 같은 효과를 얻을 수 있습니다. 조명을 계산할 때 계산된 텍스처 좌표 오프셋은 필요한 맵(알베도, 노멀, 디테일 등)을 샘플링하는 데 사용됩니다. 이러한 매개변수와 가시성 정보가 주어지면 필요에 따라 모든 조명 모델(예: Phong)을 적용할 수 있으며 반사/굴절이 매우 유연하게 계산됩니다.

또한 적응형 LOD 시스템을 사용하여 현재 밉맵 수준을 계산할 수도 있습니다. 가장 먼 LOD 레벨의 경우 노멀 맵(임계값 레벨)이 렌더링에 사용되며, 현재 밉 맵 레벨에 따라 표면이 뷰어에 접근할 때 샘플링 속도가 증가합니다. 임계값 LOD 레벨 사이의 전환 영역에서 일반 맵과 전체 시차 오클루전 맵을 혼합합니다.

다중 레이어로 끈적끈적한 재료를 렌더링하려면 다중 레이어 재료를 렌더링해야 합니다. 다층 재료는 주로 반투명, 체적 재료, 참조 매체 렌더링 및 다중 매체 렌더링에 사용되며, 여기에는 계층 조합(층 간 폐색, 알파 형식으로 불투명도 저장), 깊이 시차(층 깊이 또는 두께로 인한 시차), 광원 확산(층 간 빛 산란) 및 기타 기술이 포함됩니다. 이 기사에서는 기존의 다중 텍스처 혼합 기술을 버리고 노멀 맵, 반투명 마스크, 평행 시차, 이미지 필터링 등을 포함한 새로운 조합 기술을 채택했습니다.

다층 재료 렌더링 사례: 심장.

간단히 말해서, 이 기사는 높은 계산 효율성과 우수한 시각 효과(볼륨 깊이 시차, 표면 아래 산란의 텍스처 블러링)라는 특성을 갖는 다층 재료 렌더링의 선례를 설정합니다.

게임의 실시간 대기 효과에서는 스카이라이트 렌더링, 전역 볼륨 안개 및 이들의 결합 효과에 대해 설명합니다.

이 기사에서는 또한 부드러운 입자와 구름체의 구현을 살펴봅니다.

부드러운 입자 비활성화(왼쪽) 및 활성화(오른쪽) 비교.

클라우드 렌더링은 픽셀별 깊이를 사용하고 지형에서 구름의 폐색 및 그림자 효과를 실현합니다.

구름 그림자 효과. 구름 그림자는 깊이를 사용하여 월드 공간 위치를 복원하고 그림자 맵 공간으로 변환하여 단일 전체 화면 패스로 캐스팅됩니다.

또한 이 기사에서는 픽셀별 깊이를 사용하여 강과 같은 수역을 렌더링하여 다양한 수중 깊이에서 다양한 색상의 효과를 나타낼 수 있다고 소개합니다.

Valve 소스 엔진의 셰이딩에서는 라디오시티 노멀 매핑 및 월드 조명을 위한 하이라이트 계산, 모델 조명을 위한 Irradiance Volume, Half-Lambert 및 Phong, 톤매핑, 자동 노출 및 HDR 렌더링을 위한 색상 교정을 포함하여 2006년 Source 엔진에서 사용된 셰이딩 기술에 대해 설명합니다.

방사형 조명을 사용한 조명은 더 현실적이고, 직접 조명보다 위도가 높으며, 열악한 조명 상황을 피하고, 영화처럼 조명을 사례별로 조정할 수 없기 때문에 콘텐츠 제작 광원을 세세하게 관리할 필요성이 줄어듭니다.

직사광(상단)과 이미시브(하단)만을 비교한 것입니다.

소스 엔진 셰이딩의 핵심은 라디오시티 및 노멀 매핑을 효율적으로 해결하기 위한 라디오시티 노멀 매핑을 생성하고, 전체 조명 환경을 새로운 기반으로 표현하여 모든 수의 조명에 대해 확산 범프 매핑을 효율적으로 수행하는 것입니다.

방사성 노멀 매핑의 기반입니다.

라이트맵 값을 계산할 때 라이트맵 값의 기존 광 전달 전처리 계산은 단일 색상 값만 계산합니다. Radiation Normal Mapping에서는 Base의 각 벡터의 빛 값을 계산하여 Light Map 저장 용량을 3배로 늘립니다. 그러나 소스 엔진 개발자는 이것이 품질과 유연성을 향상시키고 추가 오버헤드를 감당할 가치가 있다고 믿습니다.

세 가지 라이트맵 색상을 샘플링하고 변환된 벡터를 기반으로 색상을 혼합합니다.

cpp
float3 dp;
dp.x = saturate( dot( normal, bumpBasis[0] ) );
dp.y = saturate( dot( normal, bumpBasis[1] ) );
dp.z = saturate( dot( normal, bumpBasis[2] ) );
dp *= dp;
diffuseLighting = dp.x * lightmapColor1 + dp.y * lightmapColor2 + dp.z * lightmapColor3;

가변 조명 밀도 시각화.

소스 엔진은 일반적으로 하프 램버트를 사용하여 모델을 조명할 때 종료자에서 N·L을 0으로 자릅니다. 이는 -1에서 1까지의 코사인 항(빨간색 곡선)을 1/2로 스케일링하고 1/2로 바이어스한 다음 제곱하여 빛을 끝까지 끌어당깁니다(파란색 곡선).

간접 조명의 경우 소스 엔진은 셰이더 상수에 저장된 6개의 RGB 로브가 있는 Ambient Cube Basis를 사용합니다. 이는 처음 두 구형 고조파보다 더 간단한 기반(9개의 RGB 색상)입니다.

환경 큐브 기반 구현 세부정보입니다.

주변 큐브와 구면 고조파의 비교.

다른 게임(위)과 하프 램버트 및 환경 큐브를 사용한 비교(아래).

소스 엔진의 조명 계산 트리는 다음과 같습니다.

주변 조리개 조명에서는 가시성 조리개, 영역 조명, 부드럽고 단단한 그림자의 사용을 설명하고 이를 지형 렌더링에 적용합니다. 주변 조리개 조명이란 조리개를 사용하여 가시성 함수, 미리 계산된 가시성, 동적 구형 조명 및 포인트 라이트를 근사화하고 수평선 매핑과 유사하지만 영역 조명을 허용하는 하드 및 소프트 그림자를 지원하는 셰이딩 모델을 의미합니다. "주변"은 수정된 앰비언트 오클루전(AO) 계산을 사용하여 평균 가시성을 위한 조리개를 찾는다는 사실에서 비롯됩니다.

주변 조리개 조명은 두 단계로 작동합니다.

  • 사전 계산 단계. 가시성 함수는 정점별 또는 픽셀별로 메쉬의 각 지점에서 계산됩니다. 가시성 기능은 평균 연속 가시 영역을 저장하는 구형 캡을 사용하여 저장됩니다. 구형 캡은 평면에 의해 잘린 구형의 일부입니다(반구 자체가 구형 캡임).

  • 렌더링 단계. 구형 캡은 조리개 역할을 합니다. 조리개는 들어오는 빛을 제한하여 눈에 보이는(방해되지 않는) 방향에서만 빛이 들어오도록 하는 데 사용됩니다. 영역 광원은 반구에 투사되고 조리개에 고정되어 조리개를 통과하는 빛의 양을 결정합니다.

조리개 조명 다이어그램.

조리개를 사용하여 렌더링하는 과정은 다음과 같습니다.

  • 구형 광원을 반구에 투사합니다.

  • 투영된 영역 조명은 반구의 특정 영역을 덮습니다. 투영된 구는 구멍과 같은 구형 캡을 형성합니다.

  • 투사된 빛의 구형 캡과 조리개의 구형 캡의 교차점을 찾습니다.

  • 교차 영역이 발견되면 조리개를 통과하는 광원 부분이 알려집니다.

정확한 조명(상단)과 대략적인 조리개 결과(하단)의 비교.

동적 장면의 조명을 위한 빠른 근사화에서는 Small World 게임이 조명 근사치를 계산하기 위해 볼륨을 사용한다는 점을 주로 설명하고 방사조도 슬라이스, 부호 있는 거리 함수, 뷰 정렬 방사조도 볼륨과 같은 몇 가지 구체적인 응용 사례를 인용합니다.

방사 조도 슬라이싱의 목표는 사전 계산 없이 동적 평면 조명의 부드럽고 선명한 그림자를 혼합하는 것입니다. 광원과 평행하게 월드를 슬라이스하고 각 슬라이스에 텍스처를 저장합니다.

광선에 가장 가까운 것부터 시작하여 각 평면에서 순서대로 광선을 추적합니다. 광선이 이전 평면에 도달하면 추적이 중지되고 결과가 이전 평면에 반환됩니다.

SDF의 경우 대략적인 AO를 얻기 위해 표면 곡률을 측정하는 데 사용합니다. 접힌 부분과 함몰된 부분은 채광창을 덜 받기 때문에 표면 곡률이 좋은 시작입니다.

SDF 계산 AO 다이어그램.

Irradiance Volume은 당시 많은 게임에서 장면의 모든 지점을 통과하는 빛을 저장하고 샘플링하는 데 사용되었습니다. 일반적으로 월드 정렬되어 대략적인 해상도로 사전 계산되었으며, 종종 구면 조화 압축을 사용하여 모든 방향으로 흐르는 방사조도를 저장했습니다.

뷰 정렬 방사조도 볼륨은 뷰 정렬된 월드 정렬 방사조도 볼륨과 다릅니다. 동적 장면에서는 조사량을 미리 계산할 수 없으므로 잠재적으로 많은 수의 발광체를 기반으로 GPU를 사용하여 저해상도에서 매 프레임마다 동적으로 다시 계산됩니다. 텍스트의 예에서 제한된 작은 세계의 경우 화면 공간에서 계산하는 것이 합리적이므로 화면에 평행한 적은 수의 슬라이스(16)를 사용하고 역투영 공간에 고르게 분포됩니다. 즉, "w"(1/z)에 고르게 분포됩니다.

뷰 정렬된 조사량의 렌더링.

요약하면, 본 논문에서는 작은 장면의 체적 표현을 사용하여 아름다운 외관을 구현하는 3가지 새로운 기술을 제시합니다. 첫 번째는 장면에서 반복적인 크기 조정 및 흐림 슬라이싱을 통해 확실한 반그림자 효과를 생성할 수 있다는 것입니다. 두 번째는 "채광창"에서 폐색 정보를 빠르게 계산하여 일부 반사된 조명 효과와 함께 멋진 AO 모양을 제공하기 위한 GPU 업데이트 볼륨 텍스처입니다. 세 번째는 수많은 이동 광원으로부터 조명을 빠르게 계산하기 위한 화면 정렬 "조도 볼륨" 그라데이션입니다.

게임 루프 아키텍처 분석에서는 복잡성, 작은 적용 범위 및 최고 수준의 게임 아키텍처를 숨기는 것을 목표로 하는 게임 루프 아키텍처의 설계 및 구현에 대해 설명합니다. 이 문서에서는 게임 루프를 정의하는 의사코드를 제공합니다.

cpp
GameLoop()
{
    Startup();
    while (!done)
    {
        GetInput();
        Sim();
        Render();
    }
    Shutdown();
}

게임 루프에는 시작 단계, 입력/출력을 처리하는 루프 단계, 종료 단계 등을 포함하여 적어도 하나의 실행 스레드가 있습니다. 또한 이 기사에서는 게임 루프 아키텍처를 정의하여 1940년대에서 2000년대까지의 진화를 설명합니다.

게임 루프의 복잡성은 다음 다이어그램으로 설명할 수 있습니다. 게임 루프를 디자인할 때 가장 먼저 고려하고 가장 기본적인 것은 시간이다. 그런 다음 여러 게임 루프를 사용하기 시작하고 시스템 복잡성이 증가하며, 이 지점에서 루프 결합을 처리해야 합니다. 마지막으로 다중 CPU, 즉 동시성을 갖춘 플랫폼으로의 이동을 고려해야 합니다. 이때 복잡성이 크게 증가합니다. 흥미롭게도 "복잡성"을 시간의 화살표 또는 "프로세서 수" 화살표로 생각할 수도 있습니다. 게임 엔진이 발전함에 따라 이를 단일 스레드 단일 CPU, 멀티 스레드 단일 CPU, 멀티 스레드 다중 CPU로 생각합니다. 또한 이는 역사적인 타임라인 또는 게임 엔진이 여러 반복을 통해 진화하고 가장자리를 따라 이동하며 게임이 더욱 복잡해지고 더 많은 프로세서를 사용하는 것으로 볼 수도 있습니다. 이 그래프는 누적되는 그래프이므로 결합도와 시간을 해결하지 않으면 동시성 문제를 해결할 수 없습니다.

가장 안쪽의 시간 순환에서 실시간으로 원활하게 실행하려면 주파수 기반 게임 루프를 사용하여 시간을 개별 반복으로 나누고 충분히 빠르고 원활하게 실행되도록 노력할 수 있습니다. 결과적으로 각 루프 반복은 시간 조각입니다. 문제는 다양한 실행 시간으로 루프 빈도를 유지하여 성능, 허용 오차, 의사 결정 및 단순성에 영향을 미치는 방법입니다. 아키텍처 결정을 구현하는 방법에는 스케줄링과 시간 단계라는 두 가지 방법이 있습니다. 일정은 루프 반복이 시작되는 시기를 제어합니다.

스케줄링 모델: 즉시, 최적 일치, 정렬.

실시간 스케줄링 모드의 경우 가능한 한 블록 단위로 실행하는 것이 성능과 단순성 측면에서는 좋지만 허용 오차 측면에서는 좋지 않으며 의사 결정이 부족합니다. 실시간 스케줄링 모드는 정밀, 고속, 저속, 가변 등으로 세분화될 수 있습니다.

인스턴트 예약 모드는 초기 어드벤처 게임에서 일반적입니다. 사용 사례와 의사 코드는 다음과 같습니다.

최적의 스케줄링 모드의 경우 시간 균등성을 설명하고 프레임 속도를 유지하고 정확한 시간에 시작하여 프레임 속도를 따라가려고 시도합니다. 성능과 관용에는 좋지만 단순성과 의사 결정에는 도움이 되지 않습니다.

정렬된 스케줄링 모델은 수직 동기화를 설명합니다. 수직 동기화는 단순성에 좋고 성능과 허용 오차가 낮으며 의사 결정이 부족합니다.

Time Step의 시간 구조는 없음, 고정 값, 실시간의 세 가지 모드로 구분됩니다. 그 특징은 다음과 같습니다.

위의 가장 안쪽 원의 시간을 분석한 후 중간 원의 결합을 분석합니다. 결합의 문제는 서로 다른 주파수를 가진 시스템을 지원하는 것입니다. 이는 여러 게임 루프로 해결될 수 있으며, 이를 통해 루프 결합, 즉 각 루프 쌍 간의 종속성을 얻을 수 있습니다. 커플링 과제 코드와 데이터를 여러 루프로 분할하는 방법, 성능, 허용 오차, 단순성, 비디오 모드, 메모리, 확장성 등에 영향을 미치는 요소. 아키텍처 결정은 주파수 커플링과 데이터 커플링으로 나뉩니다.

주파수는 한 루프가 다른 루프의 주파수에 의존하는 정도를 나타냅니다. Equal, Multi, Decoupled의 3가지 방식으로 구분됩니다. 그 특징은 다음과 같습니다.

데이터 디커플링은 공유되는 데이터의 양과 방식을 엄격(Tight), 느슨함(Loose), 없음(None)으로 구분하여 기술합니다. 그 특징은 다음과 같습니다.

게임 루프의 가장 바깥쪽 링은 동시성입니다. 문제는 하드웨어 제조업체가 여러 CPU를 포함하는 하드웨어로 해결할 수 있는 최고의 성능을 요구한다는 것입니다. 결과적으로 게임 루프와 CPU 간의 매핑이 이루어지며 동시성을 형성하게 됩니다. 인터뷰의 과제는 성능, 단순성, 확장성과 같은 요소에 영향을 미치는 동시 실행을 관리하는 방법입니다. 아키텍처 결정에는 낮은 수준의 동시성(Low-level Concurrency)과 높은 수준의 동시성(High-level Concurrency)이 포함됩니다.

낮은 수준 동시성은 없음, 명령 및 기능의 세 가지 모드를 포함하는 게임 루프 내의 동시성입니다. 낮은 수준의 동시성에 대한 광범위한 적용 범위, 차세대 아키텍처로의 가장 쉬운 전환, 소규모로 시작하여 성장, 개방형 MP, 상향식 접근 방식으로 실행이 지연될 수 있습니다.

고급 동시성은 순차, 인터리브 및 병렬의 세 가지 모드를 포함하는 한 쌍의 게임 루프에 대한 병렬 처리입니다. 해당 설명은 다음과 같습니다.

다음 그림은 데이터 디커플링에서 Madden 게임이 사용하는 구체적인 전략을 보여줍니다.

Ritual™ Entertainment: Direct3D® 10의 차세대 효과에서는 DirectX10의 새로운 기능과 이를 사용하여 새로운 효과를 얻는 사례를 자세히 설명합니다. 기사에 언급된 DirectX10의 특징은 다음과 같습니다.

  • 일관성(콘솔과 유사). 모든 칩셋에 걸쳐 엄격하게 정의된 동작을 통해 대상으로 삼을 수 있는 보장된 기본 기능 세트입니다.

  • 성능이 향상되었습니다. 더욱 강력한 지오메트리 엔진으로 CPU 부담을 덜어주도록 설계하여 소규모 배치 성능을 획기적으로 개선합니다.

  • 더 나은 시각 효과. 새로운 하드웨어 기능으로 유연성과 프로그래밍 가능성이 향상되었습니다.

DirectX10은 텍스처 배열, 지오메트리 셰이더, 스트림 출력, 리소스 보기, 입력 어셈블러, 범용 셰이더 코어(SM 4.0), 정수/비트 명령, 비교 필터링, 상수 버퍼, 상태 개체, HDR을 위한 새로운 압축 형식, 일반/범프 맵 콘텐츠, 추가 텍스처, RT, 명령, 레지스터, 단계 간 통신, 예측 렌더링, 알파 적용 범위, 다중 샘플 읽기 등을 포함한 하드웨어 렌더링 파이프라인을 개선합니다.

소프트웨어 스택의 기능에는 간소화되고 계층화된 런타임, 깨끗하고 일관된 API, 강력한 디버깅 계층, 린 코어, 새로운 언어 기능을 갖춘 새로운 HLSL 컴파일러, 새로운 효과 시스템 및 새로운 Windows Vista™ 디스플레이 드라이버 모델 기반 구축이 포함됩니다.

유비쿼터스 리소스 액세스 리소스 보기 예: 큐브맵, 보기는 다양한 바인딩 위치의 리소스를 설명할 수 있습니다.

뷰는 리소스 데이터의 형식을 재해석할 수 있습니다.

DirectX9와 달리 DirectX10은 기하학 셰이더를 통해 모든 기본 정보(점, 선, 삼각형)에 액세스할 수 있습니다.

Geometry Shader(GS)를 통해 프리미티브별 재료 선택 및 설정, 가장자리 길이 및 주름 모델, 평면 방정식, 윤곽 가장자리 계산이 가능하고 무게 중심을 보간기 수를 초과하도록 설정할 수 있는 완전한 GPU 재료 시스템을 구현할 수 있습니다. 지정된 출력 유형(점, 선 스트립, 삼각형 스트립), 제한된 기하학적 증폭/역증폭의 프리미티브를 구성할 수 있습니다. 각 호출은 0-1024 값을 출력하고 더 이상 1 입력 및 1 출력 제한이 없으며 그림자 볼륨/털/날개, 절차적 기하학/세부 사항, 전체 GPU 파티클 시스템, 포인트 스프라이트 등을 구현할 수 있습니다.

GS는 또한 프리미티브의 RenderTargetArrayIndex와 같은 시스템 해석 값(System-Interpreted Value)을 트리거하고, 볼륨 렌더링을 위한 슬라이스를 선택하고, 큐브맵에 렌더링할 면을 선택할 수 있지만 MRT는 여전히 PS에 지정되어 있습니다.

또한 VS 또는 GS는 VS/GS 결과를 메모리에 있는 하나 이상의 버퍼로 스트리밍하는 스트림 출력을 지원합니다. DrawAuto()는 앱/CPU 개입 없이 동적인 양의 GS 데이터를 그릴 수 있습니다. 반복, 절차적 지오메트리 처리, 전체 GPU 입자 시스템 등에 사용할 수 있습니다. (아래 사진)

Feeding the Monster: Advanced Data Packaging for Consoles에서는 호스트 플랫폼의 리소스 로딩 문제, LIP 솔루션, C++ 개체 패키징 등에 대해 설명합니다. 기사에서는 차세대 데이터 요구 사항을 충족하려면 로드가 더 자주 발생해야 하며 광학 드라이브 성능은 메모리/CPU 전력에 따라 확장되지 않으므로 로드 성능이 최적이어야 하며 원시 디스크 전송 이외의 모든 처리가 제거되어야 한다고 언급합니다.

데이터 로딩을 위한 기존 전략은 로딩 화면을 사용하는 것이었지만 기술적으로 방해가 되고, 건너뛸 수 없는 컷신도 나을 것이 없어 시대적 요구에 맞지 않았습니다. 또 다른 전략은 스레드나 다른 프로세서에서 I/O 차단을 사용하는 백그라운드 로딩(Background Loading)입니다. 게임 자산은 게임 중에 로드되며 플레이어 몰입도는 유지됩니다. 하지만 당시 요구 사항은 로딩 화면보다 훨씬 느려서는 안 되고, CPU 오버헤드보다 낮아야 하며, 다른 IO를 차단해서는 안 된다는 것이었으므로 백그라운드 로딩의 로딩 성능이 최적이어야 하고 원시 디스크 전송 이외의 모든 처리가 제거되어야 합니다.

차세대 로딩 기술의 요구 사항은 하드웨어 전송 제한에 가까운 속도로 대량의 자산을 로드해야 하고, CPU 비용을 적게 들고 백그라운드 로딩을 ​​구현해야 하며, 메모리 조각화를 일으키지 않고 데이터 자산이 들어오고 나가야 한다는 것입니다.

로드 시간에는 메모리 공간 확보(언로드, 조각 모음), 검색 시간, 읽기 시간, 할당, 구문 분석, 재배치(포인터, 해시 ID 조회), 등록(예: 물리적 시스템) 등이 포함됩니다. 로드 시간을 줄이기 위한 전략은 다음과 같습니다.

  • 항상 압축된 파일을 로드하세요. N:1 압축을 사용하면 N배 더 빠르게 로드되고, 이중 버퍼링은 압축 해제 시간을 숨기고, 차세대 콘솔에서는 압축 해제에 많은 처리 능력을 사용할 수 있습니다.

  • CD 기능을 활용해 보세요. 자주 액세스하는 데이터는 디스크 외부에 저장하고, 음악 스트림은 중앙에 저장하고(전체 검색 방지), 일회용 데이터(비디오, 컷씬, 엔진 실행 파일)는 중앙 근처에 저장하고, 레이어 전환에 주의합니다(0.1초 소모).

  • 가벼운 디자인 패턴을 사용하세요. 형상 인스턴스화 및 애니메이션 공유 등이 있습니다.

  • 프로그래밍 기술을 우선시하십시오. 파라메트릭 표면, 텍스처(불, 연기, 물) 등.

  • 항상 오프라인으로 데이터를 준비하세요. 엔진에서 텍스트 또는 중간 형식 구문 분석을 제거하고, 데이터 변환 또는 해석에 낭비되는 엔진 시간을 제거하고, 기본 하드웨어 및 미들웨어 형식을 로드하고, C++ 개체를 직접 로드합니다.

또한 이 기사에서는 보다 자연스러운 데이터 처리, 자산 구문 분석 또는 해석 필요 없음, 빠른 생성, 포인터 재배치, 해시 ID 변환, 개체 등록 등을 포함하여 C++ 개체를 로드하는 이유에 대해 언급했습니다. C++ 개체를 로드하려면 멤버 포인터, 가상 테이블, 기본 클래스, 정렬 문제, 바이트 순서(엔디안) 등 매우 스마트한 캡슐화 시스템이 필요합니다. C++가 아닌 객체를 로드할 때 메모리에 읽어들여 사용할 수 있는 형식(텍스처/노멀 맵, Havok 구조, 오디오, 스크립트 바이트코드 등)이어야 하며 구현 및 사용이 매우 간단합니다.

기사에서는 게임 자산 패키징 및 로딩 솔루션인 **Load-In-Place(LIP)**라는 새로운 로딩 기술을 언급했습니다. 네이티브 C++ 개체를 정의, 저장 및 로드하기 위한 프레임워크입니다. 자체 조각 모음이 수행되는 게임 자산 컨테이너인 동적 저장소가 있습니다.

LIP 로딩 기술.

LIP 기술에는 LIP 항목(항목)이 포함됩니다. 하나의 LIP 항목은 하나의 게임 자산에 해당하고 하나의 LIP 항목은 고유한 해시 ID(64비트)에 해당하며, 이 중 32비트는 유형 ID 및 속성에 사용되고 나머지 32비트는 해시된 자산 이름(CRC-32)에 사용됩니다. LIP 항목의 예로는 조인트 애니메이션, 캐릭터 모델, 환경 모델 섹션, 충돌 바닥 섹션, 게임 개체(영웅, 적, 트리거 등), 스크립트, 파티클 이미터, 텍스처 등이 있습니다.

C++ 기반 LIP 항목은 원하는 수의 C++ 개체 및 배열로 구성될 수 있습니다. 디스크에서 모든 내부 포인터는 LIP 입력 블록을 기준으로 유지됩니다. 포인터 재배치는 재배치 생성자의 새 위치에서 시작됩니다. 내부 포인터는 생성자 연결을 통해 자동으로 재배치됩니다.

디스크에서 C++ LIP 항목을 성공적으로 로드하려면 new 연산자(구문: new(<address>) <type>;)를 오버로드하고, 메모리를 할당하지 않고 생성자를 호출하고, 가상 테이블을 초기화하고, 각 LIP ​​항목에 대해 기본 클래스 재배치 생성자를 한 번씩 호출해야 합니다. 그런 다음 모든 클래스와 구조에 필요한 생성자를 재배치합니다. LIP 프레임워크에서 로드할 수 있고 재배치해야 하는 멤버를 포함하며 3가지 유형의 생성자(재배치 생성자 로드, 재배치 생성자 이동(조각 모음), 동적 생성자(선택 사항, 가상일 수 있음)를 지원하지만 기본 생성자를 지원하지 않습니다. 객체 멤버 재배치를 위해 내부 포인터는 LIP 항목 블록 내를 가리키고 절대 포인터로 변환되어야 합니다. 외부 참조(LIP 항목만)는 LIP로 저장됩니다. 항목 해시 ID를 생성하고 전역 자산 테이블 항목의 참조된 LIP 항목에 대한 포인터로 변환됩니다. LIP 프레임워크는 모든 포인터 유형에 대한 적절한 생성자를 포함하는 래퍼 클래스를 제공합니다.

cpp
// --- GameObject정의 ----
class GameObject {
public:
    GameObject(const LoadContext& ctx);
    GameObject(const MoveContext& ctx);
    GameObject(HASHID id, Script* pScript);
protected:
    lip::RelocPtr<Transfo> mpLocation;
    lip::LipItemPtr<Script> mpScript;
};

// --- GameObject구현 ----
GameObject::GameObject(const LoadContext& ctx) :
    mpLocation(ctx),
    mpScript(ctx) {}

GameObject::GameObject(const MoveContext& ctx) :
    mpLocation(ctx),
    mpScript(ctx) {}

GameObject::GameObject(HASHID id, Script* pScript) :
    mpLocation(new Transfo),
    mpScript(pScript) { SetHashId(id); }

// --- new ----
template<typename LipItemT>
void PlacementNew(lip::LoadContext& loadCtx)
{
    new(loadCtx.pvBaseAddr) LipItemT(loadCtx);
}

// 로드
loadCtx.pvBaseAddr = pvLoadMemory;
PlacementNew<GameObject>(loadCtx);

C++ 기반의 LIP 항목 구성 단계 및 흐름도는 다음과 같습니다.

LIP에는 LIP 항목 그룹이자 로드할 수 있는 데이터의 가장 작은 단위인 로드 단위(Load Unit)도 있습니다. 하나의 로드 단위는 하나의 로드 명령에 해당하므로 파일 수를 최소화합니다. 일반적으로 언어 독립적인 하나의 파일(예: 모델, 애니메이션, 스크립트, 환경...)과 N 언어 관련 파일(글꼴, 게임 내 텍스트, 텍스처, 오디오...), 로드 단위 파일이 압축됩니다. 단위 로드에는 로드 단위 테이블도 포함되며, 각 LIP ​​항목에는 LIP 항목의 해시 ID, 오프셋을 포함하는 테이블 항목이 있습니다.

이 글은 동적 로딩을 사용하며, 로딩 과정은 다음과 같습니다.

  • 로드 유닛 파일을 읽고 사용 가능한 저장 메모리에 압축을 푼다.

  • 로드 유닛 테이블 오프셋이 재배치되었습니다.

  • 로드 단위 테이블 항목이 글로벌 자산 테이블에 병합되었습니다.

  • 각 LIP ​​항목에 대해 새 배치를 호출합니다.

  • 일부 LIP 항목 유형에는 두 번째 초기화 패스(예: 등록)가 필요할 수 있습니다.

동적 로딩의 언로드 프로세스는 다음과 같습니다.

  • 각 LIP 항목을 개별적으로 제거할 수 있습니다.

  • 로드 유닛의 모든 LIP 항목을 함께 제거할 수 있습니다.

  • C++ LIP 항목에서 소멸자를 호출합니다.

  • 동적 저장 알고리즘은 나중에 새 조각을 조각 모음합니다.

LIP 항목을 잠글 수 있습니다. 잠긴 항목은 이동하거나 제거할 수 없습니다.

또한 LIP는 웹 기반 자산 편집에 사용할 수 있으며 LIP 항목은 자산 크기 변경에 관계없이 게임 플레이 중에 레벨 외부 및 내부로 마이그레이션될 수 있습니다. 또한 LIP는 Maya 내보내기에 사용하여 중간 아트 자산을 저장할 수도 있으며 이는 XML을 구문 분석하는 것보다 훨씬 효율적입니다.

또한 이 기사에서는 편집기와 그 구현에 필요한 데이터, 가상 함수 테이블 데이터 정렬, 엔진에 필요한 정보 및 재배치에 사용되는 다양한 스마트 포인터에 대해 설명합니다.

엔진에 필요한 정보 중 하나: 해시 테이블을 입력합니다.

재배치를 위한 다양한 스마트 포인터: 약한 참조와 강한 참조 스마트 포인터.

게임 개발 모범 사례에서는 게임 아키텍처의 진화, 소프트웨어 제한 사항, 게임 아키텍처 동향 등에 대해 설명합니다.

소프트웨어 제한에는 물리적 법칙, 소프트웨어 규칙, 알고리즘 문제, 릴리스 난이도, 설계 문제, 조직적 중요성, 경제적 영향, 정치적 영향, 인간 상상력의 한계 등이 포함됩니다. 대부분의 효율성이 뛰어난 조직은 실행 파일의 점진적이고 반복적인 릴리스를 통해 아키텍처를 발전시킵니다.

게임 아키텍처의 힘은 다음과 같은 요인으로 인해 변곡점에 있었습니다.

  • 새로운 콘솔의 탄생. 극적인 기술 변화.

  • 새로운 게임 유형. 개발 스튜디오는 새로운 프로그래밍 모델을 배우고, 새로운 개발 도구를 구입하고, 단일 스레드 선형 실행 모델에서 멀티 스레드 병렬 실행 모델로 전환해야 합니다.

기사에서는 다음과 같이 소프트웨어에 영향을 미치는 많은 요소가 있다고 언급했습니다.

그리고 건축 소프트웨어는 동등한 물리적 법칙이 없고, 투명성이 다르며, 구체적인 복잡성(상태 공간의 조합 폭발, 불연속 동작, 체계적 문제), 요구 사항 및 기술 손실, 복제 및 배포 비용이 저렴하다는 점을 지적했습니다. 소프트웨어 엔지니어링의 전체 역사는 컴퓨터 언어, 플랫폼, 프로세스, 아키텍처, 도구, 지원 등의 측면에서 발전하면서 끊임없이 높아지는 추상화 수준 중 하나입니다.

아키텍처를 설계하는 이유에는 생산적인 프로젝트에서 실행 가능한 아키텍처 개발을 중심으로 프로세스를 집중시키는 것이 포함됩니다. 잘 구성된 시스템은 패턴이 풍부하고 위험에 강하며 단순하고 탄력적입니다. 다음 그림은 기사에서 제안된 여러 소프트웨어 아키텍처 메타 모델을 보여줍니다.

교차 기능 메커니즘에는 일부 구조적 및 동작 교차 구성 요소(예: 보안, 동시성, 캐싱, 지속성)가 포함되며, 이는 종종 시스템 전체에 흩어져 있는 작은 코드 조각으로 나타나며 기존 방법을 사용하여 지역화하기가 어렵습니다. 이 기사에서는 4+1 보기의 소프트웨어 아키텍처 모델도 언급합니다. ABIO의 배치 뷰와 논리적 뷰는 다음과 같습니다.

이 기사는 또한 소프트웨어 경제성의 효율성 향상에 대해 설명하고 다음 공식을 제공합니다.

구축 시간 또는 비용=복잡성프로세스 ×  × 도구

그 중에는:

  • 복잡도는 수동으로 생성된 코드의 양을 나타냅니다.

  • 프로세스는 방법, 표기, 성숙도를 나타냅니다.

  • 은 기술, 경험, 동기를 나타냅니다.

  • 도구는 프로세스 자동화를 의미합니다.

아래 그림의 2차원 평면으로 측정할 수 있는데, 가로축은 느슨함에서 엄격함까지, 세로축은 폭포형에서 반복형까지입니다. 그들은 각각 다른 특성을 가지고 있습니다:

일반적인 기술 스택은 다음과 같습니다.

2007년은 DirectX 10이 출시된 다음 해입니다. 렌더링 성능을 최적화하고 몇 가지 새로운 시각적 효과를 달성하기 위한 새로운 렌더링 파이프라인과 기능의 사용을 설명하는 문서가 이미 많이 있습니다. Direct3D 10 과정 소개가 그 중 하나입니다. 이 문서에서는 성능을 극대화하기 위해 애플리케이션에서 DX10을 사용하는 모범 사례에 대해 설명합니다. 데이터를 생성하거나 업데이트해야 할 때마다 파이프라인은 어떤 방식으로든 중지되며, 데이터를 파이프라인으로 전송해야 하는 빈도를 제어함으로써 각 상태 업데이트, 리소스 생성 또는 진행 중인 수정의 오버헤드를 최소화할 수 있습니다. 작업을 가장 바깥쪽 루프 밖으로 이동하면 효율성이 크게 향상될 수도 있습니다.

아래 그림은 당시 인기 있는 FX 아키텍처를 보여줍니다.

또한 이 기사에서는 종속성 그래프를 사용하여 리소스 종속성과 리소스 업데이트를 추적할 것을 권장합니다. 리소스 종속성을 사용하면 중복된 메모리 인스턴스와 데이터를 제거할 수 있습니다.

고정 상수의 경우 먼저 CPU 주파수별로 구성하는 것이 좋습니다. 이렇게 하면 API 호출을 최소화하고 CPU-GPU 대역폭을 줄일 수 있습니다. 둘째, 필요한 셰이더 태그에 따라 구성하고 머티리얼 종속성을 최소화한 다음 4096 크기의 float4 버퍼에 넣습니다.

상수를 패키징할 때 주의해야 할 세부 사항도 많이 있습니다.

DX10을 사용하면 잔디, 모래, 자갈과 같은 더 나은 효과를 얻을 수 있습니다.

동시에 수많은 실시간 전역 조명 계산이 천천히 발견되어 다양한 렌더링 엔진에 도입되고 있습니다. Irradiance Caching을 사용한 실용적인 Global Illumination이 최고의 증거입니다. 이 문서는 실제로 무작위 레이 트레이싱, 조도 캐싱 알고리즘, 복사 조도 캐싱, 광자 매핑, 광택 반사, 시간적 일관성, 조도 분해, 관련 소프트웨어 및 하드웨어 구현을 다루는 실시간 레이 트레이싱에 대한 일련의 기사입니다. radiance의 irradiance 캐시 알고리즘의 단계는 다음과 같습니다.

  • 환경 상수 계산.

소위 "환경 용어"는 무한 계열의 나머지 부분에 가깝습니다.

  • 최고 간접 조사량의 평균은 좋은 근사치입니다.

  • 시간이 지남에 따라 조사 캐시가 채워지기 때문에 이동 평균을 사용할 수 있습니다.

  • 환경 용어를 과대평가하는 것은 과소평가하는 것보다 더 나쁩니다.

  • 반구형 적응형 슈퍼샘플링.

간접 조사 통합의 정확성을 최대화합니다.

  • 이웃 감지 분산을 기반으로 분산이 큰 영역을 오버샘플링하고 오류가 투영된 반구 전체에서 일관되거나 샘플링 한계에 도달할 때까지 샘플링합니다.

  • 최대 및 최소 레코드 간격.

최소 레코드 간격이 없으면 내부 모서리가 픽셀 수준으로 해석됩니다.

  • 최소 간격을 적용하면 특정 장면 규모에서 정확도가 점차 감소합니다.

  • 최대 간격은 최소 간격의 64배이며 이는 올바른 것 같습니다.

  • 레코드 간격에 대한 그라데이션 제한.

||gradient||*spacing > 1이 아니면 그라데이션은 간격을 제어하지 않습니다.

  • 그런 다음 음수 값을 방지하고 정확도를 높이려면 간격을 줄일 수 있습니다.

  • 최소 간격에 도달하면 대신 그라데이션이 줄어듭니다.

  • 회전 그래디언트를 사용한 범프 매핑.

견고한 표면은 기록 공유를 감소시킵니다.

  • 범프 맵을 무시하고 방금 계산한 방사조도에 회전 그라데이션을 적용할 수 있습니다.

  • 최적의 재사용 및 간격을 촉진하고 샘플 누출 문제를 방지합니다.

  • 표면/재료를 제외하는 옵션.

사용자가 선택한 재료(및 사용자가 수정하는 표면)는 간접적으로 제외될 수 있습니다.

  • 잔디와 같은 필드에서 무의미한 상호 반사 계산 시간을 절약할 수 있습니다.

  • 몇 가지 재료만 포함된 경우 포함 목록을 지정할 수 있습니다.

  • 두 번째 유형의 상호반사 계산이 가능하면 좋을 것 같습니다.

  • 다중 프로세서에 대한 공유 데이터 로깅.

후속 보기에 기록을 재사용하는 것 외에도 방사조도 캐시 파일을 사용하여 여러 프로세스 간에 기록을 공유할 수 있습니다.

  • 동기화 : 잠금 → 읽기 → 쓰기 → 잠금 해제.

  • 다른 프로세스의 기록을 읽어들인 후, 이 프로세스의 새로운 기록을 기록합니다.

  • NFS 잠금 관리자가 항상 신뢰할 수 있는 것은 아닙니다.

조명 변화율이 높고 녹화가 충분하지 않아 보간 아티팩트가 발생할 수 있는 경우 적응형 방사 캐시가 이 문제를 해결할 수 있습니다.

특히 간접 조명의 공간 샘플링 밀도는 샘플링 밀도가 장면 형상에만 기초하여 조정되는 조도 캐시와 달리 실제 로컬 조명 조건에 맞게 조정됩니다. 이전 방법과 비교하여 새로운 방법은 렌더링 효과를 향상시킬 수 있습니다.

이 기사에서는 옥트리 저장 및 순회 프로세스와 같은 GPU의 구현 세부 사항에 대해서도 설명합니다.

GPU에서 I2C(Irradiance Cache)를 더 잘 구현하기 위해 기사에서는 알고리즘을 다시 사용자 정의했습니다. 단계는 다음과 같습니다:

이 일련의 문서에서는 기타 기술 분석, 구현 세부 정보 및 다른 방법과의 비교도 제공합니다. 원문을 보려면 클릭하는 것이 좋습니다.

3D 그래픽 및 게임의 고급 실시간 렌더링에서는 지형 렌더링, 표면 분할, CryEngine 2의 건축 설계 및 조명 기술, GPU 파티클, 셀프 섀도잉 등을 포함한 다양한 렌더링 기술에 대해서도 설명합니다. 기사에서는 Valve의 Source 엔진이 거리 필드를 사용하여 Alpha-Tested의 재료 효과를 향상한다고 언급했습니다.

64x64 텍스처 인코딩을 사용한 벡터 효과. (a)는 단순 이중선형 필터링입니다. (b)는 알파 테스트입니다. (c)는 거리장 기술이다.

디스턴스 필드 생성은 고해상도 입력 텍스처에 의존합니다.

(a) 고해상도(4096×4096) 이진 입력은 (b) 저해상도(64×64) 거리 필드를 계산하는 데 사용됩니다.

생성된 디스턴스 필드 정보를 사용하여 고품질 앤티앨리어싱 중공 재질을 렌더링할 수 있으며 부드러운 가장자리와 단단한 가장자리, 광선, 획, 부드러운 그림자, 날카로운 각도 등과 같은 효과까지 지원할 수 있습니다. Valve에서 출시한 게임 "Team Fortress 2"에서는 양식화된 셰이딩 모델이 사용됩니다. 구체적인 단계는 아래 그림에 나와 있습니다.

하위 문서 애니메이션 주름 맵에서는 보간을 통해 표정 간의 법선을 얻고 얼굴 표정을 보다 정확하고 자연스럽게 일치시키기 위해 다양한 표정에 매핑된 여러 노멀 맵의 사용에 대해 설명합니다.

)

루비의 얼굴 텍스처(왼쪽에서 오른쪽으로): 알베도 맵, 탄젠트 공간 노멀 맵, 얼굴 스트레칭 후 탄젠트 공간 주름 맵 1. 얼굴 압축 후 탄젠트 공간 주름 맵 2.

8개의 주름 마스크는 두 텍스처의 색상 채널과 알파 채널 사이에 분포되어 있습니다(흰색은 알파 채널의 내용을 나타냄). [왼쪽] 왼쪽 눈썹(빨간색), 오른쪽 눈썹(녹색), 중간 눈썹(파란색), 입술(알파)용 마스크. [오른쪽] 왼쪽 뺨(빨간색), 오른쪽 뺨(녹색), 왼쪽 위 뺨(파란색), 오른쪽 뺨(알파) 마스크입니다. 여기에 표시되지 않은 턱 마스크도 사용되었습니다.

이 기술은 수년 후 언리얼 엔진에서 디지털 휴먼의 표현 렌더링에 사용되었다는 점은 언급할 가치가 있습니다. 자세한 내용은 저자의 다른 글인 언리얼 엔진의 초현실적 휴먼 렌더링 기술 분석 1부 - 개요 및 피부 렌더링을 참조하세요.

절차적 셰이더 스플래팅을 사용한 Frostbite의 지형 렌더링 하위 장에서는 Frosbite 엔진이 절차적 셰이더 스퍼터링을 사용하여 엔진의 지형 렌더링을 개선하는 방법을 설명합니다. 엔진 팀은 그래픽 기반 표면 셰이더가 지형 텍스처 합성 및 분포를 제어하여 지형 재질을 개별적으로 특화하여 성능, 메모리, 시각적 품질 및 작업 흐름의 균형을 맞추는 유연한 지형 렌더링 프레임워크 및 기술인 절차적 셰이더 스플래팅(Procedural Shader Splatter)을 제안했습니다. 이 기술을 통해 엔진은 원거리 및 근거리 모두에서 높은 시각적 품질을 유지하고 메모리 사용량을 낮게 유지하면서 지상 파괴를 위한 동적 높이 필드 수정을 지원할 수 있습니다. 부시의 절차적 인스턴스가 시스템에 통합되어 있으며, 지형 재료 분포 및 셰이더를 사용하는 것은 메모리 및 콘텐츠 생성 시 저렴한 비용으로 시각적 세부 정보를 추가할 수 있는 매우 강력한 도구이자 간단한 방법입니다.

Frosbite 엔진의 지형 렌더링 효과.

Frostbite 렌더링 아키텍처 및 실시간 절차적 셰이딩 및 텍스처링 기술은 2007년 Frostbite 엔진 렌더링 아키텍처, 렌더링 기술 및 관련 응용 프로그램을 설명합니다. Frostbite로 개발된 콘솔 게임 Battlefield: Bad Company는 파괴 가능한 대규모 풍경, 파괴 가능한 건물 및 개체, 파괴 가능한 나뭇잎이 있는 대규모 숲, 자동차(지프, 탱크, 보트 및 헬리콥터), 동적 하늘, 동적 조명 및 그림자 등과 같은 기능을 지원합니다. 렌더링 아키텍처 다이어그램 이 단계의 동상은 다음과 같습니다.

위 그림의 파란색은 메인 시스템이고 녹색은 렌더링 하위 시스템입니다. 셰이딩 시스템은 쉽고 빠른 고품질 셰이딩을 위해 렌더링, 셰이딩 및 조명을 단순화하고 일반화하는 플랫폼 독립적인 고급 렌더링 API를 갖추고 있으며 GPU 및 플랫폼 API와의 대부분의 통신을 처리합니다.

색칠 시스템은 여러 이미지 API와 고급 색칠 상태도 지원합니다. 고급 색상 지정 상태는 이미지 API와 관련이 없으므로 상위 사용자와 시스템이 더 쉽게 사용할 수 있고 중복 코드가 줄어듭니다. 고급 셰이딩 상태의 사용 사례에는 조명(양, 색상, 유형, 그림자), 지오메트리 처리(스키닝, 인스턴싱), 효과(안개, 빛 산란), 표면 셰이딩(VS, PS) 등이 있습니다. 요약하면, 높은 수준의 셰이딩 상태는 사용자가 사용하기 쉽고 효율적이며, 시스템 간에 기능을 공유 및 재사용하고, 헬 모드 셰이더 배열을 숨기고 관리하며, 셰이더 파이프라인으로 일반화 및 중앙화되고, 플랫폼은 상태를 다르게 구현할 수 있습니다(기능에 따라). 단일 패스 대신 다중 패스 조명.

초기 Frostbite용 컬러링 비주얼 편집기입니다.

Frostbite 셰이딩 시스템 파이프라인은 다음과 같습니다.

  • 크고 복잡한 오프라인 전처리 시스템. 시스템은 원하는 상태 조합을 보고합니다.

  • 런타임용 셰이딩 솔루션을 생성합니다. 각 셰이딩 상태 조합에 대한 솔루션(예: 흐름 인스턴스화, 표면 셰이더, 빛 산란 및 실외 조명과 그림자의 영향을 받는 메시, Xbox 360용 2개의 포인트 라이트).

  • HLSL 버텍스 및 픽셀 셰이더를 생성합니다.

  • 솔루션에는 완전한 상태 설정이 포함되어 있습니다. 채널, 셰이더, 상수, 매개변수, 텍스처 등과 같은

Frostbite 셰이딩 시스템이 실행되는 단계는 다음과 같습니다.

  • 사용자가 렌더링 블록을 대기열에 푸시합니다. 형상 및 고급 상태 조합.

  • 상태 조합에 대한 솔루션을 찾아보세요. 파이프라인의 오프라인 단계에서 생성됩니다.

  • 렌더 블록은 백엔드에 의해 D3D/GCM으로 전송됩니다.

렌더 블록이 정렬됩니다(범주 및 깊이).

  • 플랫폼별 상태 및 셰이더에 대한 백엔드 설정. 솔루션의 파이프라인에 따라 결정되며 가볍고 조용합니다.

  • 그리다.

Frostbite의 월드 렌더러는 3D 세계 렌더링, 월드 뷰 관리 및 하위 시스템(예: 지형, 그리드 및 포스트 프로세스) 렌더링을 담당합니다. 활성화된 기능에 따라 뷰를 여러 하위 뷰로 분할할 수 있습니다(그림자 맵 렌더링을 위한 깊이 전용 그림자 뷰, 동적 환경 맵을 위한 단순화된 뷰). 월드 렌더러는 3단계로 나누어집니다:

  • 컬. 각 뷰에 대해 표시되는 엔터티 및 빛/그림자/엔티티 상호 작용을 수집하고, 표시되는 모든 엔터티에서 렌더링에 필요한 엔터티 데이터를 복사하며, 렌더링하는 동안 데이터가 변경될 수 있으므로 멀티스레딩이 필요합니다(나쁨).

  • 짓다. 표시되는 엔터티는 하위 시스템(예: 메시 렌더러)과 통신하는 해당 엔터티 렌더러로 전달되고 하위 시스템은 렌더링 블록 및 상태(셰이딩 시스템의 뷰별로 대기열에 추가됨)를 구축합니다.

  • 렌더링. 실제 렌더링을 위해 각 뷰의 셰이딩 시스템에서 대기 중인 렌더링 블록을 플러시하고 뷰에 포스트 프로세스(블룸, 톤매핑, DOF, 색상 교정 등)를 적용합니다.

모두 별도의 스레드(아래 그림)에서 실행되고, 멀티 코어 콘솔과 PC가 필요하며, 이중 버퍼 및 계단식 방식으로 실행됩니다(동기화 및 흐름 단순화).

실외 광원은 혼합 이미지 기반 현상학으로 모델링되고 확산 태양과 하늘 빛은 분석적(3 방향: 태양, 하늘, 땅)이며 제어가 매우 쉽습니다. 하늘의 스페큘러 반사는 이미지 기반(동적 큐브맵)이며 밉맵은 가변 거칠기로 사용되며 태양의 스페큘러 반사는 정밀한 분석에 사용할 수 있습니다. 통합된 반사 러프니스, 단일 [0,1] 러프니스 값은 픽셀마다 다를 수 있는 큐브맵 밉맵 바이어스(하늘) 및 분석적 반사 인덱스(태양)를 제어합니다.

같은 해 CryEngine 2는 DirectX 10을 활용하여 다음 기능을 지원했습니다.

  • 각각 고유한 특성을 지닌 다양한 장면 환경을 지원합니다.

CryEngine 2는 정글, 외계인 내부, 얼음과 눈 등과 같은 다양한 유형의 장면을 렌더링합니다.

  • 불쾌한 계곡을 손상시키지 않으면서 영화 같은 품질의 렌더링.

  • 동적 빛과 그림자. 미리 계산된 조명은 성능과 품질을 향상시키는 많은 알고리즘에 매우 중요하며 동적 조명과 그림자를 사용하면 일반적으로 정적 속성에 의존하기 때문에 이러한 알고리즘의 대부분을 사용할 수 없습니다.

  • 다중 GPU 및 다중 CPU(MGPU 및 MCPU)를 지원합니다. 멀티스레딩 및 여러 그래픽 카드를 사용한 개발은 다른 구성을 손상시키지 않으면서 훨씬 더 복잡하고 어려운 경우가 많습니다.

  • 4km × 4km의 대규모 장면을 지원합니다.

  • 셰이더 모델 2.0~4.0(DirectX10)의 GPU를 대상으로 합니다.

  • 높은 다이내믹 레인지. HDR은 Far Cry에서 큰 효과를 내는 데 사용되었으며, 사실적인 모습을 위해 LDR의 제한 없이 게임을 개발할 수 있었습니다.

  • 동적 환경(취약함). 가장 멋진 기능 중 하나이지만 구현하기가 쉽지 않습니다.

빛과 그림자 측면에서 CryEngine 2는 템플릿 그림자를 버리고 고품질의 부드러운 그림자가 있는 그림자 맵을 선택하며 더 나은 성능이나 품질을 위해 그림자 맵을 조정할 수 있습니다. 직접 조명 측면에서는 동적 오클루전 맵, 화면 공간 무작위 검색이 포함된 그림자 맵, 광원 공간 무작위 검색이 포함된 그림자 맵, 그림자 폐색 텍스처, 지연 그림자 폐색 생성, 점 조명 확장 그림자 맵, 분산 그림자 맵(VSM) 및 기타 기술이 사용됩니다.

CryEngine 2의 결과 품질이 다른 섀도우 맵의 예. 왼쪽에서 오른쪽으로: PCF 없음, PCF, 8개 샘플, 8개 샘플 + 흐림, PCF + 8개 샘플, PCF + 8개 샘플 + 흐림.

CryEngine 2에는 무작위로 보이는 그림자 맵 예가 있습니다. 왼쪽 상단: 지터가 없는 샘플 1개, 오른쪽 상단: 화면 공간 노이즈 샘플 8개, 왼쪽 하단: 월드 공간 노이즈 샘플 8개, 오른쪽 하단: 설정이 조정된 월드 공간 노이즈 샘플 8개.

CryEngine 2 주어진 장면에 대한 그림자 마스크 텍스처의 예: 왼쪽: 태양(그림자 캐스터로)과 두 개의 그림자 캐스터 조명을 사용한 최종 렌더링, 오른쪽: RGB 채널에 세 개의 조명이 있는 무광택 텍스처.

주어진 장면에 대한 섀도우 마스크 텍스처의 CryEngine 2 예 - 빨간색, 녹색 및 파란색 채널은 3개의 별도 조명에 대한 섀도우 마스크를 저장합니다.

CryEngine 2에서 분산 그림자 맵을 장면에 적용하는 예입니다. 위: 분산 그림자 맵 없음(하드 노멀 그림자 참고), 아래: 분산 그림자 맵 포함(두 가지 그림자 유형이 결합되는 방식 참고).

간접광 측면에서 CryEngine 2는 3D Transport Sampler, 실시간 환경 맵, SSAO 및 기타 기술을 사용했습니다. 당시 이는 새롭고 획기적인 렌더링 기술이었습니다.

그 중 3D 전송 샘플링은 성능상의 이유로 여러 시스템에 분산된 전역 조명 데이터를 계산할 수 있습니다. 광자 매핑은 전역 조명을 계산하는 데 사용되며, 이는 쉽게 통합되고 좋은 결과를 신속하게 제공할 수 있습니다.

단일 광원의 CryEngine2 실시간 환경 맵.

CryEngine 2의 SSAO 시각화 및 비교 차트가 장면에 적용되었습니다.

또한 CryEngine 2는 용해, FFT, 사각 워터 팬 및 화면 공간 표면 세분화(아래 그림)와 같은 기술을 사용하여 다양한 상황에 따라 수역, 지형, 메시 및 기타 객체에 대한 세부 LOD 기술을 수행합니다.

CryEngine 2의 화면 공간 테셀레이션 와이어프레임.

왼쪽: 가장자리 폴오프가 없는 화면 공간 테셀레이션(왼쪽에서 물로 덮이지 않은 영역 참고), 오른쪽: 가장자리 폴오프가 있는 화면 공간 테셀레이션.

차세대 엔진으로 CryEngine 2는 주로 품질, 생산 시간, 성능 및 확장성 때문에 위의 기술을 선택했습니다. 게임 '크라이시스'에서 성공적으로 검증돼 당시 눈길을 끄는 주류 엔진 중 하나가 됐다.

게임 엔진 기반 가상 현실 수술 시뮬레이터를 위한 협업 소프트 객체 조작(Collaborative Soft Object Manipulation for Game Engine-Based Virtual Reality Surgery Simulator)에서 언급한 게임 엔진의 추상 아키텍처:

본 글에서는 UE, id Tech, Source Engine 등 엔진의 특성을 분석하고, Virtual Surgery를 시뮬레이션하기 위한 엔진 선택에 대한 평가 전략을 제시합니다.

조작하는 사용자(위)와 관찰하는 사용자(아래)가 본 변형 심장 모델.

Delta3D 게임 및 시뮬레이션 엔진: 진지한 게임에 대한 오픈 소스 접근 방식에서는 오픈 소스 3D 엔진 Delta3D의 프로젝트 개발에서 얻은 경험과 교훈을 설명하고, 게임 기반 오픈 소스 시뮬레이션 엔진을 구축하여 제품 성공 확률을 높입니다. Delta3D의 아키텍처는 아래 그림과 같습니다.

Delta3D가 지원하는 기능은 아래 그림에 나와 있습니다.

렌더링 효과는 다음과 같습니다.

소프트웨어 요구 사항 사양 공통 인프라 팀은 대화형 게임 엔진의 아키텍처 다이어그램을 언급했습니다.

애플리케이션, 플랫폼 프레임워크, 게임 엔진에 필요한 가장 일반적인 요소이며 메시징, 상태 유지 관리, 엔터티 등록 등의 작업을 용이하게 합니다.

애플리케이션 개발을 촉진하기 위한 프레임워크 공통 기능. 또한 프레임워크는 애플리케이션 수준 개발 속도를 높이기 위해 플랫폼에 일부 인터페이스를 제공합니다.

게임 세계는 상태 변경, 게임 로직 및 렌더링을 처리하는 많은 하위 모듈로 구성됩니다. GUI의 이벤트는 게임 컨트롤러로 전송되어 나머지 하위 모듈에 필요에 따라 동작을 변경하도록 경고합니다.

조명 렌더링 측면에서 Interactive Relighting with Dynamic BRDFs는 PRT와 동적 BRDF를 기반으로 하는 새로운 BRDF 통합 방법을 제안합니다. 구체적인 접근 방식은 동적 BRDF의 전역 조명 효과를 고려하기 위해 전송된 입사 방사선을 장면의 조명 및 BRDF의 함수로 미리 계산하는 것입니다. 복사 전달과 BRDF 사이의 비선형 관계 문제를 극복하기 위해 미리 계산된 전달 텐서 기반 기술이 채택되었습니다. 또한 표면 점의 BRDF 공간은 텐서 근사법으로 표현되므로 세 가지 장면 조건 중 하나를 변경할 때 빠른 음영 처리가 가능합니다. 전송된 입사 광도 및 BRDF 텐서 기반을 사용하면 동적 BRDF 및 해당 전역 조명 효과를 사용하여 장면의 런타임 렌더링을 효율적으로 수행할 수 있습니다.

위 그림에서:

  • Ix(l,ωi)는 투과된 입사 방사선이며, 조명 및 BRDF(사전 계산된 투과 텐서)의 함수로 미리 계산됩니다.

  • f(ωo,ωi)는 BRDF 공간으로, 대략 빠른 셰이딩을 위한 텐서로 표현됩니다.

  • Bx(ωo)는 전송된 입사 방사선 및 BRDF 텐서 기반을 기반으로 계산된 런타임 렌더링입니다.

Ix(l,ωi)를 미리 계산된 전송 텐서(PTT)로 변환하는 프로세스는 다음 일련의 그림에 표시됩니다.

다양한 광선 경로의 투과된 방사선은 PTT에서 별도로 처리됩니다. PTT는 조명과 BRDF의 선형 함수입니다. PTT는 런타임 시 신속하게 결합되어 전체 전달된 입사 방사선을 얻을 수 있습니다. f(ωo,ωi)텐서 근사 과정은 다음 일련의 그림에 표시됩니다.

최종 합성된 런타임 렌더링 방정식은 다음과 같습니다.

일련의 흐름 아래:

렌더링 효과는 다음과 같습니다.

이 기사에서는 렌더링 중 구체적인 구현 세부 정보와 성능 분석도 제공합니다. 공간의 제약으로 인해 여기서는 자세히 설명하지 않습니다. 관심 있는 학생은 원본 기사를 읽어볼 수 있습니다.

2008년이 왔습니다. Advances in Real-Time Rendering in 3D Graphics and Games(SIGGRAPH 2008 과정)에서는 Halo 3 조명 및 재료, StarCraft II 렌더링, 가상 텍스처, GPU 병렬 시뮬레이션, 하드웨어 수준 웨이블릿 등을 포함하여 그 해 실시간 렌더링 분야의 최신 연구 결과, 렌더링 기술 및 실제 응용 프로그램에 대해 광범위하게 설명했습니다.

Halo 3는 Cook Torrance BRDF, 구형 조화 조명 맵 및 디퓨즈, 다양한 반사 하이라이트(분석 하이라이트, 환경 맵 하이라이트, 지역 하이라이트) 및 기타 기능을 지원합니다.

Halo 3은 2차 SH의 조화 라이트맵 텍스처를 사용합니다.

영역 하이라이트를 위해 Halo 3에 사전 통합된 텍스처입니다. 왼쪽에서 오른쪽으로: C(0,2,3,6), D(0,2,3,6) 및 CD(7,8). 가로축은 뷰 변화를 나타내고, 세로축은 러프니스 변화를 나타냅니다.

Halo 3 렌더링 스크린샷.

고급 가상 텍스처 항목에서는 가상 텍스처가 텍스처 메모리에 부분적으로만 상주하면서 실시간 렌더링을 위해 고해상도 텍스처를 시뮬레이션할 수 있도록 캐시로 사용되는 밉맵 텍스처임을 나타냅니다. 이 기능은 최신 세대의 상용 GPU에서 사용할 수 있는 효율적인 픽셀 셰이더 기능을 통해 이미 가능합니다. 가상 텍스처 사용으로 인해 엔진 설계에 미치는 기술적 영향, 콘텐츠 생성 문제, 결과, 성능 및 이미지 품질이 논의되고, 여러 가지 실제 적용 사례가 제시되어 과제를 강조하고 솔루션을 제공합니다. 가상 텍스처에는 텍스처 필터링, 블록 압축, 부동 소수점 정밀도, 디스크 스트리밍, UV 경계, 밉맵 생성, LOD 선택 등과 같은 기술적 세부 사항이 포함됩니다.

가상 텍스처 방식의 일반적인 사용 시나리오.

가상 텍스처 사용으로 이점을 얻을 수 있는 장면의 예 - Crysis 게임의 데칼(도로, 타이어 자국, 흙)은 지형 재료 혼합 위에 사용됩니다.

March of the Froblins: GPU에서 지능적이고 세밀한 생물의 대규모 군중을 시뮬레이션하고 렌더링하는 기능이 AMD에서 제공됩니다. 데모 프로그램 Froblin은 대규모 스킨 캐릭터의 시뮬레이션, 렌더링 및 AI를 시연하기 위해 특별히 개발되었습니다.

프로블린 데모 화면.

Froblin 동적 길찾기 시각화.

Froblin의 애니메이션 텍스처 레이아웃. 변환은 텍스처 배열을 사용하여 3x4 매트릭스로 저장됩니다. 여기서 수평 및 수직 크기는 키프레임 및 뼈대 인덱스에 해당하고 슬라이스 번호는 애니메이션 시퀀스를 인덱스하는 데 사용됩니다. 텍스처 필터링 하드웨어를 사용하여 텍스처의 한 축을 따라 시간을 변경하는 것은 키프레임 간에 보간될 수 있습니다. 각 정점의 골격 영향을 가중치에 따라 정렬하고 동적 분기를 사용하여 가중치가 0인 뼈를 얻는 것을 방지하면 대부분의 정점에 뼈 영향이 2개 이상 없기 때문에 성능이 크게 향상될 수 있습니다.

hlsl
// 을(를) 활용하여 페치/가져오기(Fetch)、 및 블렌딩(Blending)의 셰이딩 코드

float fTexWidth;
float fTexHeight;
float fCycleLengths[MAX_SLICE_COUNT];
Texture2DArray<float4> tBones;
sampler sBones; // should use CLAMP addressing and linear filtering

void SampleBone( uint nIndex, float fU, uint nSlice, out float4 vRow1, out float4 vRow2, out float4 vRow3 )
{
    // compute vertical texture coordinate based on bone index
    float fV = (nIndices[0]) * (3.0f / fTexHeight);
    // compute offsets to texel centers in each row
    float fV0 = fV + ( 0.5f / fTexHeight );
    float fV1 = fV + ( 1.5f / fTexHeight );
    float fV2 = fV + ( 2.5f / fTexHeight );
    // fetch an interpolated value for each matrix row, and scale by bone weight
    vRow1 = fWeight * tBones.SampleLevel( sBones, float3( fU, fV0, nSlice ), 0 );
    vRow2 = fWeight * tBones.SampleLevel( sBones, float3( fU, fV1, nSlice ), 0 );
    vRow3 = fWeight * tBones.SampleLevel( sBones, float3( fU, fV1, nSlice ), 0 );
}

float3x4 GetSkinningMatrix( float4 vWeights, uint4 nIndices, float fTime, uint nSlice )
{
    // derive length of longest packed animation
    float fKeyCount = fTexWidth;
    float fMaxCycleLength = fKeyCount / SAMPLE_FREQUENCY;
    // compute normalized time value within this cycle
    // if out of range, this will automatically wrap
    float fCycleLength = fCycleLengths[ nSlice ];
    float fU = frac( fTime / fCycleLength );
    // convert normalized time for this cycle into a texture coordinate for sampling.
    // We need to scale by the ratio of this cycle's length to the longest,
    // because the texture size is defined by the length of the longest cycle
    fU *= (fCycleLength / fMaxCycleLength);
   
    float4 vSum1, vSum2, vSum3;
    float4 vRow1, vRow2, vRow3;
   
    // first bone
    SampleBone( nIndices[0], fU, nSlice, vSum1, vSum2, vSum3 );
    vSum1 *= vWeights[0];
    vSum2 *= vWeights[0];
    vSum3 *= vWeights[0];
    // second bone
    SampleBone( nIndices[1], fU, nSlice, vRow1, vRow2, vRow3 );
    vSum1 += vWeights[1] * vRow1;
    vSum2 += vWeights[1] * vRow2;
    vSum3 += vWeights[1] * vRow3;
   
    // third bone
    if( vWeights[2] != 0 )
    {
        SampleBone( nIndices[2], fU, nSlice, vRow1, vRow2, vRow3 );
        vSum1 += vWeights[2] * vRow1;
        vSum2 += vWeights[2] * vRow2;
        vSum3 += vWeights[2] * vRow3;
    }
    // fourth bone
    if( vWeights[3] != 0 )
    {
        SampleBone( nIndices[3], fU, nSlice, vRow1, vRow2, vRow3 );
        vSum1 += vWeights[3] * vRow1;
        vSum2 += vWeights[3] * vRow2;
        vSum3 += vWeights[3] * vRow3;
    }
   
    return float3x4( vSum1, vSum2, vSum3);
}

클로즈업 샷에 캐릭터 디테일을 추가하기 위해 캐릭터 모델에 테셀레이션을 사용했습니다.

왼쪽: 테셀레이션을 활성화하면 모델의 디테일이 더욱 풍부해집니다. 오른쪽: 테셀레이션이 없으면 윤곽선이 더 거칠어집니다.

아래 그림은 GPU 테셀레이션 파이프라인입니다.

압축된 애니메이션 정점의 비트 레이아웃은 위치의 각 구성 요소에 16비트, 접선에 대해 2개의 8비트 구형 좌표, 법선에 32비트, 각 UV 좌표에 16비트를 사용하여 아래에 표시됩니다. 탄젠트 좌표계는 직교하므로 2차 노멀(압축 해제된 노멀 및 접선에서 다시 계산할 수 있음)의 저장이 제거됩니다. 전체 32비트 필드를 사용할 수 있으므로 법선에는 DEC3N과 같은 압축이 사용되며 구형 좌표보다 ALU 작업이 덜 필요합니다. 추가 데이터 필드가 필요한 경우 DEC3N과 유사한 품질 수준의 법선에 대해 8비트 구면 좌표를 사용할 수 있습니다. ATI Radeon™ HD 4870 GPU에서 모든 대안을 시도한 결과 성능이나 품질 면에서 실질적인 차이가 거의 발견되지 않았습니다.

압축된 애니메이션 정점의 비트 레이아웃.

캐릭터는 동적이기 때문에 전처리 과정에서 캐릭터의 그림자를 SHLM(구면 조화 조명 맵)에 쓸 수 없습니다. 대신, 보다 전통적인 실시간 섀도우잉 방법인 병렬 침 섀도우 매핑을 사용하여 섀도우를 렌더링합니다. 이상적으로는 그림자 맵이 라이트 맵에 미치는 태양의 영향만 줄이고 캐릭터가 그림자를 드리우는 지형을 단순히 어둡게 하는 것을 원하지 않을 것입니다. 지형의 픽셀 셰이더에서 조명 환경에서 주요 방향 조명을 분리할 수 있습니다. (아래 사진)

지형에 캐릭터의 그림자가 있습니다. 왼쪽 절반은 산 그늘에 있고 오른쪽 절반은 직사광선을 받고 있습니다. 위: 캐릭터가 지형의 가려진 영역에 이중 그림자를 잘못 투사합니다. 하단: 이중 그림자 효과를 방지하기 위해 그림자 보정 계수가 사용됩니다.

동적 캐릭터(하단) 및 기타 정적 장면 소품(상단)은 대략적인 조명 환경을 생성하기 위해 지형에서 SHLM을 샘플링하여 음영 처리됩니다.

현재 및 미래의 하드웨어와 함께 웨이블릿을 사용하면 웨이블릿의 개념, 속성 및 용도가 소개됩니다. 웨이블릿은 상위 웨이블릿이라고 불리는 단일 파형의 크기가 조정되고 변환된 복사본으로 형성된 수학 함수입니다. 이를 통해 함수를 개별적으로 조작할 수 있는 다양한 주파수 구성 요소의 중첩으로 분해할 수 있습니다(다중 분해능 분석이라고 함). 함수는 웨이블릿 변환을 사용하여 웨이블릿 형태로 변환할 수 있으며, 역변환(푸리에 변환과 유사)을 통해 원래 함수로 다시 변환할 수 있습니다. 기본 함수로서의 웨이블릿은 표준 푸리에 표현(및 구면 고조파의 유사체)에 비해 몇 가지 중요한 이점을 가지고 있습니다. 불연속성 또는 급격한 변화, 비주기적 기능이 있는 기능을 더 잘 표현하고, 많은 경우 로컬 지원을 통해 데이터 세트의 효율적인 창 수정이 가능합니다. 이는 계층적으로 정제된 시스템이므로 데이터에서 대비가 낮은 로컬 영역을 드물게 나타낼 수 있으며 동시에 직교할 수 있습니다. 웨이블릿 변환은 연속적이거나 이산적일 수 있으며 모든 차원의 데이터를 나타낼 수 있습니다. 일반적으로 우리는 이산 2차원 웨이블릿, 특히 2차원 비표준 Haar에 관심이 있습니다.

비표준 2D Haar에 대한 수직, 수평 및 대각선 웨이블릿.

웨이블릿은 실시간 렌더링에 많은 잠재적인 응용 프로그램을 가지고 있습니다. 잠재적인 응용 프로그램 목록은 다음과 같습니다.

  • **실시간 셰이더 텍스처 압축 해제.**임의의 스칼라 데이터(예: 이미지 또는 구면 조화 계수)를 나타내는 텍스처는 웨이블릿 트리로 손실 압축된 후 선형 텍스처를 사용하여 콤팩트하게 표현될 수 있습니다. 픽셀 또는 버텍스 셰이더에 (u, v)가 주어지면 셰이더 내의 언랩 순회를 사용하여 해당 위치의 원래 텍스처 값을 실시간으로 복원할 수 있습니다. 또한 임의의 필터 작업(이중선형 필터 커널 포함)을 이미지와 필터 커널 사이에 계층화할 수 있습니다.

실시간 GPU 텍스처 압축 해제. 하위 블록 텍스처의 각 텍셀에는 웨이블릿 트리의 오프셋이 포함되어 있습니다.

  • **조명의 실시간 이중 및 삼중 제품 통합.**웨이블릿은 조명 적분 요소를 인코딩하고 압축하는 데 사용될 수 있으며, 직교 웨이블릿 세트(예: 비표준 2D Haar)를 선택하면 이중 적분을 내적 연산의 희소 목록으로 분해할 수 있습니다. 이는 삼중 계수가 간단한(그리고 작은) 규칙 세트에 의해 파생될 수 있는 방법을 설명함으로써 삼중 곱 적분으로 확장될 수 있습니다. 웨이블릿은 BRDF의 분석적 표현에 대한 근사치로 사용될 수도 있습니다.

(왼쪽) 빨간색 영역 조명과 (오른쪽) Grace Cathedral 조명 환경 간의 실시간 GPU 이중 통합.

3연속 프레임에서 그레이스 대성당과 동일하지만 대비가 과장되었습니다.

BRDF 통합 근사화를 위한 포인트 샘플링과 확산 조명을 위한 고주파 노멀 맵을 사용하는 3개의 연속 프레임의 3중 제품 통합입니다.

  • **정적 그림자 맵.**완전히 정적이거나 대부분 정적인 섀도우 맵은 압축률이 높은 웨이블릿으로 표현될 수 있으며 깊이 값은 위에서 설명한 텍스처 압축과 동일한 방식으로 쿼리됩니다. 섀도우 맵의 로컬 변경은 적용 범위가 변경 영역과 교차하는 웨이블릿만 고려하여 통합할 수 있으며 로컬 지원을 통해 기본 세트 설정의 가치를 보여줍니다.

  • **변위 맵 압축.**마찬가지로 변위 맵은 웨이블릿 압축을 겪을 수도 있습니다. 이는 변동이 적은 영역을 크게 압축하고 변동이 큰 영역을 임의의 정확도 수준으로 나타냅니다. 또한 웨이블릿 압축은 지형 표현과 같은 매우 큰 변위 맵을 압축하는 데 유용한 방법입니다. 이러한 맵은 일반적으로 그 사이에 부드러운 저주파 데이터가 흩어져 있는 작은 고주파 변화 영역이 있습니다. 적절한 압축 비율을 사용하면 단일 텍스처(정확한 변위 값 대신 웨이블릿 트리가 저장되는 위치)가 현재 그래픽 하드웨어에서 허용하는 최대 텍스처 해상도보다 훨씬 더 넓은 영역에 걸쳐 있을 수 있으며 잠재적으로 스트리밍 및 LOD 방법이 더 쉬워집니다.

  • 정적 및 동적 텍스처 패키징이 더 쉬워졌습니다. 임의 메시에서 UV 매핑 기능을 자동으로 생성하는 것은 까다로운 문제입니다. 특히 왜곡 최소화 및 아틀라스 경계의 텍셀 일치 시도와 같은 추가 매핑 기능이 필요한 경우 더욱 그렇습니다. 이러한 요구 사항 외에도 사용 가능한 메모리가 효율적으로 사용되도록 총 텍스처 사용량을 최대화하는 것이 바람직합니다. 좋은 UV 맵을 생성할 수 있는 프로그램이 있지만 생성된 텍스처가 좋지 않은 맵을 사용하는 경우가 여전히 종종 있습니다. 웨이블릿 이미지 압축은 다각형 사이의 사용되지 않는 간격을 대량으로 압축하여 거의 무손실 압축에도 좋은 결과를 생성합니다. 동적 패킹의 경우 기존 아틀라스로 효율적으로 타일링을 수행하는 것에 대해 걱정할 필요 없이 큰 청크에 새로운 텍스처 공간을 할당하고 채운 후 청크를 웨이블릿 압축합니다.

  • **기하학적 표현.**관련 UV 맵이 있는 변형 가능한 객체는 표면의 잔물결로 표현되는 변형을 가질 수 있습니다. 한 가지 가능한 접근 방식은 사용할 웨이블릿 수에 고정된 상한을 허용하는 것입니다(아마도 메모리 사용량을 제어하기 위해). 가장 오래된 기존 고주파 웨이블릿을 제거함으로써 새로운 변형을 위한 공간이 만들어집니다. 따라서 오래된 변형의 넓은 모양은 더 세밀한 세부 사항을 희생하면서 더 오래 유지됩니다. 또한 다중 해상도 표현을 사용하여 물체의 질량 중심과 관성 모멘트를 다양한 수준의 정확도로 직접 업데이트할 수 있습니다. 이는 단일 다중 해상도 형식으로 표현된 데이터가 정확도 요구 사항이 다른 다양한 시스템에서 사용하기가 더 쉽다는 것을 의미합니다.

Blizzard가 제공하는 StarCraft II: Effects & Techniques에서는 2008년 StarCraft II용 그래픽 엔진의 기능과 기술을 설명합니다. 스타크래프트 2 그래픽 엔진의 디자인 목표는 다음과 같습니다.

  • 확장성이 우선입니다. 다양한 시스템, 그래픽 API 및 하드웨어 장치에서 게임이 최대한 원활하게 실행되도록 하세요.

  • GPU 압력이 CPU보다 높도록 하십시오. 게임의 퀄리티를 높이는 데 있어 CPU보다는 GPU에 더 중점을 두기로 한 선택이었습니다. 주된 이유 중 하나는 스타크래프트 II에서는 잠재적으로 수백 개의 작은 기본 유닛을 생성하고 관리할 수 있다는 것입니다. 최대 8명의 플레이어가 동시에 플레이할 수 있으며, 이는 한 번에 최대 약 500명의 캐릭터를 화면에 표시할 수 있음을 의미합니다. 제작된 유닛의 수는 주로 플레이어가 제어할 수 있기 때문에(그리고 선택한 종족에 따라), 유닛 수가 많은 상황과 유닛 수가 적은 상황 모두에서 CPU 잠재력이 완전히 활용되도록 엔진 부하의 균형을 맞추는 것은 지루한 일이 됩니다.

플레이어는 매우 많은 수의 캐릭터를 제어할 수 있으므로 배치 수와 버텍스 처리량의 균형을 맞추는 것이 안정적인 성능의 핵심입니다.

  • 엔진의 이중 특성. 게임 모드와 스토리 모드의 두 가지 모드가 있습니다. 일반적인 게임 플레이(예: 게임 모드) 동안 장면은 상대적으로 멀리 떨어져 배치 크기가 더 크고 세부 사항보다는 동작에 중점을 두고 렌더링됩니다. 플레이어가 일반적으로 편안히 앉아 게임의 풍부한 스토리, 지식 및 시각적 요소를 즐기고, 대화를 통해 다른 캐릭터와 상호 작용하고, 전개되는 액션을 지켜보는 스토리 모드는 게임 모드와 완전히 다르며 종종 반대되는 제약을 갖습니다. 스토리 모드는 일반적으로 더 적은 배치 수, 클로즈업 및 더 명상적인 느낌을 자랑합니다.

위: 게임 모드; 하단: 스토리 모드.

StarCraft II의 경우 렌더링된 불투명 모델은 기본 렌더 패스에 바인딩된 여러 렌더 대상에 다음을 저장합니다.

  • 자체 조명, 환경 맵, 전방 조명 색상 구성 요소 등 로컬 조명의 영향을 받지 않는 색상 구성 요소

  • 깊이;

  • 픽셀당 노멀;

  • 앰비언트 오클루전(AO) 용어(정적 앰비언트 오클루전(AO)이 사용되는 경우). 스크린 스페이스 앰비언트 오클루전(SSAO)이 활성화된 경우 구운 앰비언트 오클루전(AO) 텍스처는 무시됩니다.

  • 조명되지 않은 확산 재질 색상;

  • 조명되지 않은 반사 물질 색상.

MRT는 다음과 같은 다양한 효과에 사용할 수 있는 픽셀당 값을 제공합니다.

  • 조명, 안개 볼륨, 동적 앰비언트 오클루전(AO) 및 지능형 변위, 뎁스 오브 필드(DoF), 그림자, 가장자리 감지 및 두께 측정에 대한 깊이 값.

  • 동적 앰비언트 오클루전(AO)에 대한 노멀입니다.

  • 조명을 위한 확산 및 스페큘러 반사.

스타크래프트 II는 다음 기능을 지원합니다:

  • 지연된 조명. 픽셀 위치 재구성, 템플릿, Early-Z 및 Early-Stencil 등

위: Early-ZS 다이어그램; 하단: 지연된 조명 효과.

  • 스크린 스페이스 앰비언트 오클루전(SSAO). SSAO의 주요 아이디어는 화면 공간에서 인접한 픽셀의 깊이를 샘플링하여 가시 표면에 있는 점의 폐색 기능을 근사화하는 것입니다. 결과 솔루션에는 현재 화면에 숨겨진 개체의 폐색 단서가 부족하지만 앰비언트 오클루전(AO)은 저주파 현상인 경향이 있으므로 근사치는 일반적으로 상당히 설득력이 있습니다.

스타크래프트 2의 SSAO 효과.

  • 뎁스 오브 필드(DoF). Circle of Confusion을 사용하여 카메라 초점 거리 내에서는 명확하고 초점 거리 외부에서는 시뮬레이션되는 뎁스 오브 필드(DoF) 효과를 시뮬레이션합니다.

DOF 처리 흐름 및 단계.

  • 반투명 그림자. 그림자 맵의 픽셀당 정보를 확장하기 위해 추가 정보 채널(두 번째 그림자 맵은 반투명 그림자 정보를 보유하고 추가 색상 버퍼는 반투명 그림자의 색상을 보유함)을 사용하여 반투명 그림자 지원으로 그림자 맵을 쉽게 향상시킵니다. 빛은 전면 투명도에 닿고 각 투명 레이어에 연속적으로 닿으면서 필터링됩니다.

광원 필터링 과정의 개략도.

HALO 3의 조명 및 재료는 Halo 3에서 사용되는 조명, 재료 모델, HDR 렌더링 및 기타 측면의 기술 내용을 자세히 공유합니다. 이 기사에서는 먼저 이전 조명 표현을 비교합니다.

DX SDK는 UV 패키징 방법을 개선하고 활용률을 높이는 데 사용됩니다.

라이트맵은 신호 처리와 텍스처 압축이라는 두 번 최적화되었습니다.

장면 렌더링 및 객체 렌더링 단계는 다음과 같습니다.

SH의 저장 및 계산도 최적화될 수 있습니다.

자료 측면에서 Halo 3는 당시 대부분의 게임과 달랐습니다. 이미 더 많은 PBR Cook-Torrance BRDF, 더 복잡한 영역 조명을 지원하고 더 높은 실시간 성능과 더 작은 저장 용량을 달성했습니다.

결과적으로 Halo 3에서는 디퓨즈에 SH 방사조도를 사용하고, 저주파 하이라이트에 새로운 지역 하이라이트 모델을 사용하고, 중간 주파수 하이라이트에 사전 필터링된 환경 맵을 사용하고, 고주파 하이라이트에 대해 점 광원을 직접 분석 및 평가하는 디퓨즈 + 다중 주파수(저주파, 중주파, 고주파) 하이라이트의 조명 모델을 구현합니다.

사전 통합된 SH 조명 정보의 도출 및 구현 과정은 다음과 같습니다.

아래 그림은 SH 조사의 디퓨즈 + 사전 필터링된 환경 이미지의 중간 주파수 하이라이트 + 점 광원 분석의 고주파 하이라이트를 렌더링한 것입니다.

아래 그림은 Halo 3의 HDR 렌더링 파이프라인입니다.

Halo 3의 렌더링 대상은 메모리 크기, 렌더링 속도, 하드웨어 믹싱 지원, 동적 범위, 순서(밴딩) 등과 같은 요소를 고려해야 합니다. 노출 범위에는 동적 범위와 밴딩을 사용할 수 있습니다. 다음 그림은 XBox 360의 렌더링 대상 세부 정보입니다.

게임 엔진과 GPU의 교차점: 현재와 미래에서는 Frostbite 엔진과 게임에서 DICE의 현재 및 미래 그래픽 사용 사례와 엔진의 현재 상태, 셰이더, 병렬성, 텍스처, 레이 트레이싱, 컴퓨팅 셰이더 등을 포함하여 그래픽 하드웨어에 미치는 영향을 공유하고 논의합니다. Frostbite는 아주 초기 버전에서 그래픽 노드 기반 셰이딩 편집기(아래 그림)를 사용하기 시작했습니다. 그 이점은 다음과 같습니다:

  • 풍부한 고급 컬러링 프레임워크. 모든 콘텐츠와 시스템에서 사용됩니다.

  • 아티스트 친화적. 생성, 조정 및 관리가 쉽습니다.

  • 유연한. 프로그래머와 아티스트는 기능을 확장하고 공개할 수 있습니다.

  • 데이터 중심이 되어야 합니다. 다양한 셰이딩 플랫폼으로 변환할 수 있는 캡슐화된 리소스입니다.

그래픽 노드 기반 셰이딩 편집기는 많은 셰이더 순열(셰이더 순열)을 생성합니다. 셰이더 순열은 HLSL 버텍스 및 픽셀 셰이더를 포함하여 사용되는 각 기능/데이터 조합입니다. 순열 폭발을 일으키는 기능(셰이더 맵, 조명, 기하학)이 많은 경우 순열의 성능과 동적 분기와 같은 기능은 많은 순열에 존재합니다.

병렬성 측면에서 Frostbite는 당시 증가하는 CPU 코어 수를 재사용하기 위해 이미 다중 스레드 명령 기록을 지원합니다.

Frostbite 엔진은 소프트웨어 오클루전 컬링과 하드웨어 오클루전 컬링을 모두 고려합니다. 소프트웨어 오클루전 컬링을 위한 솔루션은 SPU/CPU에서 거친 입자의 z 버퍼를 래스터라이제이션하는 것입니다. 래스터라이제이션할 때 낮은 다각형 폐색 그리드, 100m 보기 거리, 프레임당 최대 10,000개의 버텍스 및 수동 보존 방법을 사용합니다. z-버퍼는 PS3용으로 특별히 제작된 256x114 부동 소수점 형식이며 온라인 게임에 사용되었습니다. 그런 다음 화면 공간 bbox 테스트를 사용하여 다른 모든 시스템에 전달하기 전에 z 버퍼에 대해 모든 객체를 선별하여 많은 작업을 절약할 수 있습니다. GPU 래스터라이제이션 및 테스트가 필요하지만 오클루전 쿼리는 관리 가능하지만 이상적이지 않은 오버헤드와 대기 시간을 발생시키며, 조건부 렌더링은 CPU, 프레임 메모리 또는 그리기 호출이 아닌 GPU에만 도움이 됩니다. 또한 지연 시간이 짧은 추가 GPU 실행 컨텍스트, 래스터라이제이션 및 테스트가 CPU와 동기화되어 GPU에서 수행될 것으로 예상됩니다. 장면 그래프, 컬링, 시스템, 스케줄링, 최종 대상 등 전체 컬링 및 렌더링을 GPU로 이동합니다.

Frostbite는 또한 성능에 중점을 두고 래스터라이제이션된 메인 레이, 엔진에 쉽게 통합, 전체 파이프라인을 교체하지 않고 특정 효과와 개체를 전환하는 또 다른 방법, 효율적인 동적 기하학, 절차적 및 수동 애니메이션(잎, 캐릭터), 파괴(잎, 건물, 개체)를 통해 실시간 레이 트레이싱을 탐색합니다. 레이트레이싱 방출에는 유리, 메탈릭, 중요한 개체에 대한 올바른 반사, 단순화된 세계 기하학 및 테스트용 음영이 필요합니다.

컴퓨터 및 비디오 게임의 소프트웨어 계측에서는 게임과 엔진에 대한 소프트웨어 분석 도구의 역할을 설명하고, 언리얼 엔진을 기반으로 플레이어 경험을 모니터링, 제어 및 개선하는 데 사용할 수 있는 기본부터 고급까지 점진적으로 개선되는 여러 가지 솔루션을 제안하고 검증합니다.

센서를 사용하여 캐릭터 사망, 캐릭터의 무기 사용, 차량 범죄, 공격적인 언어 사용, 캐릭터 성별 및 인종 다양성, 기타 다양한 게임 통계 등 콘텐츠 분석에 유용한 다양한 데이터를 수집합니다. 데이터는 게임 전반에 걸쳐 보고되거나 게임이 끝날 때 요약으로만 보고될 수 있습니다. 수집된 데이터를 지속적인 콘텐츠 분석 요구 사항에 맞게 조정하도록 런타임 시 센서를 구성할 수 있습니다.

다음 그림은 분석기가 분석한 일부 데이터 및 정보를 보여줍니다.

John Carmack Archive - 인터뷰에서는 레이 트레이싱, GPU, 엔진 아키텍처, 크로스 플랫폼 및 기타 측면에 대한 세부 정보를 포함하여 id 회사, Domm/Quake 엔진, 렌더링 기술 및 업계 동향에 대한 Carmack의 토론을 자세히 기록합니다.

UnrealScript: A Domain-Specific Language는 UnrealScript의 기원, 목표, 특징, 사례 및 기타 기술적 세부 사항을 설명합니다. UnrealScript는 Unreal Engine 초기 버전의 스크립팅 언어로, 스타일이 Java와 유사합니다. 이를 통해 엔진을 사용하여 게임을 빠르게 개발할 수 있으며 쉽게 개발 및 수정이 가능합니다.

Unreal Tournament(Unreal Engine 1 – 1999년 출시)의 수정 버전인 Operation: Na Pali의 스크린샷.

UnrealScript는 게임 개념(액터, 이벤트, 지속 시간, 네트워크)을 직접 지원하고, 매우 추상적이며(비트와 픽셀이 아닌 개체 및 상호 작용), 프로그래밍이 간단하도록 설계되었습니다(OO, 오류 검사, GC, 샌드박싱).

UnrealScript는 Java와 유사하고 Java와 유사한 구문(클래스, 메서드, 상속), 게임별 기능(상태, 네트워킹)을 가지며 프레임워크에서 실행되고 게임 엔진은 이벤트를 객체에 보내고 객체는 서비스를 위해 게임 엔진(라이브러리)을 호출합니다.

cpp
// UnrealScript코드
function TranslatorHistoryList Add(string newmessage)
{ 
   prev=Spawn (class,owner);
   prev.next=self;
   prev.message=newmessage;
   return prev;
}

Unrealscript는 JIT 없이 런타임에 실행되는 바이트코드로 컴파일됩니다!

UnrealScript의 Actor 상태는 언어의 일부이며, 네트워크, 변수 수정, 오류 감지 등을 지원하며, C++보다 20배 느린 VM 바이트코드(예: Java)로 컴파일됩니다. 하지만 100개가 넘는 객체가 있어도 CPU는 UnrealScript를 실행하는 데 5%의 시간만 소비합니다. 그래픽과 물리 엔진이 대부분의 작업을 수행하므로 UnrealScript가 빠를 필요는 없습니다.

UnrealScript와 C++ 간의 작업 분할은 아래 그림에서 명확하게 볼 수 있으며, 여기에는 주로 UI, 게임 로직 이벤트 처리, 액션 및 공격, 상태 머신, 무기 로직, 충돌 콜백 등이 포함됩니다.

세대별 가비지 수집기는 GC에서 사용되며, 이는 세계의 액터에 대한 destroy() 함수를 갖는 복잡성을 증가시킵니다. 가비지 수집기는 파괴된 액터에 대한 포인터를 NULL로 설정하는 일도 담당합니다.

언어의 유연성은 아래 그림에 나와 있습니다. 유연성은 위에서 아래로 증가하지만 유지 관리 작업도 증가합니다.

UE4가 출시되면서 UnrealScript는 UE의 급속한 반복 개발이라는 역사적 과정에서 블루프린트로 대체되고 사라졌습니다. 그러나 사용된 디자인 컨셉과 기술은 여전히 ​​우리의 연구와 탐구의 가치가 있습니다.

새로운 게임 기술: Gamebryo Element Engine은 크로스 플랫폼, 스트림 처리 및 기타 기술을 포함하며 Gamebryo Element Engine에서 실행되고 검증되었습니다. 그 중 스트림 처리에서는 버텍스 애니메이션 + 골격 애니메이션의 적용 사례를 언급했습니다.

흐름에 의해 정의된 작업 종속성은 작업을 실행 단계로 분류합니다. 다른 작업의 결과를 사용하는 작업은 이후 단계에서 실행됩니다. N+1 단계 작업은 N 단계 작업의 출력에 따라 달라집니다. 특정 단계의 작업은 동시에 실행될 수 있습니다. 한 단계가 완료되면 다음 단계를 실행할 수 있습니다.

멀티스레딩 후 CPU 사용량 비교. 후자의 CPU 사용량이 더 균등하다는 점에 유의하세요.

ASSASSIN'S CREED에서 믿을 수 있는 군중 만들기에서는 Assassin's Creed의 군중 시뮬레이션, 렌더링, 최적화 및 기타 기술을 설명합니다. Assassin's Creed는 계층화된 애니메이션 메커니즘을 사용합니다.

또한 복잡한 모션 시스템을 갖추고 있습니다. 보다 사실적인 애니메이션은 컨트롤이 반응하지 않는다는 것을 의미합니다. 매우 복잡하다는 것은 다른 시스템에 너무 많은 영향을 미친다는 것을 의미하므로 최대한의 유동성을 유지하기 위해 단순화했습니다. 다음 그림은 단순화된 모바일 시스템 범례입니다.

시뮬레이션 실행 측면에서는 동시성을 사용하여 많은 스레드를 활용하며 PC와 360은 물론 PS3에서도 최대한 많은 SPU에서 잘 실행됩니다.

2000년대 중후반에는 게임이 멀티 플랫폼 배포의 주류가 되면서 엔진에도 강력한 크로스 플랫폼 지원이 필요했습니다. 스튜디오를 중단하지 않고 PC에서 크로스 플랫폼 개발로 전환하는 방법 소스 엔진이 게임을 멀티 플랫폼으로 만들고 생성 프로세스 속도를 높이며 다양한 문제를 해결하는 방법을 설명합니다. 기사에서는 크로스 플랫폼 개발이 개발자 효율성, 인력 할당, 반복, 인증, 사용자 경험, 프로그래밍 등에 문제가 있다고 언급했습니다. 이러한 문제를 해결하기 위해 기사에서는 자산 처리에 대한 하이브리드 접근 방식을 제안합니다. 즉, 자산 트리를 밤마다 패키지로 컴파일하고 아티스트가 개별 자산을 로컬에서 재정의하여 두 세계의 장점을 최대한 활용하는 방식입니다. 소스 엔진 파이프라인의 도구는 각 자산의 서로 다른 플랫폼 버전을 만드는 대신 플랫폼 차이를 자동으로 처리하므로 리소스의 크로스 플랫폼화를 가속화합니다. 메모리 용량보다 자산이 빠르게 증가하는 문제를 해결하기 위해 소스 엔진은 자산에 대한 참조 추적, 압축, 클리핑, 유지 관리 등의 작업을 수행하여 자산 점유를 크게 줄입니다. 자산을 신중하게 배치하면 더 나은 결과를 얻을 수 있지만 그 차이는 분명합니다(아래).

위: 압축되지 않고 잘린 자산의 렌더링; 하단: 부적절한 자산 처리로 인해 상당한 흐림이 발생했습니다.

자산 압축은 텍스처에 가장 극적인 영향을 미치며, 문제의 80%는 텍스처의 20%로 인해 발생합니다. 소스 엔진의 도구는 이 20% 텍스처를 쉽게 볼 수 있습니다.

긴 자산 로드 시간 문제를 해결하기 위해 소스 엔진은 다양한 로드 작업에 대해 다양한 최적화를 수행합니다.

질문 솔루션

찾기 신중하게 배치된 연속 파일

정렬되지 않은 읽기 섹터 정렬

버퍼링된 액세스 버퍼링되지 않은 DMA I/O

동기화 지연 비동기 로딩

요청 시 로드되는 작은 파일 큰 단일 파일

당시 모든 콘솔 게임 패키지는 DVD에 저장되었기 때문에 소스 엔진은 DVD 데이터 로딩 속도를 높이기 위해 Zip 파일 형식, CPU 또는 IO 대역폭의 균형을 맞추는 압축, 전용 스레드 비동기 로딩과 같은 조치를 채택했습니다. 다음 그림은 소스 엔진의 자산 로딩 아키텍처 다이어그램입니다.

또 다른 기사에서는 다양한 자산 구조 다이어그램도 제공합니다.

다음 두 그림은 소스 엔진의 동기 로딩과 비동기 로딩의 비교 다이어그램입니다.

주요 로딩 기술은 다음과 같습니다:

  • I/O 스레드는 버퍼링되지 않은 DMA 전송을 수행합니다.

  • 디스크를 지속적으로 회전시킵니다.

  • 잠금이 없는 구현.

  • I/O 대역폭을 위해 CPU/SPU 교환

  • 부하를 동기화하기 위해 더미 값을 반환합니다.

대용량 파일의 경우 스트리밍 로딩을 사용하세요.

  • 항상 모든 애니메이션과 오디오의 처음 1/2초를 저장합니다.

  • 나머지는 백그라운드에서 비동기적으로 로드합니다.

  • 데이터를 사용할 수 있음, 데이터를 얻는 중, 데이터를 얻을 수 없음 등 여러 상태를 알릴 수 있는 리소스 추상화 계층이 필요합니다.

작은 파일의 경우 모든 작은 임시(Ad Hoc) 파일을 하나의 큰 blob으로 미리 컴파일하고 단일 작업으로 읽어 게임 코드를 변경할 필요 없이 가짜 파일 시스템을 만듭니다. (아래 사진)

각 레벨의 리소스 참조를 미리 전처리합니다. 팩을 생성하려면 각 자산을 포함하여 팩에 무엇이 있는지 알아야 하고, 로드 종속성을 분석하고, 패키지 외부에서 리소스를 로드할 때 충돌을 트리거해야 합니다.

멀티 스레드 처리 측면에서 소스 엔진은 이미 작업 대기열 시스템을 지원합니다. 메인 스레드는 작업을 생성하고 이를 작업 큐에 삽입한 다음 계산 스레드가 작업 큐로 이동하여 작업 실행을 얻고 결과를 생성할 수 있습니다.

소스 엔진 작업 대기열 아키텍처 다이어그램. 작업은 코드 및 로컬 데이터 패키지입니다. 작업이 작업 대기열에 추가되면 다른 스레드가 작업 대기열에서 작업을 가져와서 사용합니다.

그래픽은 플랫폼 전반에 걸쳐 종종 문제가 발생하는 모듈이기도 합니다. 예를 들어, TV와 컴퓨터 모니터의 픽셀과 색상 공간이 다르기 때문에 색상 차이가 발생합니다.

셰이더는 플랫폼 차이를 일으키는 주요 요인 중 하나이기도 합니다. 소스 엔진은 PC와 콘솔 모두에서 HLSL을 사용하지만 셰이더 컴파일러는 약간 다를 수 있고 가장 복잡한 셰이더에는 몇 가지 문제가 있으며 GPU/CPU 전력 균형도 약간 다릅니다. Source 팀은 이러한 플랫폼 차이를 줄이고 피하기 위해 회귀 테스트를 위해 매일 밤 모든 것을 오프라인으로 컴파일합니다.

당시 360과 PS3 모두 주문한 PowerPC CPU를 사용했다는 점을 고려하면 복잡한 코드를 실행할 때 효율성이 x86보다 훨씬 느렸다. 크로스 컴파일된 코드를 직접 사용하면 속도가 25%-50% 향상됩니다. 신중한 최적화는 x86의 속도에 접근할 수 있습니다. SIMD를 사용하면 PPC의 x86보다 좋습니다.

소스는 렌더링 파이프라인의 특징과 문제점에도 주목합니다. 예를 들어 PPC는 대기 시간이 길고 처리량이 높으며 레지스터 종속성, 로드 적중 저장, 캐시 누락, 마이크로코드, ERAT, TLB 등 모든 잠재적 위험을 이해합니다. 분석기의 내용에 주의하세요. 성능의 80%는 코드의 20%를 만지는 데서 나옵니다. 코딩할 때 SIMD 기술을 사용해 보세요. 모든 플랫폼에서 작동하는 추상 인터페이스를 사용하고, 기본 벡터 클래스를 푸시하고, double을 float로 대체하세요. 벡터 덧셈을 예로 들면 코드는 다음과 같습니다.

python
FORCEINLINE Vector Add ( const Vector & a, const Vector & b )
{
    #ifdef _X360
        return __vaddfp( a, b );
    #elif defined(_SSE)
        return _mm_add_ps( a, b );
    #else
        return Vector( a.x + b.x, a.y + b.y, a.z + b.z, a.w + b.w );
    #endif
}

Modern Graphics Engine Design은 장면 관리에 KD 트리, 쿼드 트리 등과 같은 많은 가속 구조가 포함되어 보이지 않는 개체를 신속하게 쿼리 및 제거하고 드로우 콜을 줄일 수 있다고 언급했습니다. 그러나 당시 가장 일반적인 기술은 일관성을 가장 용이하게 하는 속성별로 개체를 정렬하는 것이었습니다. 또한 정점별 데이터를 사용하여 셰이딩 매개 변수를 인코딩하면 버텍스 셰이더 상수를 설정할 필요성이 줄어들고 버텍스 셰이더를 전환해야 하는 필요성(예: 인덱스 팔레트 스키닝)이 줄어들며 각 버텍스 인덱스를 다른 항목(예: 조명, 폐색 등)에 적용할 수도 있습니다.

텍스처 인코딩된 셰이딩 매개변수를 사용하면 픽셀 셰이더 상수를 설정해야 할 필요성이 줄어들고, SetPixelShaderConstant()를 통해 광택을 설정하는 대신 노멀 맵의 알파에 광택을 넣는 등 픽셀 셰이더를 전환해야 할 필요성도 줄어듭니다. 4개의 빛 차단 항목을 라이트맵으로 인코딩하고 4개의 그림자 조명을 모두 한 번에 그립니다.

조명 계산에는 세 가지 기술이 있습니다. 완전 정적(각 버텍스 또는 조명 맵을 미리 계산), 부분 동적(조명은 색상과 강도를 변경할 수 있지만 이동할 수 없으며, 조명별 폐색을 정점이나 텍스처에 구축), 완전 동적(그림자에 대해 많은 CPU 레이캐스팅을 수행하고, 그림자 맵 또는 그림자 볼륨과 같은 GPU 지원 그림자 사용). 조명 관련 특성 및 소모량은 다음과 같습니다.

기술 CPU 소비 VS 소비 PS 소비 비고

정적 라이트맵 텍스처 페이지를 사용하는 경우 낮음 낮음 낮음 빛과 그림자의 개수는 중요하지 않습니다.

동적 라이트맵 높음(적어도 광원이 변경될 때) 낮음 낮음 광원이 많을수록 업데이트 비용도 커집니다.

동적 라이트맵(그림자 포함) 제한된 수준 낮음 낮음 CPU에 대한 레이캐스트가 너무 많습니다.

폐색 매핑 텍스처 페이지를 사용하는 경우 낮음 낮음 안으로 조명 수를 약 4개로 제한

정점별 폐색 낮음 안으로 낮음 광원은 색상만 변경할 수 있습니다.

템플릿 섀도우(CPU) 높음(일괄 계산 및 윤곽 형성에만 해당) 낮음 높은 표면당 조명 3개로 제한됨

템플릿 섀도우(GPU) 배치 크기의 경우 중간 매우 높다 높은 표면당 조명 3개로 제한됨

깊이 그림자 맵 낮음 안으로 안으로 재깅 결함

SH 기반 PRT 낮음 안으로 낮음 무한한 광원만 있고 애니메이션은 없습니다.

셰이더 관리 측면에서 이 기사에서는 게임 유형에 따라 셰이더를 처리하는 두 가지 주요 방법이 있다고 제안합니다. Open의 경우 레벨 편집기의 아티스트 중심, 매우 유연하고 HLSL/.FX 파일을 사용하여 복잡성을 관리하고 많은 셰이더 유형을 약간 복잡하게 지원하며 주석을 사용하여 셰이더 매개변수를 식별하지만 주의하지 않으면 셰이더 폭발을 일으킬 수 있으며 셰이더를 너무 자주 전환하면 드로우 콜을 줄이는 데 도움이 되지 않습니다. 다른 하나는 통합 셰이더 모델입니다. 엔진 기능이나 게임 요구 사항에 따라 구동되고, 더 적고, 더 구체적이고, 최적화된 셰이더, 셰이더를 설정하기 위한 실용적인 C++ 코딩, .fx 파일을 사용할 수 있지만 그다지 많지는 않습니다. 셰이더는 더 제한된 선택 항목에서 제공되고, 셰이더 변경으로 인한 최대 그리기 호출 수를 제한하여 더 높은 프레임 속도를 선호하며, 속도 이점을 얻으려면 셰이더 매개변수를 지오메트리와 텍스처에 내장해야 합니다.

테스트 엔진에서 세계는 16x16x16미터 크기의 3D 그리드(셀)로 나뉩니다. 장면의 모서리가 그리드에 잘립니다. 각 셀에는 정점과 인덱스 버퍼가 있습니다. 충돌 삼각형의 AABBTree는 렌더링 삼각형의 테셀레이션과 일치합니다. 이는 재료 레코드(삼각형의 인덱스 버퍼 범위 및 재료가 있는 삼각형에 대한 AAB 포함)의 벡터이자 이동 엔터티 목록(AABox 및 렌더링에만 사용되는 메시 데이터에 대한 참조 포함)이기도 합니다.

세계를 다수의 그리드로 분할하면 다음과 같은 이점이 있습니다.

  • 효율적인 컬링.

  • 65K 꼭지점 또는 삼각형 제한을 초과하지 않고 동일한 VB 및 IB를 공유할 수 있습니다. IB에 16비트 인덱스를 사용할 수 있고, AABB 트리에 16비트 인덱스를 사용할 수 있으며, 트리의 AABB 상자를 16바이트 또는 8바이트로 압축할 수 있습니다.

  • 축당 여전히 좋은 정확도를 유지합니다.

  • 다른 셀에서 이동하는 엔터티를 더 빠르게 거부할 수 있습니다.

  • 조명을 타일당 7개의 광원으로만 제한할 수 있습니다.

이 3D 메싱 방법을 사용하면 다음과 같은 특성을 얻을 수 있습니다.

  • 한 번의 그리기 호출로 전체 월드 셀을 그립니다.

최대 7개의 광원.

  • 확산 및 스페큘러 반사 범프 맵.

  • 부드러운 그림자.

  • 광택 매핑, 색상 변화 스페큘러 반사.

  • Masked의 이미시브.

  • Dest Alpha에 물이나 안개 깊이가 저장됩니다.

  • 안개, 안개, 물은 부분 알파 채널입니다. 대상 알파를 기반으로 혼합된 안개 레이어 색상입니다.

또한 테스트 엔진에서는 광원 수가 많을 때 일반 범프 매핑의 성능이 저하되는 문제를 해결하기 위해 Averaged L Bump Mapping 기술도 언급했습니다.

"조금 더 지연됨" - CryEngine 3에서는 향상된 스트리밍 로딩, 멀티스레딩, 향상된 조명, 성능 감지 도구, 추적 셰이더 컴파일 문제 등과 같이 2009년 CryEngine 3에 도입된 새로운 기술을 언급합니다. Uber Sahder(유니버설 셰이더)의 도입으로 인해 가능한 모든 순열을 컴파일하면 메모리, 생산 및 성능과 같은 많은 문제가 발생합니다. CryEngine3의 솔루션에는 동적 분기/다중 채널로 분리/조합 감소, 더 적은 기능과 더 낮은 성능 수용, 비동기 셰이더 컴파일, 분산 운영 체제 컴파일 셰이더 캐시 등이 포함됩니다.

디퍼드 렌더링(Deferred Shading) 측면에서 CryEngine 3는 개선되었습니다. 더 이상 CryEngine 2의 디퍼드 렌더링(Deferred Shading)을 사용하지 않지만 Light Pre-Pass 렌더링을 사용하여 패스를 세 개로 나눕니다.

  1. 포워드 렌더링은 GBuffer 데이터(즉, 지오메트리 채널)를 생성합니다.

  2. 조명(Phong)을 지연하고 텍스처에 축적합니다.

  3. 빛 축적 텍스처 포워드 셰이딩을 사용합니다. (디퍼드 셰이딩 아님)

이 접근 방식의 장점은 대역폭과 메모리 사용량이 적고 음영 처리가 더 유연하다는 것입니다. 빛 축적 텍스처의 경우 6채널과 4채널 레이아웃이 있으며 후자는 효과가 약간 다르지만 더 빠릅니다.

CryEngine 3는 또한 렌더링 속도를 높이기 위해 IBL에 빛 축적 텍스처를 사용합니다. (아래 사진)

왼쪽: 확산 RGB + 하이라이트 RGB의 고품질 이미지; 오른쪽: 확산 RGB + 하이라이트 강도의 빠른 렌더링 이미지. 그 차이는 일반적으로 무시할 수 있습니다(환경에 따라 다름).

GBuffer의 노멀을 저장하는 전통적인 방법과 특징은 다음과 같습니다.

  • XYZ 세계 공간. 8비트를 사용하는 경우 극심한 반사/스페큘러 반사에 문제가 있습니다. 10비트를 사용하면 효과는 좋지만 반사광 강도와 PS3에서는 이를 만족시키지 못합니다.

  • 양자화 결함을 해결하는 방법에는 세부 노멀 맵, 노이즈 및 지터가 포함됩니다.

  • XY 뷰 공간(Z 재구성) 8/10/16비트, 반전된 Z 비트(원근 및 노멀 매핑). 하지만 z = z_sign sqrt(1-xx-y*y)의 정확도는 z가 0에 가까울수록 매우 나빠집니다. (아래 그림)

XY 뷰 공간의 일반적인 저장 정확도 문제. 그림에서 빨간색은 z가 0에 가까울 때 문제가 있음을 나타냅니다.

위의 일반적인 저장 정확도 문제에 대응하여 CryEngine 3는 법선의 z를 조정하고 xy 구성 요소를 스케일링하여 수정했습니다.

이것의 이점은 중요한 부분(밝은 부분)의 정확도가 높아지고, 프레임 버퍼 친화적인 블렌딩이 가능하며, z 재구성 문제가 없다는 것입니다. 단점은 월드 공간보다 더 많은 ALU를 차지하는 뷰 공간의 낭비된 영역과 법선입니다.

CryEngine 3는 SSAO의 효과를 개선하고 법선을 사용하여 보다 정확한 AO를 계산합니다.

왼쪽: 개선 전 SSAO; 오른쪽: 법선을 사용하여 개선한 후의 SSAO.

조명 측면에서는 광원 래스터라이제이션를 위해 2D(직사각형) 및 3D(볼록체)가 지원됩니다. 여기서 3D 방법은 Z 버퍼와 더 조밀한 경계 상자(더 적은 처리 픽셀)를 활용할 수 있습니다. 기존 광원 유형 외에도 크로스해치 맵 조회도 지원됩니다. 추가 메모리가 필요하지 않고 대역폭이 덜 필요하며 그림자 폐색 채널 수에 제한이 없다는 장점이 있습니다.

IBL 조명의 경우 장거리 조명에 라이트 프로브를 사용하고 큐브 맵과 결합하여 실시간 및 효율적인 HDR 조명을 얻고 디퓨즈 큐브 맵은 반사 큐브 맵에서 얻습니다. 서로 다른 Mip은 서로 다른 강도의 스페큘러 반사 효과를 나타냅니다. 주변 조명 조건 하의 그림자는 일반 의존성 및 반사 조명을 추가하여 개선되었습니다. 라이트 프로브는 지정된 수평 위치에서 생성될 수 있습니다. 지연 조명을 사용하면 SSAO와 결합된 로컬 조명 프로브를 혼합하여 더 나은 결과를 얻을 수 있습니다.

CryEngine 3의 다양한 조명 효과. 왼쪽 위에서 오른쪽 아래로 밝은 환경 + SSAO, 어두운 환경 + 그림자 투사 광원 + SSAO, 회색 환경(SH) + 그림자 투사 광원 + SSAO, IBL 주변광(반사광 + 디퓨즈) + 그림자 투사 광원 + SSAO입니다.

또한 CryEngine 3는 Xbox360, PS3 및 PC에서 빠르게 실현할 수 있는 실시간 동적 전역 조명 효과도 시도했습니다. 사전 계산 없이 완전히 동적(형상, 재료 및 조명)이며 정적 개체와 동적 개체의 통합을 달성합니다.

위: GI 없음; 하단: 동적 GI가 활성화되었습니다.

Unreal Engine으로 60Hz 달성: Inside the Tech of Mortal Kombat vs DC Universe에서는 Unreal Engine 3 및 해당 솔루션에서 발생하는 성능 문제를 언급합니다. 성능 오버헤드에는 주로 CPU와 GPU가 포함되며, 이는 다음과 같이 분류됩니다.

  • GPU 오버헤드

GPU 고정 오버헤드

포스트 프로세스

일반적으로 고정 비용이 가장 큽니다.

  • 작업을 숨기기 위해 가능한 한 많은 작업을 그룹화합니다(예: Bloom+DOF+Gamma+Resolution Redirect)

  • 가능한 많은 모서리를 잘라내고 필요에 따라 특수한 경우를 제거합니다. 예를 들어 상황에 따라 3가지 다른 DOF 방법 중 하나를 사용합니다. 일반 플레이를 위한 클래식 블러 크로스페이드, 메인 메뉴/영화를 위한 확장된 포아송 디스크, 일련의 블러 평면을 갖춘 Klose-Kombaty.

  • 일반 렌더링 오버헤드

8bpc 렌더 타겟, 선형 레벨 0..2.

  • 비용 절감을 위해 조명 내용에 따라 γ=1.0과 γ=2.2의 조합으로 조명을 실시합니다.

  • 불투명: MSAA를 사용합니다.

  • 반투명: MSAA 이후에 구문 분석됩니다.

  • 게임의 3D 해상도는 1040x624이며, HUD가 1280x720에서 렌더링할 수 있도록 확장됩니다.

  • 다중 채널 오버헤드

채널의 조명당 오버헤드가 너무 높습니다.

  • 대부분 pre-lit이므로 포워드 렌더링을 선택합니다.

  • Z-Prepass 일반적인 깊이 복잡성 < 1.5.

  • Z-프리패스를 제거하면 "디테일 링"을 통해 불투명한 물체를 앞에서 뒤로 느슨하게 분류하여 약 0.75밀리초가 절약됩니다.

  • 가능하다면 각 픽셀을 한 번만 처리하십시오.

  • 머리 위 조명

월드 라이팅(정적). 저는 미리 계산된 조명을 위해 Illuminate Labs의 Beast를 사용했고 Turtle을 사용하여 재료나 MITV를 통해 애니메이션화되는 동적 RNM을 구축했습니다. 미리 계산된 조명은 텍스처와 버텍스 RNM 조명의 혼합으로, 멀리 있는 객체의 정점별 확산 RNM 평가를 지원하기 위해 빠른 경로가 추가되었습니다.

  • 월드 라이팅(동적). 효율적인 포인트 라이트는 픽셀당 조명(지면)과 정점당 조명(나머지 환경)을 혼합하여 달성됩니다. 최대 부하를 고려하여 셰이더는 활성화되어 재료에 주입된 3개의 확산 전용 포인트 라이트를 사용합니다. 분기가 없으며 세 개의 조명이 모두 항상 평가되며 이는 3-deep FIFO에서 전역적으로 할당 및 관리됩니다.

  • 캐릭터 조명.

맞춤형 조명 모델: SH 계수 세트의 방사조도. 그라데이션은 각 객체에 대한 SH 세트를 결정하기 위해 평가되고, 모델은 처음 4개의 계수(환경 및 방향 항)만 사용하여 확산되고, 3개의 효율적인 포인트 라이트는 정점당 평가되고 최종 확산 조명 결과로 결합되며, (E·N)의 거듭제곱으로 크기가 조정되고 확산 조명을 곱하여 사양을 가짜로 만듭니다. 아래 그림은 캐릭터의 하이라이트 효과입니다.

  • 확산 조명과 SH 환경 항 사이의 Lerp 인자로 (E·N)을 사용하여 피부 투과를 위조합니다.

  • 엣지 조명: 전력 스케일링(1-E·N)을 감쇠한 다음 하드 임계값(1-E·N)을 통해 다중화합니다. 임계값이 충분히 높게(~0.7) 올라가면 크롬 맵처럼 보입니다. 캐릭터 메시는 일괄적으로 렌더링됩니다. 다음 그림은 피부와 금속의 효과를 보여줍니다.

  • 입자 오버헤드.

  • CPU 오버헤드

입자 오버헤드

  • 옷감과 수체

  • 렌더링 스레드 가상 오버헤드, 상태 캐시

유니버설 렌더링 스레드 최적화: 불필요한 작업을 줄이기 위한 많은 작업; 렌더링 스레드 가상화는 많은 유휴 상태를 의미합니다. 중복된 가상 호출을 줄이기 위해 가능한 한 많은 상태를 캐시합니다. 예를 들어 FMaterialRenderProxy의 GetMaterial 가상 호출을 캐시 호출로 대체합니다. 셰이더 처리 내부에서 GetXXX()(예: GetPixelShader) 상태에 대한 불필요한 반복 호출을 많이 제거합니다.

  • 쓰레기 수거

게임에서 모든 실시간 GC 호출을 제거했습니다. 모드를 종료할 때만 호출되었습니다.

  • 메모리 관리가 UObject/AActor의 지연된(프레임별) 정리로 전환되었습니다.

  • Rootset을 통해 캡처된 모든 로딩 데이터.

  • 참조 카운트 UObject인 UResource 클래스를 소개합니다.

  • 모든 USurface 파생 클래스(예: UMaterial, UTexture 등)는 불필요한 삭제를 방지하기 위해 UResource를 통해 참조 계산됩니다.

  • 기타 제안

성능을 위한 예산을 미리 확보하세요!

  • Edge 및 360용 통합 셰이더를 고려할 때 형상 문제는 채우기 속도보다 작습니다.

  • 유효한 PostFx는 대부분의 순열에 대해 미리 결정되고 고정되어 있습니다.

  • 동적 중요 섹션 메모리 할당을 최대한 줄이면 모든 성능이 크게 차단됩니다.

  • 가능하면 풀 할당자를 사용하고 재할당에는 주의하세요.

  • 디자이너와 아티스트가 성능 지표를 사용하여 실행하도록 강요합니다!

이러한 비용에 대응하여 위의 제안 외에도 이 기사에서는 다른 많은 건설적인 최적화 제안 및 개선 기술도 제시합니다. 수년이 지난 후에도 여전히 특정 참조 가치가 있으며 읽을 가치가 있습니다.

크로스 플랫폼 자산과 관련하여 여러 크로스 플랫폼 타이틀의 코드 및 자산 관리를 위한 실무 기술과 같은 기술과 경험을 공유하는 논문도 있습니다. 이 백서는 데이터를 게임으로 가져오기, 버전 제어/워크플로, 데이터를 최적화된 대상 형식으로 변환, 특정 플랫폼에 맞게 최적화, 유효성 검사/오류 검사, 자산의 오류/문제 확인(본 수, 텍스처 종횡비, 다각형 수, 파일 크기...) 및 동일한 프로젝트의 여러 버전 관리를 목표로 하는 자산 파이프라인으로 시작합니다. 자산 파이프라인은 다음과 같습니다.

  • 각 프로젝트에는 작업 세트가 있습니다(각 소스 자산당 하나씩).

  • 모든 작업 설정과 소스는 하나의 중앙 위치에 저장됩니다(자산에 대해 Perforce 사용).

  • 맞춤형 사용자 인터페이스.

  • 워크플로 도구와 연결됩니다(다른 권한/역할).

  • 아티스트/자산 제작자는 로컬에서 작업하고 완료되면 변경 사항을 커밋합니다.

  • 이러한 변환기를 참조하는 변환기 실행 파일과 작업 유형이 있습니다.

예: 텍스처에 대한 다양한 작업 유형(UI, 환경, 캐릭터...)

  • JobType은 프로젝트별로 다릅니다.

  • 작업 유형을 사용하여 제한/제약을 정의할 수 있습니다.

작업 유형은 강제/사전 설정 변환기의 일부 매개변수를 공통 설정으로 정의합니다.

  • 각 프로젝트의 디렉터리 구조에는 변환기 실행 파일의 자체 복사본이 있습니다.

맞춤형 프로젝트별 변환기.

  • 프로젝트에 사용되는 변환기의 버전을 제어합니다. 이는 최종적으로 프로젝트를 보관하는 데 필요합니다.

  • 대부분의 프로젝트에는 해당 프로젝트에 특정한 특수 변환기가 있습니다.

지도/레벨 데이터 변환기.

  • 데이터베이스에는 보류 중인(구축 예정) 작업 목록이 포함되어 있습니다.

  • 각 클라이언트는 보류 중인 작업을 가져와서 로컬로 빌드하고 결과를 중앙 서버로 푸시합니다.

변환기 실행 파일 + 작업 설정 + 소스 자산은 컨텍스트에 관계없이 항상 모든 컴퓨터에서 동일한 결과를 생성해야 합니다.

  • 각 클라이언트는 서버에서 최신 버전을 복사합니다.

  • 변환된 자산은 게임 빌드 폴더(모든 파일이 포함된 플랫 폴더)에 배치됩니다.

프로젝트의 각 버전마다 게임 빌드 폴더가 있습니다.

디스크 버전의 경우 이러한 파일이 재정렬되어 패키지됩니다.

  • 대부분의 플랫폼은 이 디렉터리에서 직접 로드됩니다.

다른 것들은 먼저 디스크 에뮬레이션/ROM 구축이 필요합니다.

  • 빠른 반복 시간을 지원하기 위해 특정 자산을 빠르게 다시 로드합니다.

런타임 통계를 사용한 차세대 자산 스트리밍에서는 차세대 엔진 기능을 위한 자산 스트리밍 로딩 기술을 살펴봅니다. 핵심 아이디어는 런타임에 수집된 통계 정보를 사용하여 빌드 파이프라인에서 리소스 종속성 그래프를 생성한 다음, 리소스 종속성 그래프를 사용하여 로드해야 할 것과 로드하지 말아야 할 것을 찾는 것입니다.

장면 객체 및 리소스의 종속성 그래프. 점선은 장면 객체가 런타임에 추가된 종속 리소스를 참조한다는 것을 나타냅니다.

리소스 종속성 그래프 메커니즘 아키텍처 다이어그램을 구성합니다. 정보의 런타임 비동기 수집을 포함하고 이를 통계 서버로 보냅니다. 올바른 통계 서버는 통계 빌더가 구축한 리소스 종속성 정보를 시작합니다.

이 기사에서는 빌드 대기 시간 및 메모리 관리와 같은 문제도 논의합니다.

차세대 주류 프로그래밍 언어: 게임 개발자의 관점은 팀 스위니(에픽게임즈 CEO)가 2009년에 발표한 연설입니다. 게임 개발자의 관점에서 차세대 주류 프로그래밍 언어를 알려줍니다. 이 기사에서는 게임 개발의 일반적인 프로세스와 기술(게임 시뮬레이션, 수치 계산, 음영 처리)뿐만 아니라 당시 언어 동시성 및 안정성에 어떤 단점이 있었는지 언급합니다. UE3로 개발된 Gears of War의 게임 로직은 C++ 코드가 250,000줄에 이르렀고, 당시 UE3의 엔진 소스 코드도 마찬가지였습니다. (아래 사진)

UE3에서 개발한 Gears of War의 게임 화면입니다.

Gears of War, UE3, 그래픽 API 및 타사 라이브러리의 아키텍처 다이어그램 및 코드 볼륨.

게임 로직과 렌더링 기술이 점점 복잡해지면서 당시 UE 게임용 코드에는 게임 로직 시뮬레이션, 수치 계산, 컬러링의 세 가지 유형이 있었습니다.

상호 작용하는 개체가 시간 경과에 따른 게임 세계의 상태를 모델링하는 게임 논리 시뮬레이션의 경우 C++ 또는 스크립팅 언어로 작성된 고급 개체 지향 코드, 명령형 프로그래밍 스타일이 가비지 수집되는 경우가 많습니다. 대규모로 초당 30~60개의 업데이트(프레임), 최대 1000개의 다양한 게임 클래스(명령형 상태, 멤버 함수, 높은 역학 포함), 최대 10000개의 활성 게임 개체, 게임 개체가 업데이트될 때마다 일반적으로 5~10개의 다른 개체와 접촉합니다.

알고리즘(장면 그래프 순회, 물리 시뮬레이션, 충돌 감지, 경로 찾기, 사운드 전파)을 포함한 수치 계산의 경우 SIMD 내장 기능을 사용하여 C++로 작성된 하위 수준 고성능 코드는 본질적으로 큰 상수 데이터 구조를 사용하여 작은 입력 데이터 세트를 작은 출력 데이터 세트로 변환하는 기능을 합니다.

셰이딩의 경우 픽셀 및 버텍스 어트리뷰트이 생성되고 HLSL/CG 셰이딩 언어로 작성되었으며 GPU에서 실행되었습니다. 이는 본질적으로 데이터 병렬이었고 제어 흐름은 컴파일 타임에 알려졌는데, GPU가 16폭에서 48폭이었던 당시 당황스러울 정도로 병렬이었습니다. 대규모로 게임은 30FPS @ 1280x720p, 최대 5000개의 가시 개체, 프레임당 최대 1천만 픽셀 렌더링되고, 픽셀당 조명과 그림자에는 개체당 및 조명당 여러 렌더링 패스가 필요하며, 일반적인 픽셀 셰이더의 길이는 ~100명령이고, 셰이더 FPU는 4와이드 SIMD, ~500GFLOPS의 컴퓨팅 성능입니다.

이 세 가지 유형의 코드 정보를 비교한 표는 다음과 같습니다.

당시 UE3가 자주 직면했던 문제는 다음과 같습니다.

  • 성능. 60FPS에서 10,000개의 개체를 업데이트하는 경우 모든 것이 성능에 민감합니다.

  • 모듈식. 게임당 약 10~20개의 미들웨어 라이브러리가 매우 중요합니다.

  • 신뢰성. 오류가 발생하기 쉬운 언어/유형 시스템은 사소한 오류를 찾는 데 에너지를 낭비하게 하여 생산성에 심각한 영향을 미칩니다.

  • 동시. 하드웨어는 6~8개의 스레드를 지원하지만 C++에는 동시성이 없습니다.

성능을 위해 UE는 생산성도 마찬가지로 중요하다고 믿으며 생산성을 10% 높이기 위해 기꺼이 10% 성능을 희생하고 어셈블리 언어를 사용하지 않으며 최적화할 단순한 "핫스팟" 세트가 없습니다! 모듈성 측면에서 기본 목표는 오픈 월드 시스템에서 전체 소프트웨어 프레임워크의 클래스 계층 구조를 병렬로 확장하는 것입니다. 다음 그림은 기본 프레임워크와 확장 아키텍처의 코드 다이어그램입니다.

신뢰성 측면에서는 불법적인 주소, 범위를 벗어난 접근 등의 문제를 수정하여 더욱 견고한 코드를 얻습니다. (아래 사진)

상위: 개선 전의 신뢰할 수 없는 코드; 하단: 수정된 코드입니다.

동시성 측면에서 이상적인 상황은 모든 스레드가 언제든지 모든 상태를 수정할 수 있고 모든 동기화가 명시적이고 수동으로 이루어지며 정확성 속성에 대한 컴파일 시간 확인이 없습니다. 교착 상태나 경합이 없습니다. 현실적으로 이상적인 상황을 완벽하게 충족시키기는 어렵습니다! UE3 측정값은 다음과 같습니다:

  • 멀티스레드로 안전하게 기대할 수 없는 모든 작업을 담당하는 하나의 메인 스레드.

  • 1개의 헤비급 렌더링 스레드.

  • 4-6개의 보조 스레드 풀. 간단한 작업을 동적으로 할당합니다.

  • 세심한 주의를 기울여 프로그래밍해야 합니다!

그러나 위의 방법은 생산성에 큰 부담을 주고 스레드 수에 잘 적응하지 못한다.

셰이딩 동시성에서 UE3의 새로운 프로그래밍 언어는 "당황스러울 정도로 병렬" 셰이더 프로그래밍을 목표로 하며, 그 구조는 정적 제어 흐름(마스크에서 지원하는 조건부 사용)을 사용하여 자연스럽게 데이터 병렬 구현에 매핑됩니다.

수치 계산 동시성 측면에서 이는 본질적으로 순수한 함수형 알고리즘이지만 변경 가능한 상태에서 로컬 실행을 지원합니다. Haskell ST 및 STRef 솔루션은 참조 투명 코드의 로컬 힙 및 가변성 캡슐화를 지원합니다. 이는 암시적 병렬 프로그램의 구성 요소입니다. UE3 CPU 작업의 약 80%가 이런 방식으로 병렬화될 수 있습니다.

세 가지 유형의 코드의 특성과 병렬성은 다음 표에 나와 있습니다.

병렬성과 순도 사이의 관계는 아래 그림에 나와 있습니다.

Tim Sweeney는 또한 게임 기술의 간략한 역사를 설명했습니다.

Velvet Assassin을 위한 동적 조명 엔진 구축에서는 라이트맵을 사용하지 않고 Velvet Assassin 게임에서 완전히 동적 조명을 구축하는 방법을 설명합니다. 이 게임은 느슨한 옥트리 장면 관리, 객체 삼각형 OBB 트리, 그림자 맵이 포함된 하이브리드 단일/다중 채널 조명, 가시성 포털, 바운스 라이트를 통한 간접 조명 및 Xbox 360 관련 최적화를 지원하는 엔진을 사용합니다.

OBB와 ABB 비교.

하이브리드 조명은 다중 패스 및 단일 패스 포워드 렌더러의 하이브리드로, 각 기본 조명에 대한 한 패스와 모든 보조 조명이 하나의 패스로 결합됩니다. 주 광원은 주변 형상에 그림자와 빛 쿼리를 투사할 수 있는 클래식 다중 채널(Doom 3 스타일)을 사용합니다. 보조 조명은 클래식 단일 채널(Half Life 2 스타일)이고, 빛이 하나의 채널로 수집되며(수 기반 셰이더 변경), 그림자를 투사할 수 없으며, 주변 조명의 형상 쿼리(최대 수)입니다.

반사된 빛의 경우 표면에서 처음 반사된 간접광의 모양을 제공합니다. 이 빛은 배치된 표면을 비추어서는 안 되며 영향을 미치는 반구형 반경은 축에 의해 결정됩니다.

각 프레임에 대해 다음을 수행합니다.

  • 모든 주요 광원이 보이도록 유지하세요.

  • 섀도우 맵 풀을 배포합니다.

  • 각 렌더링에 대한 그림자 맵:

조명 프러스텀 내에 포함된 모든 개체를 렌더링합니다.

  • 뷰의 모든 객체를 가져옵니다.

  • 기본 패스 렌더링:

각 객체에 대해 셰이더에 대해 가장 가까운 N개의 보조 조명(중요도 순)을 수집합니다.

  • 각 주요 조명에 대한 추가 패스 렌더링:

조명 프러스텀 내에 있는 뷰의 모든 객체에 대해.

이것이 바로 공간 효율적인 인덱스 데이터 구조가 필요한 이유입니다.

Valvet Assassin은 멀티 스레드 렌더링을 채택하고, 첫 번째 스레드는 모든 공간 쿼리를 수행하고 "drawlist"를 컴파일하고, 두 번째 스레드는 셰이더 레지스터를 설정하고, 상태를 렌더링하고 배치를 제출합니다. 대부분의 장면에는 프레임당 300~1200개의 배치가 있습니다.

넓은 세계 및 지형 스트리밍 개선을 위한 기술에서는 MMO의 넓은 세계 지도에 대한 요구 사항, 과제 및 새로 도입된 기술에 대해 이야기합니다.

기사에서는 모든 게임 자산을 로드하는 조잡한 방법이 큰 세계로 확장되지 않는다고 언급합니다. 로드하는 데 시간이 너무 오래 걸리고 메모리를 너무 많이 사용하며 공동 편집이 필요합니다. 모든 데이터를 로드하지 않고, 필요에 따라 동적으로 데이터를 로드 및 언로드하며, 런타임 시 필요한 최소한의 데이터 세트를 유지하는 새로운 기술이 도입되어야 합니다.

기본 스트리밍은 모든 개체 인스턴스를 메모리에 유지하고, 인스턴스에는 많은 메모리(위치, 회전, 크기, 상태...)가 필요하지 않으며, 들어오고 나가는 데 필요한 리소스, 리소스 데이터(메시, 텍스처 등)에는 더 많은 메모리가 필요합니다. 캐릭터 주변 영역의 에셋을 로드하고 더 이상 표시되지 않는 에셋을 언로드합니다. 기본 스트리밍 로딩 문제: 모든 객체를 메모리에 보관해야 하고, 스트리밍 동작에 대한 제어가 거의 없으며, 불규칙하고 예측할 수 없는 스트리밍, 리소스 종속성 없이 예약하기가 어렵습니다.

이 문서에서는 스트리밍 로드의 파이프라인 프로세스를 개선합니다.

스트리밍 로딩을 위한 도구 요구 사항은 다음과 같습니다.

  • 공동 편집자

여러 사람이 함께 세상을 편집할 수 있어야 합니다.

  • 편집 충돌을 해결/방지해야 합니다.

  • 장면을 여러 레이어 파일로 저장합니다.

  • 레이어는 다른 사람들이 독립적으로 잠그고 편집할 수 있습니다.

  • 개정 관리와 동기화합니다.

  • 스트리밍 가능한 세계 데이터

단일 월드 파일이 없습니다.

  • 편집 및 스트리밍을 위해 대용량 파일을 별도의 파일로 나눕니다.

  • 아트 도구는 메시 인스턴스의 재사용을 촉진하고 지원해야 합니다.

  • 자원 의존성

전 세계의 모든 리소스 종속성을 계산하고 저장합니다.

  • 그룹

데이터를 더 잘 제어하는 방법: 편집, 베이크, 스트리밍.

  • 지형 구역화

2D 그리드로 자동 그룹화됩니다.

  • 파티션은 편집, 베이킹 및 스트리밍을 위해 관리 가능한 데이터 덩어리를 제공합니다.

  • 지형 데이터는 100MB까지 가능합니다.

  • 해당 섹터만 메모리에 유지합니다.

  • 필요에 따라 교환하세요.

  • 먼 섹터의 더 낮은 세부 근사치.

  • 실시간으로 편집하고 그립니다.

  • 공동 편집을 지원합니다.

  • 섹터별 잠금.

  • 편집기는 연속성을 자동으로 잠그고 처리합니다.

  • 지역

객체 인스턴스는 레벨 디자이너에 의해 공간적으로 영역으로 그룹화될 수 있습니다.

  • 각 영역은 플레이어가 접근할 때 동적으로 스트리밍할 수 있는 별도의 파일을 생성합니다.

  • 객체 그룹의 생성/파괴를 허용합니다.

  • 데이터 처리

편집 시 프로그램/원시 데이터.

  • 런타임에 구운 데이터입니다.

  • 지형 구역화

높이 맵, 텍스처 블렌드 맵, 구운 n 개의 가장 중요한 텍스처, 식생 확률 맵 및 사전 계산된 객체 인스턴스를 포함하는 섹터당 하나의 파일.

  • LOD: 미리 계산된 오류 측정항목, 월드 공간 노멀 맵.

  • 사용자 정의 색인 목록.

  • 지역

내보낼 때 사용할 각 영역의 리소스 목록을 설정합니다.

  • 편집자는 어떤 리소스(및 종속성)가 사용되는지 알고 있습니다.

  • 런타임 시 리소스 목록이 순차적으로 처리되고 로드됩니다.

  • 종속성에 따라 정렬되므로 로딩을 중지할 필요가 없습니다(예: 셰이더 라이브러리는 메시 로딩을 위한 전제 조건입니다).

스트리밍 로딩을 위한 런타임 요구 사항은 다음과 같습니다.

  • 자원관리 시스템

동적 로딩/언로딩.

  • 참조 카운팅.

  • 메모리 사용량 정보.

  • 자원 종속성 정보.

  • 리소스가 마지막으로 사용된 시간을 평가하는 데 사용되는 타임스탬프입니다.

  • x초 동안 사용되지 않은 리소스는 언로드될 수 있습니다.

  • 하드 메모리 및 소프트 메모리 제한.

  • 텍스처, 메시 등을 위한 다양한 리소스 풀

  • 스트리밍 영역

스트리밍 영역.

  • 모든 리소스를 미리 캐싱합니다.

  • 별도 스레드: 파일에서 리소스 데이터 로드, 선택적 데이터 변환/생성.

  • 메인 스레드: 리소스를 생성합니다.

  • 리소스를 로딩한 후 해당 zone에 모든 객체 인스턴스를 생성합니다. 리소스가 이미 로드되어 있고 선택적으로 시간이 지남에 따라 배포되기 때문에 빠릅니다(즉, 가장 크고 가장 중요한 개체부터 시작).

  • 플레이어가 대기 중인 자원이 있는 지역으로 직접 순간이동하면 어떻게 되나요?

옵션 A: 대체 리소스(예: 매우 낮은 해상도 텍스처)를 표시합니다.

  • 옵션 B: 관련 리소스를 사용할 수 있게 되면 개체가 팝업됩니다.

  • 옵션 C: 게임을 일시 중지하고 메인 스레드에서 나머지 리소스를 로드합니다.

  • 스트리밍 지형 구역화

낮은 디테일의 지오메트리와 텍스처를 사용하여 멀리 있는 섹터를 로드하고 렌더링합니다.

  • 고해상도 버전을 스트리밍합니다.

  • 조명 아티팩트/점프를 방지하려면 월드 공간 노멀 맵을 사용하세요.

Insomniac Physics에서는 IG의 물리 시스템, 셰이더, 라이브러리 셰이더, 사용자 정의 이벤트 셰이더 등의 발전을 설명합니다. 이 객체 시스템이 겪은 여러 반복의 개략도는 다음과 같습니다.

진화의 기본과 변화는 멀티스레딩, 프로세스 개선, Fine-graining, 코어 활용도 향상입니다.

Uncharted 2의 상태 기반 스크립팅: Between Thieves에서는 확장된 게임 개체 모델, 상태 스크립트 구문, 사례 연구, 구현 토론, 요약 및 제안을 포함하여 Uncharted 2의 스크립팅 시스템을 설명합니다.

스크립트 사용의 주요 이점: 엔지니어링 팀의 부담을 줄이고 코드는 데이터가 됩니다. 빠르게 반복하고 모드 커뮤니티의 주요 조력자인 콘텐츠 제작자에게 권한을 부여합니다.

게임 스크립팅 언어에는 데이터 정의 언어와 런타임 언어라는 두 가지가 있습니다. 런타임 스크립팅 언어는 일반적으로 다음과 같습니다. 가상 머신(VM)에 의해 해석됨, 단순함 – 낮은 오버헤드, 디자이너 및 기타 “프로그래머가 아닌” 사람이 사용 가능, 강력함 – 한 줄의 코드가 큰 영향을 미칠 수 있음. Naughty Dog는 PLT Scheme(Lisp 변형)을 기반으로 하는 데이터 정의와 런타임 스크립트를 많이 사용합니다. Lisp 유사 언어의 주요 장점: 쉬운 구문 분석, 데이터 정의 및 런타임 코드를 자유롭게 혼합할 수 있음, 강력한 매크로 시스템 - 사용자 정의 구문 정의가 용이함, Naughty Dog는 풍부한 Lisp 전통을 가지고 있습니다. 데이터 정의 언어에는 사용자 정의 텍스트 형식, Excel 쉼표로 구분된 값(.csv), XML 등이 포함됩니다. 런타임 언어에는 Python, Lua, Pawn(little C), OCaml, F# 등이 포함됩니다. 널리 사용되는 많은 엔진은 이미 Quake C, UnrealScript, C#(XNA) 등의 스크립팅 언어를 제공합니다.

모든 게임 엔진에는 게임 세계의 모든 개체 유형을 정의하는 일종의 게임 개체 모델이 있습니다. 일반적으로(항상 그런 것은 아니지만) 개체 지향 언어로 작성되며 기본 개체 모델을 확장하는 스크립팅 언어인 경우가 많습니다. 이를 수행하는 방법에는 여러 가지가 있습니다.

UnrealScript는 C++ 개체 모델, 일부 추가 기능이 포함된 단일 루트 클래스 계층 구조, UnrealScript(.uc)에 정의된 클래스, 자동 생성된 C++ 헤더 파일(.h), C++로 구현되거나 UnrealScript에서 완전히 구현되는 C++ 개체 모델과 긴밀하게 통합됩니다.

속성 중심 디자인은 Thief, Dungeon Siege, Age of Mythology, Deus Ex 2 등에 사용됩니다. 게임 개체는 단지 고유 ID(UID)일 뿐이며 "장식"은 다양한 속성(체력, 갑옷, 무기 등)을 가지며 속성은 데이터 + 동작을 캡슐화합니다.

Uncharted Engine의 개체 모델은 얕은 단일 루트 클래스 계층 구조를 가지며 수많은 추가 구성 요소를 포함합니다.

Uncharted 2의 상태 스크립트는 FSM(Finite State Machine) 지원을 추가하고, "속성"에 구애받지 않고, 대략적(객체당 하나의 스크립트)이며, 기존 엔터티 유형에 대한 스크립트 확장이나 다른 엔터티의 작업을 조정하는 "디렉터"와 비슷하다는 점에서 여러 면에서 속성 중심 모델과 유사합니다. 상태 스크립트에는 속성과 상태가 포함됩니다. 상태는 이벤트에 대한 반응, 시간에 따른 자연스러운 동작(업데이트 이벤트), 상태 간 전환 동작(시작/종료 이벤트) 등 런타임 스크립트 코드를 통해 객체의 동작을 정의합니다.

상태 스크립트를 인스턴스화할 때 네이티브(C++) 게임 개체에 연결합니다. 디자이너는 네이티브 C++ 개체 유형을 확장하거나 수정하여 새로운 개체 유형을 정의합니다. 트리거 영역에 부착: 볼록한 볼륨, 입구, 출구 및 점유 감지; 독립적인 개체로 배치: "감독"은 다른 개체(예: IGC)의 작업을 조정합니다. 관련 작업: 작업은 체크포인트이며 스크립트는 관련 작업을 관리하고 AI 업데이트를 예약하며 플레이어 목표를 제어합니다.

다음 그림은 사용자 정의 개체 유형(취약한 플래그)의 예입니다.

간단한 VM으로 구현되는 클래스 체계 런타임 언어인 가상 머신 구현 측면에서 각 트랙은 람다라는 바이트코드 블록으로 컴파일됩니다.

VM의 내부 상태에는 현재 람다(바이트코드 프로그램)에 대한 포인터, 현재 명령어의 인덱스, 임시 및 즉시 데이터의 레지스터 뱅크가 포함됩니다. 여기서 레지스터는 변형 유형입니다.

언어는 중첩된 함수 호출을 지원하므로 호출 스택이 필요합니다. 스택 프레임 = 레지스터 세트 + 프로그램 카운터.

상태 저장 스크립트 코드는 Continuation이라는 것을 통해 대기(휴면)할 수 있습니다.

성공적인 스크립팅 시스템의 주요 특징: 게임 엔진에 통합된 가상 머신, 매 프레임마다 코드를 실행(업데이트)하는 기능, 이벤트에 응답하고 전송하는 기능, 게임 개체를 참조하는 기능(핸들, 고유 ID 등을 통해), 게임 개체를 조작하는 기능, 디자이너가 스크립트에서 새로운 개체 유형을 정의하는 기능.

다양한 엔진 스크립팅 아키텍처: 스크립트 기반 엔진(엔진은 단지 스크립트에 의해 호출되는 라이브러리임), 엔진 기반 스크립트(간단한 스크립트 이벤트 핸들러, 스크립트된 속성 또는 구성 요소, 스크립트된 게임 개체 클래스).

Star Ocean 4 - 유연한 셰이더 관리 및 후처리는 RGP 게임 STAR OCEAN: The Last Hope에서 Aska Game Engine이 사용하는 셰이더 관리 및 포스트 프로세스 기술을 공유합니다. 완전히 유연한 셰이더는 관리 아티스트가 Maya에서 재료를 생성하고 Hypershade 인터페이스를 사용하여 아티스트의 설정에 따라 셰이더 바이너리 파일을 자동으로 생성할 수 있음을 의미합니다.

디자인 정책은 아티스트가 Maya에서 셰이더를 생성하고, 프로그래머 없이 셰이더를 생성할 수 있고, 프로그래머에 의해 제한되지 않고, 새로운 아이디어를 즉시 시도할 수 있으며, 아티스트에 대한 교육, 매개변수 설정 및 셰이더 구성 방법, 물리 지식(약간의) 및 템플릿이 필요하다는 것입니다.

런타임(개발 중) 생성 셰이더의 장점은 다음과 같습니다. 셰이더 바이너리를 리소스 파일에 포함할 필요가 없고, 아티스트가 셰이더를 자유롭게 만들 수 있으며, 셰이더 변경 사항이 런타임에 쉽게 지원되고, 셰이더 바이너리가 관리하기 쉽습니다. 단점: 셰이더 변형 수가 폭발적으로 증가하고, 셰이더 바이너리가 크며, 가능한 셰이더 변형을 만들어야 하고, 게임에서 가능한 모든 콘텐츠를 플레이해야 합니다.

셰이더는 Maya의 셰이더 노드에 해당하는 특정 기능을 가진 작은 셰이더 노드를 구현하도록 세분화될 수 있습니다. 아티스트는 UV, 컬러, 노멀, 알파 등 각 입력과 출력을 자유롭게 연결할 수 있습니다.

조명 결과를 사용하여 표면 음영 처리: Phong, Anisotropic Phong, Blinn-Phong, Normalized Phong, Ashikhmin, Kajiya-Kay, Marschner(알베도 맵, 반사 맵, 광택(광택) 맵, 프레넬 맵, 오프셋 맵, 반투명, 앰비언트 오클루전(AO)).

셰이딩 편집기는 노멀, UV, 그림자, 투영, 계산 및 기타 여러 기능 노드도 지원합니다.

쉐이딩 노드 편집기.

포스트 프로세스 측면에서 Aska Game Engine은 톤매핑(표준 톤매핑, 필름 시뮬레이션(필름 또는 C-MOS 센서의 사양 재현, 필름 그레인 또는 디지털 노이즈 재현), 흔들림), 렌즈 시뮬레이션(DOF, 눈부심, 물리적 기반 렌즈 구조, 모션 블러(카메라, 개체), 컬러 필터(대비, 밝기, 모노톤, 톤 곡선, 색온도), 기타 효과(야외 빛 산란, 광축 시뮬레이션, 스크린 스페이스 앰비언트 오클루전(SSAO)) 등을 지원합니다.

셰이딩 파일은 캐싱 메커니즘을 사용하여 컴파일된 셰이더를 개발 키트의 셰이더 캐시 파일, 캐시 구성 요소(셰이딩 키, 상수 테이블, 셰이더 바이너리)에 저장합니다. 이 파일에는 게임에서 사용되는 가능한 모든 셰이더 조합이 포함되어 있다고 가정합니다.

셰이더 캐시는 QA 과정에서 생성되었으며 프로젝트가 종료되면서 캐시 파일의 크기가 늘어나 처음에는 10M로 추정됐으나 실제로는 10M를 초과했다. 크기 문제에 대한 해결책은 런타임 시 각 셰이더 바이너리의 압축을 풀고, 셰이더 캐시를 L1과 L2로 분리하고, 여러 셰이더 캐시 파일을 지원하고, Windows에서 셰이더 파일을 관리하는 도구를 만들고, 구현 성능과 크기 제어 매개변수의 균형을 맞추는 것이었습니다.

그러나 많은 노력에도 불구하고 크기는 50M가 넘고 캐시 조합도 30000개가 넘었으며, 캐시 파일을 분할한 후에도 여전히 허용 가능한 파일 크기를 초과했습니다. 셰이더 캐시 파일의 세부 사항을 분석하기 시작하면서 셰이더 어댑터가 셰이더 조합의 대부분을 차지하고 있음을 발견했습니다.

셰이더 어댑터란 무엇입니까?

그림자, 프로젝터 등과 같이 런타임에 추가되는 셰이더입니다. 이러한 셰이더는 특히 그림자에서 80%를 차지합니다. 하나의 셰이더는 객체에 대해 5개의 그림자도 지원합니다.

셰이더 어댑터(그림자) 수에 대한 제한을 늘리고 생성 중 또는 도구에서 수를 제한하면 크기가 크게 줄어들지만 수동 조정이 필요한 외관 문제가 발생합니다. 셰이더 어댑터의 경우 생성되지 않은 셰이더 구현에 대한 지원은 기본 셰이더를 사용하여 사라지는 것보다 낫습니다.

QA팀에서 셰이더 관련 사양 및 리소스 수정, 디버깅 기능 사용, 게임 플레이, 여러 테스터가 생성한 파일 병합 등을 하면서 캐시 파일을 생성했는데, 이 시스템이 낯설기 때문에 수십 명의 테스터가 작업을 반복하면서 몇 주가 걸릴 정도로 그 과정이 예상보다 훨씬 힘들었습니다.

위 셰이딩 시스템의 장점은 다음과 같습니다: 높은 유연성, 아티스트가 프로그래머 없이 다양한 셰이더를 만들 수 있음, 불합리한 셰이더 조합을 만들 수 있음, 성능이 최적화됨(셰이더 즉시 상수...), 셰이더 컴파일러를 사용하여 최적의 셰이더 코드가 자동으로 생성될 수 있습니다.

위 셰이딩 시스템의 단점은 셰이더 캐시 생성 비용, 자동 생성 제한, 파일 크기 문제, 캐시 파일이 너무 크다는 것입니다. 따라서 리소스를 생성하는 경우 이 점을 인지하고 셰이더 수를 줄이려고 노력해야 하며 셰이더 생성의 어려움, 아티스트는 셰이더의 메커니즘을 알아야 합니다.

또한 Aska Game Engine은 물리적 기반 Bokeh DOF, 렌즈 시뮬레이션, HDR 렌더링 및 기타 효과도 구현했습니다.

Bokeh 뎁스 오브 필드(DoF) 렌더링 순서도.

톤매핑의 경우 특정 알고리즘이 사용되지 않지만 필름 또는 C-MOS 센서의 사양을 사용하여 곡선 테이블을 생성하고 곡선은 로그 기반으로 압축됩니다.

cpp
float u = saturate(log2(vInCol.r+1)/2.32);
vOutCol.r = tex1D(s, u).r;

톤매핑 렌더링 흐름도.

일반적인 색조 표면 효과 비교.

다양한 품질 수준에서 포스트 프로세스 파이프라인 비교.

CryEngine 3의 빛 전파 볼륨은 CryEngine 3의 조명 파이프라인, 핵심 아이디어, 애플리케이션, 개선 사항, 결합 기술, 호스트 최적화 등을 공유합니다.

2009년 CryEngine 3는 이미 많은 주류 플랫폼을 지원하며 통합 그림자 맵, SSAO, 지연 조명 등을 사용하여 실외 및 실내 조명 렌더링에 적합합니다.

CryEngine 3는 조명 축적 파이프라인을 사용합니다. 전역/국부 반구 환경을 적용하고, 이를 지역 지연 광 감지기로 대체하고(선택 사항), 전역 조명, 간접 항에 SSAO를 곱하여 앰비언트 오클루전(AO)을 적용하고, 간접 조명 위에 직접 조명을 적용합니다.

위 그림의 전역 조명을 위해 CryEngine 3는 LPV(Light Propagation Volume)를 사용합니다. LPV의 목표는 화면 적용 범위(해상도 × 오버드로), Radiance 캐싱 및 저장 기술, 포인트 라이트의 대규모 조명, 전역 조명, 관련 미디어 렌더링(아직 진행 중인 작업...), 콘솔(Xbox 360, PlayStation 3) 친화적에서 조명 복잡성을 분리하는 것입니다.

LPV의 가장 중요한 단계는 방출체의 주어진 초기 방사선 분포, 방사선 전파의 반복 프로세스, 인접한 셀에 대한 6점 축 템플릿(수집, GPU에 더 효율적, 에너지 보존)에서 시작하여 방사선 볼륨에서 빛의 전파이며, 각 반복은 결과를 증가시키고 더 전파됩니다.

6포인트 축 스텐실(6포인트 축 스텐실) 다이어그램. *

방사선 전파 과정의 개략도(2D 단일 셀의 단일 반복을 예로 들어).

위 이미지에서는 이미터가 위치한 셀에만 초기 복사 분포를 배치하는 것이 가능합니다(하나의 광원에 대해 하나의 픽셀만 렌더링하면 되므로 매우 편리한 상황). 그리드의 최종 복사 분포를 얻기 위해 제안된 솔루션은 복사를 반복적으로 전파하는 것입니다. 각 반복은 각 셀에 6점 축 템플릿을 적용합니다. 즉, 각 셀에 대해 인접한 축 셀의 방사선이 수집 방식을 통해 전송됩니다. 수집은 GPU 친화적이며, 각 반복의 결과는 최종 방사선 볼륨으로 수집되고, 다음 반복은 이전 반복의 결과에 적용됩니다.

LPV의 여러 반복에 대한 개략도.

위 이미지는 방사선 전파를 여러 번 반복한 결과입니다. 첫 번째 줄은 초기 방사선 분포입니다. 광원이 많다는 것을 알 수 있습니다. 왼쪽 상단의 사각형은 방사 텍스쳐이므로 이 사진에서는 3D 방사 텍스쳐를 확대한 것입니다. 이 프로세스는 매우 감쇠됩니다. 즉, 여러 반복(광원의 초기 강도에 따라 32x32x32 방사 볼륨의 경우 8~16회)으로 제한될 수 있으며 결과 방사 분포는 이러한 모든 방사 반복의 누적입니다.

LPV로 렌더링할 때 SH Irradiance Volumes와 유사한 평소와 같은 셰이딩, 월드 공간 위치를 사용한 간단한 3D 텍스처 조회, 조도를 얻기 위한 법선과의 코사인 리프 통합, 셰이더에서 2차 SH의 간단한 계산, 투명 객체 및 참여 미디어 조명, 디퍼드 렌더링/조명, 볼륨 모양을 누적 버퍼로 그리기, 거의 모든 지연 최적화 지원.

LPV는 또한 수많은 광원의 조명을 지원합니다. **반사 그림자 맵(RSM)**과 결합하면 전역 조명 효과를 얻을 수 있습니다. 그 중 반사 그림자 맵에는 MRT 레이아웃 그림자 맵(깊이, 일반 및 색상)이 있으며 이는 효율적인 VPL(Virtual Point Light) 생성기입니다.

RSM이 전달하는 데이터: 일반(오른쪽 위), 깊이(왼쪽 아래), 색상(오른쪽 아래).

LPV와 RSM을 결합한 전역 조명의 렌더링 단계는 다음과 같습니다.

  • VPL의 초기 방사선을 방사선량에 주입합니다.

포인트 렌더링.

  • 버텍스 텍스처 가져오기/R2VB를 사용하여 각 점을 적절한 셀에 배치합니다.

  • SH를 사용한 각 VPL의 대략적인 초기 휘도, 셰이더의 간단한 분석 표현.

  • 방사선을 퍼뜨리세요.

  • 방사선이 전파되는 장면을 렌더링합니다.

LPV 렌더링.

VPL의 문제점은 VPL 주입에 위치 오프셋이 포함되고, 주입된 VLP의 위치가 그리드 정렬이 되고, 공간 방사선 근사화의 결과가 발생하며, 양면 얇은 기하학적 조명과 같은 예상치 못한 방사선 오버플로입니다.

솔루션은 다음과 같습니다.

  • VPL을 일반 또는 광원 방향으로 그리드의 절반만큼 오프셋합니다.

  • 최종 렌더링 프로세스 중에 이방성 양측 필터링을 통해 결합됩니다. 표면 노멀 이동의 휘도 샘플, 휘도 기울기 계산, 휘도와 휘도 기울기 비교.

전역 조명의 계단식 조명 전파 볼륨은 다음과 같이 설명됩니다.

  • 크기가 제한되고 해상도가 낮은 그리드입니다.

  • 방사선량에 대한 다중 해상도 방법. Cascaded Shadow Maps 기술과 유사하게 주변 방사선이 시야 외부에 보존됩니다.

  • 각 캐스케이드는 독립적입니다. 각 캐스케이드에는 인접한 가장자리를 통해 방사선을 전송하는 별도의 RSM이 있어 특정 RSM의 크기에 따라 물체를 필터링합니다.

  • 방사선 방출체의 효율적인 계층적 표현.

GI와 SSGI를 결합하는 단계는 다음과 같습니다.

  • 화면 공간 전역 조명.

SSGI의 제한 사항: 화면 공간 정보만 제공되며 가까운 개체에 대한 큰 커널 반경입니다.

  • LPV의 한계: 로컬 솔루션, 저해상도 공간 근사.

  • SSGI와 LPV는 서로를 보완합니다.

  • 맞춤형 블렌드.

[번지의 조명 연구](https://advances.realtimerendering.com/s2009/SIGGRAPH 2009 - 조명 연구: Bungie.pdf)에서는 Bungie가 개발한 게임 Halo 3의 실시간 조명 및 사전 계산된 전역 조명을 소개합니다. 실시간 조명에는 하늘과 대기, 하늘빛, Probabilistic Shadow Test, VSM(Variance Shadow Map), ESM(Exponential Shadow Map), CSM, EVSM 등이 포함됩니다. 사전 계산 측면에서는 광자 매핑을 기반으로 원래 느린 부분을 개선하고 새로운 렌더링 프로세스를 제안하며 속도가 크게 향상되었습니다.

대기 렌더링에는 단일 및 다중 산란, GPU에서의 사전 계산, 공간에서 볼 수 있는 사전 계산 및 광선 지원을 고려하는 Precomputed Atmospheric Scattering 방법이 채택되었습니다. 산란 모델은 Raleigh 및 Mie 산란을 사용합니다. 사전 계산 부분에는 투과도, 내부 산란, 조도가 미리 포함되어 있습니다. 미리 계산된 룩업 테이블은 텍스처로 저장되며, 텍스처는 GPU를 사용하여 생성됩니다. (아래 사진)

하늘빛의 경우, 먼 산과 물체에 대해 단일 색상을 하늘 방사조도로 사용하는 Precomputed Atmospheric Scattering 방법을 사용합니다. 가까운 물체는 CIE 하늘 밝기 분포, 미리 계산된 방사조도를 사용하여 크기가 조정되고, 각 방위각은 SH에 투영되고, 다항식 피팅 계수가 사용되며, GI 모양은 PRT를 사용하여 렌더링됩니다.

하늘빛의 대비, 위에서 아래로: 직사광만, SH로 근사, PRT로 근사.

확률적 그림자 테스트 샘플이 그림자에 있을 확률, 현재 수신기 및 차단기 깊이를 고려하면 해당 공식은 다음과 같습니다.

f(dr)=Pr(dodr)

그 중에는:

  • do는 교합체의 깊이 분포 함수를 나타내는 랜덤 변수입니다.

  • dr은 현재 그림자 수신기의 깊이입니다.

분산 기반 그림자 테스트의 경우 이진 테스트는 확률 분포 함수가 됩니다. 이는 현재 조각이 그림자에 있을 확률입니다. Pr(dodr)은 두 순간에서 발생합니다.

μ=E(do)\시2=E(do2)E(do)2

체비쇼프 부등식을 검정의 상한으로 사용합니다.

Pr(dodr)  pmax(dr)  σ2σ2+(μdr)2

VSM을 기반으로 더 나은 결과를 얻기 위해 Bungie 팀은 ESM과 EVSM도 시도하여 보다 정확한 그림자 효과를 얻었습니다.

사전 계산된 조명의 경우 기존 CPU 광자 매핑 파이프라인에는 두 가지 주요 병목 현상이 있습니다.

번지팀은 병목 현상 부분을 최적화했습니다. 직접 조명 단계에서는 GPU KD 트리의 빠른 레이 캐스팅이 사용됩니다. 최종 수집 단계에서는 GPU KD 트리의 빠른 레이 캐스팅, 광자 조명 절단(Cut), 간접 조명 클러스터링 샘플 포인트가 사용됩니다.

광자 조명 클리핑(컷)은 광원 절단과 유사하며, 광자 트리의 각 노드의 방사조도를 추정하고, 트리를 통해 "컷"을 계산하고, 보간을 위해 RBF 기반을 사용합니다.

게임 객체 컴포넌트 아키텍처의 이론과 실제에서는 컴포넌트 지향과 객체지향의 특징과 차이점, 구현 방법을 설명한다.

GameObject(GameObject)는 게임 세계를 대표하는 모든 것(예: 캐릭터, 소품, 차량, 미사일, 카메라, 트리거 볼륨, 조명 등)입니다. 명확성과 통일성, 기능, 사물 및 도구의 이동성, 코드 재사용 및 유지 관리(예: 중복을 줄이기 위한 모듈성/상속 사용)를 요구하는 표준 온톨로지가 필요합니다.

초기 엔진은 종종 통합을 사용하여 다양한 유형의 게임 개체를 구현했습니다. 객체의 유형이 증가함에 따라 다중 상속은 문제를 해결하는 방법이었지만 확장이 잘 되지 않았고 최종 과제인 디자인/요구 사항 변경을 해결하지 못했습니다. (아래 사진)

이 상속 중심 방법의 모든 관계 집합을 방향성 비순환 그래프로 설명할 수 있는 것은 아닙니다. 클래스 계층 구조는 변경하기 어렵고, 함수는 상위 클래스로 마이그레이션되며, 형제 유형의 특수화에는 추가 메모리 소비가 필요합니다. 복잡한 애플리케이션의 경우 복잡한 클래스 상속 트리가 많이 생성됩니다.

컴포넌트 기반 접근 방식은 관점 지향 프로그래밍과 관련이 있지만 동일하지는 않습니다. 클래스는 속성(데이터)과 동작(논리)을 포함하는 컨테이너입니다. 속성은 키-값 쌍의 목록이고 동작은 OnUpdate() 및 OnMessage()와 같은 응답 함수가 있는 개체입니다.

객체지향과 컴포넌트지향의 비교.

이 기사에서는 데이터 기반 생성, 텍스트 또는 바이너리, 파이프라인에서 로드, 동시 로드, 지연 인스턴스화, 특수 도구, 데이터 기반 상속 등에 대해서도 언급합니다.

cpp
TOD_BeginObject GameObject 1 "hotdog_concession"
{
    behaviours
    {
        PhysicsBehaviour 1
        {
            physicsObject "hotdog_concession"
        } ,
        RenderBehaviour 1
        {
            drawableSource  "hotdog_concession"
        } ,
        HealthBehaviour 1
        {
            health 2.000000
        } ,
        GrabbableBehaviour 1
        {
            grabbableClass "2hnd"
        }
    }
}
TOD_EndObject

데이터 기반 생성의 장점: 새로운 속성 제공이 쉽고, 새로운 유형의 엔터티 생성이 쉽고, 동작이 이식 가능하고 재사용 가능하며, 게임 개체와 통신하는 코드가 유형에 구애받지 않고, 모든 것이 서로 상호 작용하도록 패키지 및 설계되었습니다. 즉, 범용 코드를 작성할 수 있습니다.

데이터 기반 생성의 단점: 일반 코드를 작성해야 하고, 게임 개체는 형식이 지정되지 않고 불투명하며, 개체에 연결 가능한 동작이 있는 경우 개체에 연결되고, 코드는 모든 개체를 동일하게 처리해야 하며, 데이터와 속성을 쿼리할 수 없습니다. 예를 들면 다음과 같습니다.

cpp
if object has AttachableBehaviour //
    then attach to it

Black Rock Studio의 렌더링 기술은 알파 조합, 지상 식물, 나무 렌더링, 화면 공간 투명도 마스킹 및 기타 기술의 기본을 공유합니다.

기사에서는 알파 테스트가 조각이 단일 값으로 표시되는지 여부를 결정하고 z 버퍼와 함께 사용할 수 있는지 여부를 결정하므로 앨리어싱이 발생할 수 있다고 언급했습니다. Alpha To Coverage는 알파를 픽셀의 적용 범위 마스크로 변환하는 반면, 적용 범위 마스크는 알파 테스트와 결합될 때 더 부드러운 가장자리를 제공하는 MSAA 적용 범위 마스크와 AND로 연결됩니다. 이는 z-버퍼와 함께 작동하지만 결과 알파 그래디언트가 항상 좋아 보이는 것은 아닙니다. (아래 사진)

알파 테스트(위) 및 알파 투 커버리지(아래)로 인해 발생한 결함입니다.

본 논문에서는 보다 부드럽고 자연스러운 지피 혼합 효과를 얻기 위해 위의 문제를 해결하기 위한 새로운 처리 프로세스를 제안합니다. 새로운 렌더링 프로세스는 3단계로 구분됩니다.

  • 밀도 플롯. 오프라인 생성, 표면 덮개를 배치하는 데 사용되는 2D 지도.

  • 청크 캐시. 카메라가 이동함에 따라 지표면의 종류, 위치, 크기 등을 계산합니다.

  • 버텍스 버퍼링. 프레임별로 생성된 스프라이트 정점은 유형, 위치 및 크기로 인코딩됩니다.

렌더링할 때 잔디는 카메라 주변의 고정된 영역에 렌더링됩니다. 면적은 8제곱미터의 타일 400개로 나누어져 있습니다. 각 평방 미터에는 화면에 정렬된 4개의 스프라이트가 포함되어 있으므로 각 타일에는 256개의 스프라이트가 포함됩니다. 각 스프라이트 정보는 4차원 벡터로 인코딩되며 타일은 메모리에 캐시됩니다. 카메라 위치를 기반으로 렌더링해야 하는 타일을 결정한 다음 밀도 맵을 쿼리하여 바닥 피복 데이터를 얻고, 각 프레임에 대한 버텍스 버퍼를 생성하고, 타일 캐시에서 타일 정보를 복사합니다. CPU는 표시되는 각 타일에 대해 16KB를 복사해야 합니다.

카메라 근처에서 타일 바닥재를 렌더링하는 전설입니다.

여러 믹싱 효과의 비교 차트.

알파 블렌딩에는 지오메트리 정렬이 필요하지만 일반 지오메트리 배치를 사용하면 두 가지 수준의 세분성을 포함하여 정렬이 더 쉬워집니다. 타일은 뒤에서 앞으로 렌더링되고 스프라이트는 각 타일 내에서 정렬됩니다. 각 타일 내의 순서는 미리 계산되어 16개 카메라 방향(아래 이미지)의 렌더링 순서를 미리 계산하고 현재 방향에 가장 가까운 카메라 방향의 순서를 선택합니다. 성능을 향상시키기 위해 스프라이트는 총 8개의 32개 "유닛"으로 그룹화되고 유닛 수준에서만 정렬됩니다.

사전 계산된 블록 정렬, 총 16개 방향 중 사진에서는 4개만 선택되었습니다.

이 믹스의 이점은 고품질 알파 블렌드 지면 커버리지, 저렴한 CPU 시퀀싱 및 아티스트 친화적인 워크플로우입니다. 단점은 오버드로가 높다는 것인데, 이는 GPU에 대한 오버헤드가 높다는 것을 의미합니다.

또한 이 기사에서는 나무와 같은 개체를 더 잘 그릴 수 있는 화면 공간 알파 마스크 프로세스를 제안합니다.

트리의 알파 템플릿을 그릴 때 구문 분석된 깊이를 사용하고 Z 쓰기를 비활성화하고 트리의 알파 값을 렌더링합니다.

cpp
sampler alphaTexture : register(s0);

struct PSInput
{
     float2 vTex : TEXCOORD0;
};

float4 main( PSInput In ) : COLOR
{
    return tex2D(  alphaTexture , In.vTex ).aaaa;
}

알파 마스크를 출력할 때 소스 + 대상 및 max(소스, 대상)의 두 가지 방법이 있습니다.

ADD는 불투명한 결과를 제공하고 MAX는 더 세밀하고 부드러운 윤곽을 제공하며 운 좋게도 두 가지를 동시에 생성하는 것이 가능합니다! 렌더링 대상의 색상 및 알파 구성 요소를 작성하기 위한 다양한 혼합 모드로 최종 조합 중 두 값의 평균을 냅니다. 결합된 PS 코드는 다음과 같습니다.

hlsl
sampler maskImage: register(s0);
sampler treeImage: register(s1);
sampler worldImage: register(s2);

float4 main( float2 vTexCoord : TEXCOORD ) : COLOR
{
    float4 vTreeTexel = tex2D( treeImage, vTexCoord.xy );
    float4 vWorldTexel = tex2D( worldImage, vTexCoord.xy );
    float4 vMaskTexel = tex2D( maskImage, vTexCoord.xy );

    float lerpValue = (vMaskTexel.r +vMmaskTexel.a) * 0.5f;
    return lerp( vWorldTexel, vTreeTexel, lerpValue );
}

최종 효과는 다음과 같이 비교됩니다.

또한 이 기사에서는 디퍼드 렌더링에 대한 실제 경험을 공유합니다. Split/Second 패스는 디퍼드 셰이딩 렌더러를 사용하여 지오메트리에서 조명을 분리합니다. 메인 장면을 렌더링할 때 조명에 필요한 정보는 지오메트리 버퍼(G-Buffer)에 기록되고, 장면의 조명은 포스트 프로세스 단계에서 발생하는 조명 패스로 연기됩니다. MRT의 구성은 다음과 같습니다.

디퍼드 렌더링을 사용하여 MSAA에 최적화되었습니다. 전체 화면 앤티앨리어싱으로 변경된 FSAA는 장면을 필요한 것보다 더 높은 해상도로 렌더링하여 평균을 필요한 해상도로 낮춰 성능에 심각한 영향을 미칩니다. MSAA는 픽셀 셰이더를 픽셀당 한 번만 실행하여 다각형으로 덮힌 각 조각의 조각 색상을 설정하지만, G-버퍼는 보간에 적합하지 않기 때문에 하드웨어를 사용하여 조각을 평균화할 수 없습니다. 즉, 각 조각을 수동으로 혼합해야 합니다.

관찰을 통해 우리는 픽셀의 85%가 폴리곤 내부에 위치한다는 것을 발견했습니다. 이는 모든 조각이 동일하다는 것을 의미합니다. 나머지 15%의 차이점을 빠르게 식별할 수 있습니까? 식별해야 할 조각은 아래 그림에서 빨간색으로 표시됩니다.

다각형 가장자리를 식별하려고 시도하는 하드웨어 기능인 중심 샘플링을 사용할 수 있습니다. 중심 샘플링은 다각형 경계 외부의 버텍스 어트리뷰트 샘플링을 방지합니다.

중심 샘플링은 색상을 결정하는 데 사용되는 위치를 다각형으로 덮힌 모든 샘플 점의 중심으로 조정하므로 중심이 이동하면 다각형의 가장자리에 있게 됩니다.

다행히 픽셀 셰이더에서는 중심 샘플의 값을 얻을 수 있으며, 이 값이 0이 아닌 경우 삼각형이 픽셀의 모든 샘플을 포함하지 않는다는 것을 알 수 있습니다.

cpp
struct PSInput
{
    float4 vPos : TEXCOORD0;
    // 샘플링의 좌표 !TEXCOORD1_CENTROID는
    float4 vPosCentroid : TEXCOORD1_CENTROID;
};
 
float4 main( PSInput In ) : COLOR
{
    // 대비 및 픽셀(Pixel)위치 의 ,만약 ,,픽셀(Pixel)。
    float2 vEdge = In.vPosCentroid.xy - In.vPos.xy;
    // ,를 위해 ,:float fEdge = (abs(vEdge.x) + abs(vEdge.y) <= 0.001f) ? 0.0f : 1.0f;
    float fEdge = (vEdge.x + vEdge.y == 0.0f) ? 0.0f : 1.0f;
 
    // 레이턴시 셰이딩 ,개 까지 G-Buffer 내에서 의 。
    return float4( fEdge );
}

섀도우 아티팩트를 방지하기 위해 백분율 프로그레시브 필터링을 사용하는 PCF는 섀도우 맵에서 여러 샘플을 가져와 각 섀도우 수신기에 대해 깊이 테스트를 수행한 다음 결과의 평균을 구합니다. 화면을 3가지 영역으로 나눌 수 있습니다: 확실히 그림자에 있는 영역, 확실히 그림자에 있지 않은 영역, 그림자에 있을 수도 있고 없을 수도 있는 영역. 실제로 PCF는 이러한 영역 중 마지막 영역(그림자에 있을 수도 있고 없을 수도 있는 영역)에만 적용하면 됩니다. 정확하게 계산할 수는 없지만 근사화하여 PCF를 수행해야 하는 위치를 보여주는 마스크를 생성할 수 있습니다.

첫 번째 패스는 화면 크기의 1/4인 섀도우 맵을 출력하고, 두 번째 패스는 가장자리를 확장하기 위해 보수적 래스터라이제이션를 사용하여 화면 크기의 1/16입니다.

1/4 크기 그림자 맵과 1/16 그림자 맵은 가장자리를 확장하기 위해 보수적인 알고리즘을 사용합니다.

이 기사에서는 조사량의 실습, 효과 및 성능 최적화도 공유합니다.

14.3.3.2 병렬 처리

2000년대 초반에는 엔진 병렬화 방법에 대한 기술과 구현을 설명하는 문서(병렬 게임 엔진의 프레임워크 설계)가 이미 있었습니다. 그 중 문서에는 Free Step과 Lock Step이라는 두 가지 실행 모드가 언급되어 있습니다. (아래 사진)

위: 프리 스텝 모드; 하단: 잠금 단계 모드.

자유 단계 실행 모드를 사용하면 계산을 완료하는 데 필요한 시간 내에 시스템을 실행할 수 있습니다. Free Step은 시스템이 언제든지 완료할 수 없지만 실행해야 하는 틱 수를 자유롭게 선택할 수 있기 때문에 오해의 소지가 있습니다. 이 접근 방식을 사용하면 단순히 상태 관리자에게 상태 변경을 알리는 것만으로는 작업을 완료하는 데 충분하지 않습니다. 데이터가 필요한 시스템이 업데이트할 준비가 되어도 공유 데이터를 수정한 시스템이 계속 실행 중일 수 있으므로 데이터도 상태 변경 알림과 함께 전달되어야 합니다. 이 모드는 더 많은 메모리와 복사본을 사용하므로 모든 상황에서 가장 이상적인 모드는 아닐 수 있습니다.

잠금 단계 실행 모드에서는 모든 시스템이 한 클럭 내에 실행을 완료해야 합니다. 구현하기가 더 간단하고 알림을 통해 데이터를 전달할 필요가 없습니다. 다른 시스템의 변경 사항에 관심이 있는 시스템은 다른 시스템에 값을 간단히 쿼리할 수 있기 때문입니다(물론 실행이 끝날 때). Lock Step은 여러 단계의 계산을 인터리빙하여 의사 자유 단계(pseudo-Free Step) 작동 모드를 구현할 수도 있습니다. 이것의 한 가지 용도는 AI가 첫 번째 시계 내에서 초기 "큰 보기" 목표를 계산하도록 하는 것이며, 다음 시계에 대한 목표 계산을 반복하는 대신 이제 초기 목표를 기반으로 보다 집중된 목표를 제시할 수 있습니다.

이 문서에서는 작업을 여러 개의 더 작은 세부 하위 작업으로 분할하여 작업 관리자가 예약하고 실행을 위해 스레드 풀의 스레드에 할당하는 스레드 풀 기술에 대해서도 언급합니다. (아래 사진)

병렬화 엔진에는 시스템의 핵심 개념이 포함됩니다. 시스템과 장면, 작업, 개체 간의 관계는 다음 그림과 같습니다.

게임 엔진 메인 루프의 단계는 다음과 같습니다.

  • 플랫폼 관리자를 호출하여 현재 플랫폼에서 작동하는 데 필요한 모든 창 메시지 및/또는 기타 플랫폼별 항목을 처리합니다.

  • 계속하기 전에 시계 시간이 만료될 때까지 기다리는 스케줄러로 실행을 전송합니다.

  • Free Step 모드의 경우 스케줄러는 이전 시계에서 어떤 시스템 작업이 실행을 완료했는지 확인합니다. 완료된(즉, 실행 준비가 완료된) 모든 작업은 작업 관리자로 전송됩니다.

  • 이제 스케줄러는 현재 시계에 어떤 작업이 완료될지 결정하고 해당 작업이 완료될 때까지 기다립니다.

  • Lock Step 모드의 경우 스케줄러는 모든 작업을 실행하고 각 클록 단계가 완료될 때까지 기다립니다.

작업을 실행할 때 스케줄러는 작업 큐에서 작업을 가져와 실행을 위해 스레드 풀에 배포합니다. 수행되는 작업에는 작업 관리자, 서비스 관리자, 상태 관리자, 환경 관리자와 같은 많은 개념이 포함됩니다(자세한 내용은 아래 그림을 참조하세요. 오늘날의 다중 스레드 병렬 시스템은 이를 단순화했습니다).

본 문서에서 추상화된 엔진 아키텍처는 엔진 레이어와 시스템 레이어로 구분되며, 이들은 인터페이스 레이어를 통해 통신하고 상호 작용합니다. 아래 그림에 표시된 것처럼 각 계층에는 많은 개념과 하위 시스템이 포함됩니다.

엔진과 시스템 사이의 참조 및 상호 작용 관계는 다음과 같습니다. 전역 장면(Universal Scene)에는 형상, 그래픽 및 물리적 시스템 장면 표현이 있습니다. 각 장면 표현에는 전역 장면에 해당하는 객체 표현이 있습니다. 이러한 각 장면 대표는 작업 관리자가 예약하고 실행할 수 있는 많은 작업을 생성합니다.

다중 프로세서 플랫폼 프로그래밍의 2004 과제에서는 다양한 다중 처리 기술을 설명합니다. 이 기사에는 다음 개념이 포함됩니다.

  • 하드웨어 프로세서 레이아웃

이기종: 여러 개의 서로 다른 프로세서

  • 동형성: 동일한 프로세서의 배수

  • 소프트웨어 정리

비대칭: 다양한 코드 기반 실행

  • 대칭: 동일한 코드 실행

  • 작업 단위

적용: 해결해야 할 문제로, 제품 요구사항에 따라 정의됩니다.

  • 작업: 디자인 타임에 정의된 응용 프로그램에서의 프로그래머 작업의 제한된 표현입니다.

  • 스레드: 소프트웨어 구현 중에 사용되는 애플리케이션에서 작업을 구현하기 위한 메커니즘입니다.

소프트웨어를 설계할 때 프로그래밍 가능성이 중요하고, 여러 동적 애플리케이션을 실행할 때 복잡성이 증가하고, 소프트웨어의 툴링/가시성이 점점 더 어려워지고, 검증 및 반복성에 초점을 맞추며, 개발 환경 설계 및 테스트가 점점 더 복잡해집니다. 또한 재사용성은 솔루션의 이식성과 기능의 계층적 추상화가 요구되는 명백한 사실입니다.

기존의 단일 명령어 컨텍스트(유니프로세서)는 현재 방법을 사용하여 확장하여 명령어 수준 병렬성에서 더 많은 정보를 추출할 수 없으며, 프로세서 엔진은 개발자가 다중 명령어 컨텍스트를 사용하여 애플리케이션을 표현할 수 있도록 애플리케이션 프로그래머의 도움이 필요합니다.

높은 MHz에서 나오는 고성능은 데스크탑 및 임베디드의 열 또는 에너지 한계에 도달하고 있으며, Intel은 멀티 코어에 대한 P4 지원을 취소하고, ARM은 멀티 프로세서 코어를 출시하고 있으며, IBM은 프로세스 축소로 확장할 수 없다고 말합니다. 따라서 마이크로아키텍처는 진화해야 합니다.

CPU 코어 수와 에너지 소비 간의 관계를 보여주는 그래프입니다.

전형적인 이기종 비대칭 CPU 아키텍처는 다음과 같습니다.

T.I. OMAP 듀얼 코어 프로세서 아키텍처.

동형 비대칭 CPU 아키텍처.

AMP(비대칭 다중 처리)는 다양한 형태로 제공되는 이기종 프로세서와 동종 프로세서 간의 메시지 기반 상호 연결을 사용하여 프로그래머가 여러 애플리케이션을 동시에 실행할 수 있도록 하는 소프트웨어 모델입니다. 애플리케이션이 프로세서 전체에 걸쳐 정적으로 분할될 수 있을 때 효율적인 솔루션이 제공되어 작업의 영향을 다른 작업으로부터 격리할 수 있고 기존 코드를 MPSoC로 확장할 수 있는 간단한 메커니즘을 제공합니다.

AM 사용 사례. 왼쪽이 마스터 CPU, 오른쪽이 슬레이브 CPU입니다.

AMP의 과제는 다음과 같습니다. 프로그래머는 애플리케이션을 분할하고 여러 마이크로아키텍처에 걸쳐 프로세서에 하위 애플리케이션을 정적으로 할당해야 하는데, 이는 애플리케이션을 이해하지 못하면 매우 어렵습니다. 개방형 플랫폼에서 동적 작업 부하를 관리하는 복잡성으로 인해 이 모델이 무너지고 프로세서의 효율적인 활용이 어려워집니다. 동적 특성으로 인해 특정 프로세서에 과부하가 걸릴 수 있어 단일 작업 확장성을 제공하기가 어렵습니다. 모든 공급업체의 솔루션이 다르기 때문에 도구 지원이 단편화되고 변경이 필요한 경우 재작성/재설계가 필요합니다.

대칭형 멀티프로세싱(SMP)을 사용하면 프로그래머는 다중 명령어 컨텍스트 아키텍처(다양한 하드웨어 아키텍처에서 제공되는 공통 메모리 및 주변 장치 가정), 일관된 상호 연결이 있는 비대칭 MP, 일관된 캐시가 있는 대칭 MP, 공통 캐시가 있는 멀티스레드 단일 프로세서를 갖춘 소프트웨어 모델을 활용할 수 있습니다. SMP는 또한 표준을 높이기 위한 공통 모델을 제공하고, 프로그래머는 스레드를 사용하여 작업을 표현하며, 운영 체제는 프로세서에서 스레드를 예약합니다. 이는 단일 프로세서 설계 간에 이식성을 유지하는 차세대 주요 프로그래밍 모델로 간주됩니다.

멀티태스킹 응용 사례.

당시에는 몇 가지 구현 옵션이 있었습니다.

  • 단일 프로세서. 이벤트 기반, 협력적 시간 분할, 비동기 작업 파견, 선제적 시간 분할 멀티스레딩.

-여러 프로세서. 단일 프로세서와 마찬가지로 운영 체제도 CPU를 통해 스레드를 공유하여 컨텍스트 전환 비용을 줄이고 시스템 수준 응답을 향상시킬 수 있습니다.

  • 두 경우 모두 가장 쉬운 접근 방식은 애플리케이션 작업을 스레드에 간단히 매핑하여 기존 코드를 사용하여 구현할 수 있도록 하는 것입니다.

멀티스레딩과 관련된 메커니즘은 다음과 같습니다.

  • Fork-Exec: 요청 시 스레드를 생성합니다. 작업에는 명확한 시작 및 종료 조건이 있고, 작업은 스레드 생성/종료 비용을 숨길 만큼 충분히 오래 지속되며, 기존 코드를 멀티태스킹 애플리케이션으로 마이그레이션하는 데 도움이 되며, 각 작업에는 여러 동기화 지점이 있을 수 있으며, 잘못된 파티셔닝은 성능을 저하시킬 수 있습니다.

  • 작업 풀: 작업 풀에 작업을 넘겨줍니다. 애플리케이션에는 잘 정의된 "작업 단위", 작업을 기다리는 작업 풀이 있고 작업 동기화는 작업 단위의 분할/병합으로 제한되는 것이 가장 좋으며 작업 항목이 순서에 종속되지 않도록 해야 합니다.

cpp
// Fork-Exec코드
main() 
{
    while( !Shutdown ) 
    {
        work = WaitForWork();
        CreateThread(WorkerTask, work);
    }
}
WorkerTask(work) 
{
    DoWork(work);
}

// Worker Pooling코드
main() 
{
    For(i=0; i< numCPU * 2; i++) 
    {
        CreatThread(WorkerTask, workQueue);
    }
    
    While( !Shutdown ) 
    {
        work = WaitForWork();
        PostWork(workQueue, work);
    }
}
WorkerTask(workQueue) 
{
    while( !Shutdown ) 
    {
        work = WaitforWork(workQueue);
        DoWork(work);
    }
}

멀티태스킹은 단일 작업에 단일 프로세서보다 더 많은 성능이 필요할 때까지 잘 작동합니다. 작업이 여러 하위 작업으로 쉽게 표현되는 경우 이는 중요한 문제가 아닙니다. 하위 작업이 복잡할 수 있는 경우는 다음과 같습니다. 특히 코드에 설정된 경우 단일 선형 알고리즘으로 표현됩니다. 알고리즘은 일련의 상호 의존적인 작업입니다. 다행스럽게도 이러한 문제는 소프트웨어 코드 블록 또는 루프 수준에서 병렬성을 찾고, 루프 반복을 프로세서 간에 분할하고, 별도의 코드 조각을 프로세서에 배치함으로써 단순화될 수 있습니다.

대칭형 MP의 과제는 다음과 같습니다.

  • 한 작업의 영향을 다른 작업과 분리하기 어렵고, 모든 작업이 동일한 프로세서를 공유하며, OS API는 일반적으로 선호도와 작업 수준 우선순위를 제공합니다.

  • 프로그래머는 너무 많은 동기화 지점을 요구하여 프로세서 간에 데이터를 지속적으로 마이그레이션해야 하므로 범용 메모리 시스템을 남용하지 않도록 주의해야 합니다.

  • 하드웨어는 메모리 일관성 보장, 프로세서 간 동기화와 관련하여 처리 하위 시스템의 병목 현상을 해결해야 합니다.

ARM MPCore 하이브리드 멀티프로세서.

간단히 말해서, 임베디드 개방형 플랫폼 MPSoC는 데스크톱 마이크로아키텍처를 단순히 복사할 수 없고 전통적인 일관성 비용(느린 시스템 버스 및 통신 SoC 구성 요소)을 감당할 수 없으며 최고 성능보다 낮은 전력 소비를 우선시해야 합니다. 솔루션은 개방형 플랫폼 개발 팀에 의해 프로그래밍되어야 하며, 기존 코드 기반의 마이그레이션을 허용하고, "대중 시장" 모델을 사용하고, 높은 수준의 처리 효율성도 제공해야 합니다.

PC 게임의 실제 멀티스레딩 사례 연구에서도 멀티스레드 병렬 기술에 대해 언급합니다. 이 기사에서는 하이퍼스레딩과 일반 스레드 간의 비교를 언급합니다.

하이퍼스레딩에는 리소스를 동시에 처리할 수 있는 두 개의 논리적 스레드가 있어 리소스 활용도와 처리량이 향상됩니다.

멀티 프로세서(왼쪽)와 하이퍼스레딩(오른쪽) 하드웨어 아키텍처의 비교 다이어그램.

게임을 스레드하기가 왜 그렇게 어려운가요? 기술적인 측면과 상업적인 측면 모두에서 답변이 가능합니다. 기술적으로 순차 파이프라인 모델은 단계 간에 단일 데이터 세트를 공유하고, 고도로 최적화된 조밀한 코드는 HT의 이점을 최소화하며, 스레딩에는 상당한 높은 수준의 설계 변경이 필요한 경우가 많습니다. 비즈니스 측면에서는 멀티스레드 프로그래밍에 대한 경험이 거의 없고 HT를 지원하는 시스템(SSE 등)의 시장 점유율이 제한적이며 소비자는 HT를 인식하지 못합니다.

하지만 게임은 스레드되어야 하고 기술적인 측면과 상업적인 측면 모두에서 여전히 대답할 수 있습니다. 기술적으로 병렬성은 CPU 아키텍처의 미래입니다 -> 확장하기 쉽고(HT, 멀티 코어 등), 그래픽 카드/드라이버를 기다리는 동안 다른 작업을 수행하고, 우수한 MT 설계 확장이 가능하며, 반복적인 재작성을 방지합니다. 비즈니스 측면에서는 경쟁 환경에서 두각을 나타내며 모든 PC 플랫폼은 멀티스레딩을 지원하고 병렬 프로그래밍 교육은 여러 플랫폼(PC, 콘솔, 서버 등)에서 성과를 거두며 MT는 더 확장할 수 있으며 -> 제품 수명을 연장할 수 있습니다.

게임 파이프라인 모델, 게임 업데이트 및 렌더링에는 타이밍 종속성과 데이터 종속성이 있습니다.

멀티스레딩 전략에는 두 가지가 있습니다.

  • 작업이 병렬화됩니다. 분리된 작업을 동시에 수행합니다. 전체 3D 그래픽 파이프라인을 멀티스레드합니다. 예를 들어 스레드 1은 N 프레임을 처리하고 스레드 2는 N+1 프레임을 처리합니다. GPU 집약적인 게임에 적합하며 종속성으로 인해 구현하기가 더 어렵지만 불가능하지는 않습니다.

  • 데이터 병렬성. 분리된 데이터를 동시에 처리합니다. 오디오 처리, 네트워킹(VoIP 포함), 파티클 시스템 및 기타 그래픽 효과, 물리, 인공 지능, 콘텐츠(추측) 로드 및 압축 풀기와 같은 작업을 작업자 스레드에서 수행할 수 있습니다. 기하학, 질감, 환경 등과 같은 멀티 스레드 절차적 콘텐츠 생성. CPU 집약적인 게임에 적합하고 구현이 쉽습니다.

아래 그림은 실제 모든 스레드의 생명주기를 관리하기 위해 논문에서 제안한 스레드 관리자이다.

다음 그림은 기사에 제공된 프로그래밍 방식 스카이박스의 다중 스레드 구현 사례입니다.

또한 이 기사에서는 비동기 파일 로딩, 날씨 시스템 렌더링 및 파티클 시스템 렌더링의 멀티스레딩에 대해서도 설명합니다.

대규모 시스템에서 확장 가능한 멀티 코어 성능 활용에서는 Intel CPU 및 멀티 스레드 아키텍처 분석, 스레드 사용, 디버깅 및 성능 분석의 개발 동향을 설명합니다.

인텔 스레드 기술의 네 가지 측면.

이 기사에서는 스레드 간의 데이터 경쟁과 교착 상태에 대해 설명하고 어셈블리 수준 분석을 포함한 힘과 자세한 솔루션 단계를 제공합니다.

인텔 CPU 데이터 경합에 대한 어셈블리 수준 분석.

교착상태 예시.

당시 인텔 스레드 분석 도구의 기술 모델은 다음과 같습니다.

또한 이 기사에서는 다중 스레드 잠금, 단일 스레드 및 다중 스레드 안전 읽기-쓰기 잠금의 최적화와 직렬 영역에서 잠금을 비활성화하기 위한 코드 예제도 제공합니다(아래 그림).

차세대 콘솔을 위한 고성능 물리 솔버 설계는 현재 단위 프로세서 모델과 물리 시뮬레이션을 병렬로 처리하는 방법을 보여줍니다. 다음 두 그림은 기사에서 언급된 스트리밍 통합 프로세스와 동시 DMA 및 전송 시간을 숨기기 위한 처리를 보여줍니다.

다음 두 그림은 강체 파이프라인 병렬화의 비교 그림입니다.

대역폭을 줄이고 운영 효율성을 향상시키기 위해 작업을 일괄 처리할 수 있습니다.

다음 그림은 여러 SPU의 병렬 다이어그램과 성능 비교입니다.

또한 이 문서에서는 높은 수준 및 낮은 수준의 병렬화도 제공합니다. 다음 그림은 낮은 수준의 병렬화를 보여줍니다.

2007년에는 CPU와 GPU의 속도가 점점 빨라져 CPU는 연평균 1.4배, GPU는 연평균 1.7배(픽셀)에서 2.3배(버텍스)씩 증가했다. 예를 들어, 3.0GHz Intel Core2 Duo(Woodcrest Xeon 5160)의 컴퓨팅 피크는 48GFLOPS, 메모리 대역폭은 21GB/s인 반면, NVIDIA GeForce 8800 GTX의 컴퓨팅 피크는 330GFLOPS, 메모리 대역폭은 55.2GB/s입니다.

위: 2001년부터 2007년까지 CPU 및 GPU 속도 개선; 하단: GeForce 8800 GTX의 통합 셰이더 아키텍처 다이어그램.

속도가 증가하면 애플리케이션 계층에서 보다 일반적인 계산을 수행할 수 있는 더 많은 기회가 제공됩니다. 그래픽 하드웨어의 범용 컴퓨팅(General-Purpose Computation on Graphics Hardware) 문서가 대표적인 예입니다. GPU용 일반 컴퓨팅에는 대규모 행렬/벡터 연산(BLAS), 단백질 폴딩(분자 역학), 재무 모델링, FFT(SETI, 신호 처리), 레이 트레이싱, 물리 시뮬레이션(천, 유체, 충돌...), 시퀀스 매칭(숨겨진 마르코프 모델), 음성/이미지 인식(숨겨진 마르코프 모델, 신경망), 데이터베이스, 정렬/검색, 의료 영상(이미지 분할, 처리) 등이 포함됩니다.

기사에서 다루는 내용은 GPU 아키텍처, 데이터 병렬 알고리즘, 검색 및 정렬, GPGPU 언어, CUDA, CTM, 성능 분석, GPGPU 래스터라이제이션, 레이 트레이싱, 기하학적 계산, 물리적 시뮬레이션 등을 다루고 있습니다. 이는 포괄적이라고 할 수 있으며 GPU의 기술적 내부 이야기와 애플리케이션 세부 사항에 대한 심층 분석을 제공합니다.

당시 GPGPU는 계산 집약적이었고 대규모 병렬 처리에 적합했습니다. 독립적인 작업을 위해 설계된 그래픽 파이프라인이 있었고, 긴 지연을 견딜 수 있었고, 정확성 부족을 견딜 수 있는 깊은 순방향 피드백 파이프라인이 있었으며, 병렬, 산술 집약적인 스트리밍 메모리 문제에 능숙했습니다. 새로운 기능은 GPGPU에 잘 매핑되어 있으며 새로운 API의 컴퓨팅 장치에 직접 액세스할 수 있습니다. 새로운 통합 셰이더는 3D 렌더링 파이프라인을 단순화하고 컴퓨팅 장치의 활용도를 향상시킬 수 있습니다.

위: 작업 병렬 GPU 아키텍처; 하단: 통합 셰이더 GPU 아키텍처.

위: Geforce 6800의 3D 렌더링 파이프라인, 비통합 셰이더 아키텍처; 하단: Geforce 8800의 통합 셰이더 아키텍처.

GPU는 기존의 고정 기능 기능과 새롭고 유연하며 프로그래밍 가능한 기능이 혼합되어 있습니다. 고정 기능은 전용 하드웨어에 의해 가속화되는 알려진 액세스 패턴, 동작입니다. 프로그래밍 가능은 알려지지 않은 액세스 모드이며 다양성이 뛰어납니다. 그리고 GPU 메모리 모델은 두 가지 모두를 수용해야 하므로 전용 기능이 풍부하고 유연성이 제한되어 성능이 향상될 수 있습니다.

2007년 CPU 및 GPU용 메모리 아키텍처 모델.

GPU 데이터 구조 분류:

  • 조밀한 배열. 주소 변환: n 차원을 m 차원으로.

  • 희소 배열. 정적 희소 배열(예: 희소 행렬), 동적 희소 배열(예: 페이지 테이블)

  • 적응형 데이터 구조. 정적 적응형 데이터 구조(예: kd 트리), 동적 적응형 데이터 구조(예: 적응형 다중 해상도 그리드)

  • 인덱싱할 수 없는 구조(예: 스택)

GPU는 CPU보다 메모리 액세스가 더 제한됩니다. 계산 전에 메모리를 할당/해제하기만 하며 메모리와 CPU 간에 명시적인 전송이 이루어집니다. GPU는 CPU에 의해 제어되며 전송을 시작하거나 디스크에 액세스하는 등의 작업을 시작할 수 없습니다. 따라서 GPU는 데이터 구조에 더 잘 액세스하고 CPU는 데이터 구조를 구축하는 데 더 좋습니다. CPU-GPU 대역폭이 증가하면 "자연스러운" 프로세서에서 데이터 구조 작업을 수행하는 것을 고려하십시오.

GPU는 레지스터 읽기 및 쓰기(조각/스레드별), 로컬 메모리(스레드 간에 공유), 전역 메모리(계산 중 읽기만, 계산 종료 시에만 쓰기, 즉 미리 계산된 주소) 및 전역 메모리(일반 분산/수집 허용, 즉 읽기/쓰기 허용, 아래 그림 참조)를 포함하여 계산(커널) 중에 메모리 액세스가 제한됩니다.

요소 병합, 스트리밍 데이터 압축, 검색, 정렬 등과 같은 일반 컴퓨팅에 대한 일반적인 사용 사례가 많이 있습니다.

GPGPU 일반 컴퓨팅 사용 사례. 위에서 아래로 2D 데이터 병합, 스트리밍 데이터 압축, 균형 트리 스캐닝, 이중 병합 정렬이 있습니다.

또한 GPGPU는 그룹 계산에도 사용할 수 있습니다.

GPU 병렬 정렬의 파이프라인과 성능 비교는 다음과 같습니다.

GPU 캐시 모델은 메모리 대기 시간을 더 잘 숨기기 위해 작은 데이터 캐시를 갖추고 있으며 공급업체는 GPU의 과학 컴퓨팅에 중요한 캐시 정보를 공개하지 않습니다. 정렬, FFT 및 SGEMM 성능을 향상시키기 위해 캐시 매개변수(블록 및 캐시 크기)를 결정하도록 간단한 모델을 설계할 수 있습니다. 캐시 효율적인 알고리즘 성능은 아래 그림에 나와 있습니다.

GPU 캐시 효율성 알고리즘 성능 범례. 관측 시간은 이론적인 피크 값보다 약간 높습니다.

다음 그림은 CPU, GPGPU, CUDA의 캐시 모델을 비교한 것입니다.

스탠포드 대학에서 개발한 오픈 소스 도구 GPUBench는 대기 시간을 숨길 수 있는지, 액세스 모드가 대기 시간에 영향을 미치는지 여부 등 GPU 메모리 액세스의 대기 시간 세부 정보를 이해할 수 있습니다. 구체적인 측정 방법은 서로 다른 수의 텍스처 가져오기 및 서로 다른 액세스 패턴(캐시 적중: 매번 동일한 텍셀을 가져옴, 순서: 가져올 때마다 주소가 1씩 증가, 무작위: 무작위 텍스처의 관련 조회)을 시도하여 셰이더의 ALU 작업을 늘리는 것입니다. 최적화를 피하기 위해서는 ALU 작업에 의존해야 합니다. 다음은 여러 ATI 및 NVidia GPU에서 다양한 액세스 모드의 데이터 수집 소비입니다.

Early-Z 테스트의 경우 Z 버퍼 및 비교 기능을 설정하여 계산을 마스크하고, 블록 크기를 변경하고, 그릴 픽셀 수를 다르게 설정하여 블록의 일관성을 변경합니다. NVIDIA 7900 GTX의 테스트 결과는 다음과 같습니다.

위의 내용을 보면 랜덤 액세스가 가장 효율적이지 않고, 4x4 블록에서 일관된 액세스가 가장 효율적이라는 것을 알 수 있습니다.

셰이더 브랜치의 경우 테스트 문을 사용합니다. if{ do a little }; else { LOTS of math}, 블록 일관성을 변경하고, 블록 크기를 변경하고, 무거운 수학 분기를 수행하기 위해 다른 픽셀 수를 갖습니다. 테스트 결과는 아래와 같습니다.

완전히 일관된 액세스가 가장 좋은 성능을 가지며, 랜덤 액세스가 가장 나쁜 성능을 갖는 것을 알 수 있습니다.

일반 계산은 동적 합산 면적 테이블에도 적용됩니다. 동적 총 면적 테이블을 위한 알고리즘 개요: 동적 텍스처, 반사 맵 등을 생성하고, 데이터 병렬 GPGPU 계산을 사용하여 SAT를 생성하고, 기존 렌더링 프로세스에서 SAT 데이터 구조를 사용합니다.

SAT는 광택 반사(흐림은 반사 물체와의 거리에 따라 다름) 및 뎁스 오브 필드(DoF)(흐림은 눈으로부터의 거리에 따라 다름)에 사용할 수 있습니다.

또한 해상도 일치를 위해 그림자 맵 계산을 사용할 수 있습니다.

Saints Row Scheduler는 다중 스레드 스케줄러 개념, 아키텍처 및 성능과 같은 주제를 탐구합니다. Wen Zhong은 스케줄러를 기능 호출이나 스레드 예약과 유사하게 흐름을 제어하는 ​​메커니즘으로 정의하며, 여러 스레드에 걸쳐 독립적으로 예약 가능한 엔터티 또는 "작업"의 예약을 관리하는 데 사용됩니다. 효과적인 디자인은 많지만 일반적으로 플랫폼/하드웨어에 따라 다릅니다. 작업은 시퀀스나 데이터 없이 독립적으로 예약 가능한 엔터티입니다. 이는 다른 "준비된" 작업에 의존합니다. 일반적으로 비차단형이며 I/O, D3D 장치 또는 기타 비동기 이벤트를 기다릴 필요가 없습니다. 이는 함수 포인터/데이터 블록 쌍과 동일합니다. 애플리케이션의 처리를 작업으로 나누는 것은 애플리케이션을 다중 프로세서 지원으로 만드는 데 큰 부분을 차지합니다.

스케줄러 설계에 대한 지침은 최대한 단순하고, 최대한 직관적이며, 애플리케이션별로 구성 가능하고, 고성능/낮은 오버헤드, 정밀한 작업 세분화가 가능하고, 선점형 이벤트를 우아하게 처리할 수 있는 메커니즘입니다. 일반적인 철학은 와이어를 계속 매달고, 빌딩 블록을 생성 및 사용하고, 고급 언어 구성을 피하는 것입니다.

Saints Row는 단일 스레드 애플리케이션으로 시작되었으며 나중에 6개의 하드웨어 스레드를 갖게 되었습니다. 초기 스레드 레이아웃: 표준 시뮬레이션/렌더링 분할, 프레임 시간 변형, ~20~50ms, 일반적으로 ~33ms, 오디오 드라이버는 5ms마다 ~2.3ms를 사용하고, 스트리밍 활동 시 전체 스레드가 사용되며, 오디오는 ~30ms마다 실행됩니다.

그러나 이 설계 아키텍처에는 다음과 같은 많은 문제가 있습니다.

  • 메인 스레드 중 하나에서 많은 수의 작업을 예약하면 작업 스레드가 독점될 수 있으며 결과적으로 "선착순" 순서가 적용됩니다.

  • 일부 스레드는 대역폭이 감소했습니다. DSP/오디오 드라이버가 있는 스레드 4는 약 65%에 불과하고 스레드 1은 스트리밍 전용으로 사용할 수 있으며 오디오와 스트리밍이 모두 선점됩니다.

  • 결합 및 프리패스 렌더링은 시간이 오래 걸리고 지속적인 작업입니다.

  • Havok 문제는 자체 스레딩 유틸리티를 사용하여 스레드 메모리를 각 스레드에 대해 종종 직렬로 할당해야 한다는 것입니다.

Saints Row는 스케줄러 디자인을 개선하고 선착순 순서를 채택했습니다. FIFO 작업 Q를 사용하면 "자연스러운" 작업 처리 순서가 생성됩니다. 문제는 메인 스레드에서 대량의 작업을 예약하면 작업 스레드가 차단될 수 있으며 작업을 세밀하게 세분화하고 작업에 우선순위를 추가하는 것이 도움이 되지 않는다는 것입니다. 해결책은 추가 FIFO 작업 Q 및 "작업 바이어스", 작업 스레드 바이어스를 추가하여 시뮬레이션 유형 또는 렌더링 유형 작업을 제공하고 동적으로 구성할 수 있는 것입니다. 바이어스 알고리즘 및 스레드/작업 유형을 동적으로 변경하여 각 메인 스레드에 작업자 스레드 시간이 있는지 확인합니다.

스레드 처리의 특별한 경우:

  • Sim(시뮬레이션) 작업과 렌더 작업 간에 작업 스레드를 분할합니다.

심은 스트리밍하지 않을 때 0, 2, 1을 얻습니다.

  • Havok은 0, 1, 2에서만 실행됩니다.

  • 렌더는 오디오 처리 후 3, 5 및 나머지 4를 얻습니다.

  • 또한 프레임의 렌더링 집중 부분에서 1과 2를 얻습니다.

  • 콤보 및 프리패스:

기본 렌더 스레드에서 사전 패스를 실행하고 결합된 패스 작업을 스레드 3에 고정합니다.

  • 최우선 작업입니다.

  • 스레드 3은 다른 처리 스레드에서 무료로 사용할 수 있습니다.

  • 하복:

자체 스레딩 유틸리티가 함께 제공됩니다. 동적 제어가 없으면 Havok 처리를 수행하는 각 스레드에는 Havok 스레드 메모리가 필요하며 처리의 약 절반이 직렬로 수행됩니다.

  • 해결책:

세 개의 스레드를 Havok 전용으로 지정하고 이러한 스레드에만 스레드 메모리를 할당합니다.

  • 스케줄러에서 Havok 시간 단계를 호출합니다. Havok 스레딩 유틸리티와 동일한 성능으로 스레딩 제어를 허용하고 프레임별로 스레드를 추가하거나 제거합니다.

  • 직렬부품을 직접 분해하고 분해해 보세요.

최종 스레드 레이아웃은 Havok이 처음 3개 스레드로 이동하고 스레드 3의 결합 패스로 고정되어 대부분의 프레임 동안 렌더링 작업을 허용하고 집중적인 시뮬레이션 처리 중에 시뮬레이션 작업을 허용하며 실제 시뮬레이션 창은 더 복잡하며 메인 스레드의 유휴 시간 동안 작업을 예약할 수 있습니다.

응용 프로그램의 경우 작업을 분할할 때 최적의 작업 크기는 스케줄러 오버헤드의 함수이며 "5% 이하의 오버헤드"와 같은 "허용 가능한" 기준을 설정한 다음 각 작업의 예약 시간을 측정합니다. Saints Row의 경우 최적의 크기는 약 250~500마이크로초이므로 작업이 더 오래 걸릴 경우 권장되지 않습니다.

PIX의 Saints Row 타임라인에 따르면 CPU 사용량은 약 90%입니다.

Saints Row 스케줄러 내부의 추가 기술 세부정보:

  • 중요한 섹션이나 스핀 잠금을 통해 모든 보호가 제공됩니다.

  • 각 하드웨어 스레드에 6개의 작업 스레드가 있습니다.

  • 작업 Q에 작업을 삽입하면 유휴 일치 작업 스레드가 활성화됩니다. 유연성을 위해 세마포어 대신 이벤트를 사용합니다.

  • 작업 생성 스레드가 정지되어 이벤트 예약을 기다릴 수 있습니다. 일시 중지 스레드는 하드웨어 스레드에서 실행할 수 있는 작업 유형을 지정합니다.

  • 완료되면 작업이 이벤트를 트리거하거나(이벤트 트리거) 더 많은 작업을 예약합니다(스케줄링 트리거).

성능 측면에서 단일 작업 큐인 경우 작업 블록을 작업 큐로 하나씩 이동하는 것이 더 효율적이고 유연합니다. 작업 큐는 스레드 경합으로 인한 오버헤드의 중요한 원인입니다. 중요 섹션 보호가 사용되는 경우 대기열이 차단될 수 있습니다. 잠금 없는 구조(스택, 큐)의 성능은 중요 섹션 보호 구조, 후입선출(SLists, GPGems 6 스택), 매우 평탄한 선입선출(FFO), Michael의 부동 노드, Fober의 재삽입보다 훨씬 우수합니다. 주의해서 사용하세요.

드래그 킥킹 앤 스크리밍: 소스 멀티코어는 멀티코어에서 소스 엔진이 직면하는 결정과 멀티코어 활용 방법을 소개하고 알고리즘과 패러다임을 제공합니다.

소스 엔진 멀티코어의 목표는 멀티코어를 Valve의 비즈니스에 통합하고, 재컴파일 없이 코어를 확장하고, 게임 프레임 속도를 개선하고, 새로운 게임 로직에 코어를 적용하는 것입니다. 문제는 게임에 최대 CPU 활용도가 필요하고, 게임이 본질적으로 직렬이며, 수십 년간의 단일 스레드 최적화 경험이 있었고, 단일 스레드에 대해 수백만 줄의 코드가 작성되었다는 점이었습니다. 멀티 코어 전략에는 스레딩 모델과 스레딩 프레임워크가 포함됩니다. 스레딩 모델에는 미세한 스레드, 거친 스레드 및 혼합 스레드의 세 가지 유형이 있습니다.

스레드 안전성 측면에서 소스 엔진은 효율적인 스레드 안전성, 동기화 없음("대기 없음")을 채택하고 각 스레드에는 작업을 수행하는 데 필요한 모든 데이터의 개인 복사본이 있습니다. 스레드는 독립적인 문제를 처리하고 전역 변수를 스레드 전용 데이터로 대체하며 파이프로 리디렉션됩니다.

소스 엔진의 잠금 없는 데이터 스레드 안전 모델: 공간 분할. 위 그림은 클라이언트와 서버가 동일한 데이터를 공유하는 모습을 보여주고, 아래 그림은 잠금 없는 스레드 안전성이라는 목표를 달성하기 위한 공간 분할을 보여줍니다.

또한 소스 엔진은 읽기/쓰기 잠금을 사용하는 기호 테이블, 대기 중인 함수 호출을 사용하는 분리와 같은 더 나은 동기화 도구 및 기술을 사용하여 데이터 액세스를 분석합니다. Source 엔진은 또한 하이브리드 스레딩 기술을 사용하여 일부 시스템이 코어(예: 사운드)에서 실행되고 일부 시스템이 대략적인 방식으로 내부적으로 분할되는 등 작업에 적합한 방법을 사용합니다. 코어 전반에 걸쳐 비용이 많이 드는 반복을 세밀하게 분할하여 코어가 유휴 상태일 때 일부 작업을 대기열에 추가하여 바쁜 상태를 유지합니다. 코어 활용도를 극대화하려면 강력한 기술이 필요합니다.

Source의 렌더링 아키텍처와 프로세스는 다음과 같습니다.

문제는 뷰별 장면이 임의의 객체 유형과 임의의 코드 실행이 시뮬레이션과 렌더링(일명 게으른 계산 최적화) 사이에 인터리브될 기회를 제한한다는 것입니다. 변환(예: 골격 애니메이션)을 반복하고, 지연 계산 트리거를 병렬화하고, 골격 설정을 뷰당 단일 패스로 재구성하고, 모든 뷰를 단일 패스로 재구성하고, 기타 CPU 집약적 단계에 대해 동일한 패턴을 적용합니다. 수정된 파이프라인은 다음과 같습니다.

  • 여러 장면(예: 세계와 물에 반사된 세계)에 대해 병렬로 장면 렌더링 목록을 구성합니다.

  • 오버레이 그래픽 시뮬레이션.

  • 모든 장면의 모든 캐릭터에 대한 캐릭터 골격 변환을 병렬로 계산합니다.

  • 여러 스레드를 병렬로 그릴 수 있습니다.

  • 다른 코어에서 그리기 작업을 직렬화합니다.

하이브리드 스레딩을 구현하면 프로그래머는 스레딩 문제 대신 게임 개발 문제를 해결할 수 있어 모든 프로그래머가 코어를 활용할 수 있습니다. 대부분의 프로그래머에게 운영 체제는 너무 낮은 수준이고 컴파일러 확장(OpenMP)은 너무 불투명하여 맞춤형 도구, 즉 올바른 추상화만 필요합니다. 맞춤형 도구에는 게임 스레딩 인프라와 맞춤형 작업 관리 시스템이 포함되어 있어 프로그래머는 코어를 바쁘게 유지하는 데 집중할 수 있습니다. 스레드 풀: N 코어용 N-1 스레드, 혼합 스레드 지원: 함수 스레드, 배열 병렬 처리, 대기열 추가 및 즉시 실행. 맞춤형 도구의 목표는 컴파일러 생성 펑터, 함수 및 데이터 패키징을 위한 템플릿 사용, 유사하게 보이도록 사이트 호출, 직렬과 같은 대기열 함수 호출, 시간 절약, 오류 감소 및 실험 장려와 같이 시스템을 사용하기 쉽게 만들고 덜 복잡하게 만드는 것입니다. 다음은 소스 엔진의 여러 다중 스레드 호출 방법입니다.

cpp
// 회 까지 개
if ( !IsEngineThreaded() )
    _Host_RunFrame_Server( numticks );
else
    ThreadExecute( _Host_RunFrame_Server, numticks );

// 병렬 루프
void ProcessPSystem( CParticleEffect *pEffect );

ParallelProcess( particlesToSimulate.Base(), particlesToSimulate.Count(), ProcessPSystem );

// 힙 ,
BeginExecuteParallel();
    ExecuteParallel( g_pParticleSystem, &CParticleSystem::Update, time );
    ExecuteParallel( &UpdateRopes, time );
EndExecuteParallel();

경합과 관련하여 공유 리소스에 대한 경합을 제거할 수 없으면 어떻게 되나요? 예를 들어 할당자는 여러 개의 고정 크기 블록 풀을 많이 사용하고 각 풀에는 사용자 정의 스핀록 뮤텍스가 있으며 뮤텍스는 크기를 제한하고 스레드별 할당자를 사용하지 않으려고 합니다. 잠금 없는 알고리즘을 사용해 볼 수 있습니다. 스레드는 일정이나 상태에 관계없이 시스템을 차단할 수 없으며 모든 서비스와 데이터 구조는 원자성 쓰기 지침(비교 및 교체)에 의존합니다.

cpp
// c++
bool CompareAndSwap(int *pDest, int newValue, int oldValue)
{
    Lock( pDest );
    bool success = false;
    if ( *pDest == oldValue )
    {
        *pDest = newValue;
        success = true;
    }
    Unlock( pDest );
    return success;
}

// asm
bool CompareAndSwap(int *pDest, int newValue, int oldValue)
{
    __asm
    {
        mov eax,oldValue
        mov ecx,pDest
        mov edx,newValue
        lock cmpxchg [ecx],edx
        mov eax,0
        setz al
    }
}

할당자에서 잠금 없는 알고리즘을 사용하여 뮤텍스와 기존 풀별 사용 가능 목록을 풀별 잠금 없는 목록으로 바꾸려면 Windows API 또는 XDK SList와 같은 시스템 API를 사용해야 합니다. 다음은 잠금 없는 목록을 삽입하기 위한 샘플 코드입니다.

cpp
void Push( SListNode_t *pNode )
{
    SListHead_t oldHead, newHead;
    for (;;)
    {
        oldHead.value64 = m_Head.value64;
        newHead.value.iDepth = oldHead.value.iDepth + 1;
        newHead.value.iSequence = oldHead.value.iSequence + 1;
        newHead.value.Next = pNode;
        pNode->pNext = oldHead.value.pNext;
        
        if ( ThreadInterlockedAssignIf64( &m_Head.value64, newHead.value64, oldHead.value64 ) )
        {
            return;
        }
    }
}

잠금 없는 목록은 각 스레드에 컨텍스트를 제공하는 것이 비실용적일 때 컨텍스트 구조 풀을 유지하는 데 특히 유용하며, 나중에 처리하기 위해 병렬 프로세스의 결과를 효과적으로 수집하고, Push()를 사용하여 작업할 데이터 목록을 작성한 다음 Detach()("Flush"라고도 함)를 사용하여 단일 작업으로 다른 스레드에서 데이터를 가져옵니다. Source의 lock-free 알고리즘의 세부 내용은 다음과 같습니다.

  • 스레드 풀 작업 할당 대기열. 하나의 생산자와 하나의 소비자를 위해 설계된 Half Life 2 비동기 I/O 큐에서 파생되었으며, 뮤텍스 잠금이 있는 간단한 우선 순위 큐, 모든 우선 순위, 모든 스레드에 대한 하나의 큐입니다.

  • 솔루션: 잠금 없는 큐, 우선순위당 하나의 큐, 공유 큐 외에 코어당 하나의 큐를 사용하여 고정 우선순위 인터페이스를 재작업하고, 원자적 작업을 사용하여 "티켓"을 얻습니다. 실제 수행된 작업은 다를 수 있습니다.

  • 잠금은 안정적인 현실을 허용합니다.

  • 잠금이 없으면 현실 변경 지침이 허용되지 않습니다.

  • 시스템의 일부가 안정적이라는 것을 이해하기 위해 고정보다는 추론을 사용합니다.

  • 기다리지 않는 것이 항상 좋습니다.

소스 개발자는 또한 업계에 강력한 도구와 신기술을 구축하거나 획득하고, 작업과 데이터를 대기 코드 안팎으로 이동하기 위한 잠금 없는 메커니즘을 채택하고, 여러 코어에 걸쳐 기능을 고려할 수 있도록 준비하고, 액세스 가능한 솔루션을 사용하여 시스템 프로그래머뿐만 아니라 모든 프로그래머에게 권한을 부여하고, 게임 문제에 따라 더 높은 수준의 스레드를 지원할 것을 요구했습니다.

뱀! Seamless Living World에서는 Stackless라는 Python 라이브러리에서 멀티스레딩을 시뮬레이션하기 위한 아키텍처와 기술을 설명합니다. 스택리스는 최소한의 스레드 관리, 초고속 컨텍스트 전환, 강력한 예외 처리, 소규모 작업 관리 등을 통해 1000개의 작은 작업을 제공합니다. 기술 아키텍처는 다음과 같습니다.

독립적인 스케줄러와 이벤트 필터가 있습니다.

인기 PC 게임 및 엔진의 스레딩 성공에서는 게임 엔진의 실제 멀티스레딩 사례에 대해 이야기하고 몇 가지 멀티스레딩 모델을 제안했습니다.

작업 대기열은 데이터를 지역화한 상태로 유지하고 대역폭을 줄입니다. 데이터를 한 곳에 보관하고 작업을 대기열에 추가하세요. 완벽한 세상에서 읽기는 즉각적이고 무료이며 일관됩니다.

당시 게임이 멀티코어가 되면 다음 사항에 주의해야 합니다.

  • 스레드 성능. 프레임 속도, 로딩 시간, 타임 슬라이싱보다 프레임 속도를 매끄럽게 만드는 것이 더 쉬움, 매우 일반적

  • 스레딩 기능. 가능한 많은 CPU 집약적 효과.

  • 스레딩 미들웨어를 사용하세요. 모든 작업을 수행하고 확장하는 것이 일반적이지만 함께 작동하는 방식은 알 수 없습니다.

THQ/Gas Powered Games Supreme Commander 및 Supreme Commander: Forged Alliance에서도 유사한 멀티스레딩 모델을 보여줍니다.

위의 아키텍처는 로드에 따라 잘 확장되며, 렌더링 로드는 일반적으로 프레임 속도를 유지하기 위해 지배적이며 다시 렌더링되며, 시뮬레이션이 많은 맵은 시뮬레이션이 지배하려고 합니다. 다음 그림은 시뮬레이션 스레드와 렌더링 스레드의 실행 프로세스 다이어그램입니다.

메모리 관리자는 추가 향상을 제공할 수 있습니다. 스레드 게임에서 메모리를 주의 깊게 관리하지 않으면 메모리 사용이 캐시를 손상시키고 메모리 할당/해제 속도가 느려질 수 있습니다. 작은 할당을 많이 수행하는 등 메모리 관리에 문제가 의심되는 경우 메모리 관리자를 쉽게 전환할 수 있는 코드를 빌드하세요. 사용자 정의 메모리 관리자는 기본 malloc/free보다 우수하며 일부 디버깅 문제가 발생할 수 있습니다.

게임 엔진을 멀티스레딩할 때는 처음부터 스레드 아키텍처를 설계하고, 이전 단일 스레드 코드를 멀티스레딩하고, 스레드를 최대한 분리해 보세요. 다중 스레드 단일 스레드 코드를 두려워하지 마십시오.

Hellgate: London의 EA 파트너인 Namco Bandai는 작업 병렬화를 위한 몇 가지 기술과 사례를 언급했습니다. 첫 번째 시도는 포크 ​​앤 조인(fork-and-join) 접근 방식이었습니다. 여기서는 메인 스레드가 두 코어 모두에서 작업의 절반을 수행하고 전용 스레드가 나머지 절반을 수행했습니다. 데이터 복사가 없었고 메모리가 일관되었습니다.

두 번째 시도: 비동기 업데이트, 동기 렌더링. 비동기 업데이트는 메인 스레드를 차단하지 않으며, 메모리는 더 이상 일관되지 않으며, 새 위치를 사용할 수 있게 되면(아마도 모든 프레임에서) 날씨 입자가 다시 그려집니다. 더 까다롭지만 승자처럼 보입니다.

코드 변경 사항은 날씨 입자를 업데이트하기 위해 콜백이 정의되었고, 기존 작업 작업 풀이 사용되었으며, 날씨 입자를 보유하기 위해 버텍스 버퍼가 생성되었으며, 입자 시스템이 일반 또는 비동기로 표시되었다는 것입니다. 비동기 시스템은 "좋은 쌍둥이"와 "악한 쌍둥이"로 나뉩니다. 그리기 과정에서 좋은 쌍둥이는 마지막 프레임에서 사악한 쌍둥이의 입자를 그린 다음 사악한 쌍둥이의 그리기 상태를 캡처합니다. 업데이트하는 동안 사악한 쌍둥이는 콜백의 버텍스 버퍼를 처리하고 채웁니다.

그들은 스레딩 기능이 큰 잠재력을 갖고 있으며 각 추가 코어는 풀에 추가 스레드를 제공하고 각 추가 스레드는 추가 기능과 동일하다고 결론지었습니다.

Project “Smoke” N 코어 엔진 실험에서는 Project Smoke의 멀티스레딩 경험을 공유했습니다. 이 기사에서 언급된 게임 엔진의 멀티스레드 아키텍처는 다음과 같습니다.

나는 또한 2007년 게임의 멀티스레딩 추세와 방향에 동의합니다.

시스템 구독 변경 메시지 메커니즘은 다음과 같습니다.

작업의 구분, 단계, 실행 및 메시지 전통은 다음과 같습니다.

New Dog, Old Tricks: 하드 드라이브 없이 Halo 3 실행에서는 고급 IO 디자인, 콘텐츠 로딩, 로딩 프로세스 및 기타 기술에 대해 설명합니다. 이 기사에서는 로딩 충돌을 해결하기 위해 캐시된 리소스 액세스 상태를 추가하는 Halo 3에 대해 설명합니다.

리소스 액세스 상태는 다음과 같습니다.

또한 이 문서에서는 지역 집합 전환을 위한 전략과 방법에 대해 자세히 설명합니다.

리소스 로딩은 리소스 스케줄러에 의해 제어됩니다. 각 우선순위는 우선순위에 따라 처리되는 필수 세트를 생성합니다. I/O가 실행되거나 성공할 수 없으면 처리가 중지됩니다.

지도는 최적화된 지도 레이아웃과 공유 지도 레이아웃을 통해 지도 파일을 다시 연결하여 최적화된 지도를 얻습니다.

2009년에는 Frostbite의 병렬 그래픽 - 현재와 미래(Frostbite - Current & Future)에서 Frostbite의 CPU 및 GPU 병렬 기술과 이를 렌더링 시스템에 도입하는 방법을 설명했습니다. 당시 주류 PC는 하드웨어 스레드가 2~8개에 이르렀고, PS3는 하드웨어 스레드 2개와 SPU 6개, Xbox 360도 하드웨어 스레드 6개에 이르렀습니다.

2009년경 CELL 프로세서의 등장.

이러한 하드웨어 스레드를 완전히 활용하기 위해 Frostbite는 시스템을 작업, 명시적인 입력 및 출력이 있는 비동기 함수 호출, 일반적으로 완전히 독립적인 상태 비저장 함수 작업 종속성으로 나누어 작업 그래프를 생성하고 모든 코어가 작업을 소비하는 작업 시스템을 도입합니다.

대규모 CPU 작업 그래프를 작성할 때 CPU 및 SPU 작업의 일괄 처리 및 혼합을 사용하여 GPU 작업의 대기 시간을 줄입니다. 그 중 작업 종속성은 실행 순서, 동기화 지점, 로드 밸런싱 등 효과적인 병렬성을 결정합니다.

렌더링을 위해 Frostbite는 렌더링 시스템을 다수의 작업으로 나눕니다. 렌더링 작업에는 주로 지형 지오메트리 처리, 관목 생성, 데칼 투영, 입자 시뮬레이션, 프러스텀 클리핑, 폐색 클리핑, 폐색 래스터라이제이션, 명령 버퍼 생성, PS3의 삼각형 클리핑 등이 포함됩니다. 대부분의 렌더링 작업은 GPU로 이동되며 주로 단방향 데이터 흐름을 사용하는 작업입니다.

명령 버퍼를 병렬로 기록할 때 그리기 호출과 상태는 코어의 동일한 선형 확장(프레임당 1500-4000 그리기 호출)을 사용하여 여러 명령 버퍼에 병렬로 전달되어 대기 시간을 줄이고 성능을 향상시킵니다. DX11에 병렬 명령 로깅을 구현하는 것은 CPU 오버헤드와 대기 시간을 줄이는 킬러 기능입니다. 렌더링 예약 작업 시간의 약 90%가 D3D/드라이버에서 이루어집니다. 구현 단계는 대략 다음과 같습니다.

  1. 각 코어에 대해 DX11 지연 장치 컨텍스트를 생성하고 이를 지연 컨텍스트용 동적 리소스(cbuffer/vbuffer)와 함께 사용합니다.

  2. 렌더러에는 프레임의 각 렌더링 "레이어"에 대해 수행하려는 모든 그리기 호출 목록이 있습니다.

  3. 각 레이어의 드로우 콜을 약 256개의 청크로 분할하고 지연 컨텍스트와 병렬로 예약합니다. 각 청크는 명령 목록을 생성합니다.

  4. 즉각적인 컨텍스트로 렌더링하고 명령 목록을 실행합니다.

당시 목표는 완전한 DX11 드라이버 지원이 이루어졌을 때 8개 코어(현재는 IHV)로 거의 선형 확장하는 것이었습니다.

오클루전 컬링의 경우 보이지 않는 개체는 여전히 로직과 애니메이션을 업데이트하고 명령 버퍼를 생성해야 하며 CPU와 GPU의 양쪽 끝에서 처리되어야 합니다. 파괴 가능한 건물, 동적 폐색, 사전 계산이 어렵고 GPU 폐색 쿼리 렌더링이 무거울 수 있는 등 일부 개체는 전체 컬링을 달성하기 어렵습니다.

위의 오클루전 컬링 문제에 직면한 Frostbite의 솔루션은 소프트웨어 폐색 래스터라이제이션를 사용하는 것입니다. 두 가지 주요 단계로 나누어집니다.

  • SPU/CPU에서 대략적인 zbuffer를 래스터라이제이션합니다. 256x114 플로트, SPU LS(로컬 캐시)에 적합하지만 아마도 16비트, 낮은 폴리 폐색 메시, 보수적인 수동 설정, 100m 보기 거리, 최대 10000 버텍스/프레임, 병렬 SPU 버텍스 및 래스터 작업, 밀리초 단위 소비.

  • 그런 다음 zbuffer를 기반으로 모든 개체를 추려냅니다. 큰 성능 향상을 위해 다른 모든 시스템에 전달되기 전에 화면 공간 경계 상자 테스트가 수행됩니다.

Battlefield: Bad Company PS3의 이미지 및 대략적인 z-버퍼.

소프트웨어 오클루전 컬링 외에도 Frostbite는 GPU 오클루전 컬링도 지원합니다. GPU 래스터라이제이션 및 테스트가 이상적으로 필요하지만 오클루전 쿼리는 오버헤드와 대기 시간(관리 가능하지만 이상적이지는 않음)을 발생시키고 조건부 렌더링은 GPU에만 도움이 됩니다(CPU, 프레임 메모리 또는 그리기 호출이 아님). 이때 탐구해야 할 두 가지 목표가 있습니다.

  • 낮은 대기 시간의 추가 GPU 실행 컨텍스트. 래스터라이제이션 및 테스트는 CPU와 동일한 단계로 GPU에서 수행되며 데이터는 밀리초 단위로 다시 읽어야 하며 이는 LRB가 필요한 모든 하드웨어에서 가능합니다.

  • 전체 컬링 및 렌더링을 GPU로 이동합니다. 세계의 대표성, 선별, 시스템, 파견도 최종 목표입니다.

조명 지연 측면에서 Frostbite는 화면 공간 타일 분류도 사용합니다. 구체적인 단계는 다음과 같습니다.

  • 화면을 타일로 나누고 각 타일과 교차하는 광원 수와 광원을 결정합니다.

  • 각 패치의 픽셀에만 가시광선을 적용합니다. 단일 셰이더에서 여러 조명을 사용하여 대역폭과 설정 비용을 줄입니다.

화면을 타일로 분할한 후 컴퓨팅 셰이더와 쉽게 조정하여 작업을 한 번에 완료할 수 있습니다. 이 접근 방식은 Naughty Dog의 Uncharted(아래) 및 SCEE PhyreEngine에서 사용되었습니다.

GDC'08 "The Technology of Uncharted"의 화면 공간 타일링 기술.

화면 공간 분할을 기반으로 CS 기반의 Deferred Shading을 구현할 수 있습니다. 프로덕션 테스트 또는 최적화가 아닌 Frostbite 2에서 실험적으로 구현된 DX11 CS를 사용한 디퍼드 렌더링에는 그림자가 없다고 가정할 때 Compute Shader 5.0이 필요합니다.

새로운 하이브리드 그래픽/컴퓨팅 셰이딩 파이프라인:

  • 불투명 표면을 위한 그래픽 파이프라인 래스터라이제이션된 GBuffer.

  • 계산 파이프라인은 GBuffer를 사용하여 광원을 제거하고, 조명을 계산하고, 셰이딩 결과를 결합합니다.

셰이더를 계산하는 단계는 다음과 같습니다.

  • GBuffer와 깊이를 로드합니다.

  • 스레드 그룹/청크의 최소 및 최대 Z를 계산합니다.

  • 각 타일의 가시광선 소스를 결정합니다.

  • 각 픽셀에 대해 가시광선으로부터 조명을 축적합니다.

  • 조명과 그림자 알베도/매개변수를 결합합니다.

각 단계에는 더 자세한 내용이 있습니다. 자세한 내용은 논문 또는 4.2.3.2 Tiled-Based Deferred Rendering (TBDR)을 참조하세요. 이 기술은 장면의 대규모 광원 조명을 지원하는 데 사용될 수 있습니다.

CS 기반 Deferred Shading의 장점과 단점은 다음과 같습니다.

  • 장점:

일정하고 절대적인 최소 대역폭.

GBuffer와 깊이를 한 번만 읽으십시오!

  • 중간 광 버퍼가 필요하지 않습니다.

HDR을 사용하면 MSAA 및 색상 반사광이 많은 메모리를 차지합니다.

  • 다수의 대형 중첩 광원으로 확장!

세분화된 컬링(16x16).

  • ALU 비용만 들고 향후 확장성이 좋습니다.

  • VPL(Virtual Point Light)을 축적하는 데 유용할 수 있습니다.

  • 단점:

DX11 이상을 지원하는 하드웨어가 필요합니다.

CS 4.0/4.1은 원자성 및 분산된 그룹 공유 쓰기로 인해 어렵습니다.

  • 작은 광원의 오버헤드를 제거합니다.

표준 라이트 볼륨 렌더링을 사용하여 누적할 수 있습니다.

  • 또는 타일 분류를 위한 별도의 CS.

  • 잠재적인 성능.

MSAA 텍스처 로딩/UAV 쓰기는 표준 PS보다 느릴 수 있습니다.

  • MSAA 텍스처로 출력할 수 없습니다.

DX11 CS UAV 제한 사항.

요약하자면, 우수한 병렬화 모델은 우수한 게임 엔진 성능의 핵심이며 작업과 데이터 병렬 CPU 및 SPU 작업을 혼합하는 작업 그래프는 SPU 작업이 무거운 작업을 수행하는 Frostbite에 완벽하게 적합합니다. 효율적인 상호 운용성을 위해 DX11을 최대한 활용함으로써 Frostbite는 하이브리드 컴퓨팅/그래픽 파이프라인에 새로운 시도와 기술적 이정표를 열었습니다. 또한 Frostbite는 순차적 메모리 전송보다는 데이터 흐름과 패턴에 초점을 맞춘 사용자 정의 스트리밍 파이프라인 모델, 큐가 있는 표현력 있고 확장 가능한 하이브리드 파이프라인을 기대합니다.

물리 파이프라인 병렬화: GPU에서의 물리 시뮬레이션에서는 GPU를 사용하여 강체, 유체, 입자 등의 물리적 매개변수, 동작 및 충돌을 포함한 물리 시뮬레이션 기술을 병렬화하는 방법을 설명합니다. 단계에 대한 개요는 다음과 같습니다.

  • 입자 값 계산. 각 입자에 대해: 강체의 값을 읽고 입자 값을 씁니다.

  • 그리드 생성.

  • 충돌 감지 및 반응. 각 입자에 대해 메시에서 이웃을 읽고 계산된 힘(스프링 및 댐퍼)을 씁니다.

  • 모멘텀을 업데이트하세요. 각 강체에 대해: 입자의 힘을 요약하고 운동량을 업데이트합니다.

  • 위치와 쿼터니언을 업데이트합니다. 각 강체에 대해 운동량을 읽고 업데이트합니다.

위 단계에는 격자 생성, GPU 트리 구조 탐색 및 동적 생성과 같은 기술이 포함됩니다. 다음 그림은 히스토리 마커를 사용하여 GPU의 트리 구조 최적화 기술을 탐색하는 방법과 스택과의 성능 비교 차트를 설명합니다.

이 기사에서는 또한 병렬 업데이트의 문제에 대해 언급합니다. 하나의 강체가 다른 강체와 충돌하더라도 문제가 없습니다. 하나의 강체가 여러 개의 강체와 충돌하는 경우 병렬 업데이트를 수행할 수 없습니다. 해결책은 함께 일괄 처리하고, 모든 것을 동시에 업데이트하지 않고, 일괄 처리로 분할하고, 일괄 충돌이 병렬로 업데이트되도록 순차적으로 일괄 업데이트하는 것입니다. GPU에서 결합된 배치를 만드는 것은 CPU에 비해 ​​쉽지 않습니다. 당시 GPU의 특성을 기반으로 특정 전략에 따라 배치를 병렬로 생성해야 합니다.

또한 이 기사에서는 물리적 시뮬레이션의 다중 GPU 조정 실행을 위한 전략과 사례도 설명합니다.

상단: 물리적 효과에 대한 다중 GPU 병렬 시뮬레이션 설계; 하단: 다양한 수의 GPU 성능 비교.

id Tech 5 과제 - 텍스처 가상화에서 대규모 병렬화까지 id Tech 5의 GPU 가상 텍스처, 가상 텍스처로 운영 체제 병렬화, (GP) GPU로 작업 마이그레이션 등에 대해 설명합니다. 가상 텍스처 기술의 개요는 다음과 같습니다.

가상 텍스처 시각화:

피드백 정보 분석을 통해 어떤 페이지가 필요한지 알려주며, 실시간 애플리케이션이므로 차단이 허용되지 않습니다. 캐시는 적중을 처리하고, 미스를 디스패치하여 백그라운드에서 로드하며, 상주 페이지는 디스크 캐시와 독립적으로 관리되며, 물리적 페이지는 가상 텍스처당 쿼드트리로 구성되고, 무료 페이지, LRU 및 잠긴 페이지의 링크된 목록이 구성됩니다. 가상 텍스처의 피드백 분석 중에 우선순위에 해당하는 너비 우선 쿼드트리 시퀀스가 ​​생성됩니다.

종속성이 있는 계산 집약적인 복잡한 시스템이지만 모든 다른 플랫폼에서 병렬로 실행되기를 원합니다. 가상 텍스처 파이프라인은 다음과 같습니다.

id Tech 5의 작업 처리 시스템은 단순성이 확장성의 핵심임을 강조합니다. 작업은 입력과 출력이 명확하고, 독립적이고 상태가 없으며, 일시 중지가 없고 항상 완료됩니다. 작업 목록에 추가된 작업에는 여러 개의 작업 목록이 있으며, 작업 작업은 완전히 독립적입니다. 목록의 작업은 단순히 "신호" 및 "동기화" 토큰을 통해 동기화됩니다.

그러나 동기화는 대기를 의미하며 대기는 병렬성을 파괴합니다. 아키텍처 결정: 작업 처리를 완료하려면 1프레임 지연이 필요하고, 작업 결과는 1프레임 늦고, 일부 알고리즘 작업(예: 나뭇잎)이 필요하고, 일부 알고리즘(예: 투명도 정렬을 위한 화면 공간 비닝)을 제외하지만 전반적으로 나쁜 절충안은 아닙니다.

id Tech 5의 운영 하위 시스템에는 충돌 감지, 애니메이션 혼합, 장애물 회피, 가상 텍스처, 투명도 처리(나뭇잎, 입자), 옷감 시뮬레이션, 수면 시뮬레이션, 세부 모델 생성(바위, 조약돌 등) 등이 포함됩니다.

SIMD/SIMT 레인을 채울 작업이 충분하지 않고 다양한 작업의 코드 경로가 너무 많이 갈라지고 작업이 작업 단위(대기 시간 허용 및 작은 메모리 공간)로 유용한 (GP) GPU 작업의 경우 작업에서 데이터 병렬성을 활용할 필요가 있습니다. 작업을 여러 개의 세분화된 스레드로 분할, 입력의 데이터 종속성, 출력 데이터의 수렴, 세분화된 스레드의 메모리 액세스가 중요합니다.

지연 접근 방식을 기반으로 하는 새로운 멀티스레드 렌더링 시스템은 멀티스레드 렌더링을 위해 설계된 렌더링 시스템의 아키텍처를 소개합니다. 디퍼드 렌더링 방법을 따른 아키텍처 구현은 듀얼 코어 시스템에서 65% 향상된 성능을 보여줍니다.

게임은 일반적으로 D3D 런타임 및 드라이버에서 프레임 시간의 25%~40%를 소비하며, 엔진이 장면을 렌더링하는 동안 다른 작업을 처리할 수 있다면 오버헤드는 문제가 되지 않습니다. 그러나 그래픽 시스템은 작동하는 동안 공유 데이터를 변경하지 않고 유지해야 하기 때문에 다른 엔진 시스템이 차단되므로 그래픽 시스템이 단일 스레드인 경우 응용 프로그램은 CPU 성능을 많이 낭비하게 됩니다. 이 문서에서 설명하는 시스템 아키텍처는 모든 CPU 코어를 활용하여 공유 데이터의 소유권을 가능한 한 빨리 다른 시스템에 반환하는 명령 버퍼를 생성하도록 설계되었습니다. 버퍼가 생성되면 업데이트 시스템이 다시 실행될 수 있지만 단 하나의 그래픽 스레드만 여전히 명령 버퍼를 GPU에 제출합니다. 아래 다이어그램은 설명된 프로세스를 보여줍니다.

또한 이 문서에서는 다양한 플랫폼에서 차별화된 구현을 위한 렌더링 상태를 캡슐화합니다.

그래픽 관리자는 스레드 풀을 초기화하고 이후에 애플리케이션의 작업을 제공하는 추상화 계층의 중앙 클래스입니다. 생성된 각 스레드에 대해 컨텍스트가 인스턴스화되고 할당되며 이 소유권은 스레드 수명 동안 지속됩니다. 서로 다른 스레드가 동일한 컨텍스트를 호출할 수 없기 때문에 컨텍스트의 소유권을 변경하는 것은 불가능합니다. (아래 사진)

아래 그래프는 일반적인 단일 스레드 솔루션과 멀티 스레드 그래픽 관리자를 사용하는 렌더링 엔진이 달성한 초당 프레임 수를 보여줍니다. 단일 스레드 결과는 ST라는 제목의 녹색 막대로 표시되고, 멀티 스레드 결과는 MT라는 제목의 빨간색 막대로 표시됩니다. 가로 좌표는 개체 수를 나타내고 세로 좌표는 프레임 수를 나타냅니다.

위 그림을 보면 객체 수가 적을수록 멀티스레딩의 프레임 수가 조금씩 감소하는 것을 알 수 있습니다. 그러나 개체 수가 증가할수록 멀티스레딩의 장점은 더욱 두드러집니다. 개체 수가 2006개에 도달하면 멀티스레딩의 프레임 수가 싱글스레딩의 1.7배가 됩니다.

RT 렌더링의 회귀: LittleBigPlanet Post Mortem은 버텍스 애니메이션, 구성 감지, 클러스터, 그림자, 접촉 AO, 스프라이트 광원, 예수 조명, 이중 레이어 투명도, DOF, 모션 블러, 수역, 유체 및 기타 기술을 포함하여 LittleBigPlanet 게임에 사용된 기술을 공유했습니다.

클러스터는 메시가 없는 알고리즘입니다. 즉, 변형된 버텍스 구름에 새로운 강성 행렬을 맞추는 최소 제곱입니다.

  • 질량 중심을 제거합니다. - 병진 구성 요소를 제공합니다.

(\text{P} - \text{COM}) \ \times \ (\text{P}_{\text{rest} - \text{COM}_{\text{rest})의 이진 행렬 합입니다.

  • 나머지 포즈에서 미리 계산된 행렬의 역수를 곱합니다.

  • 결과로 나온 최적 맞춤 행렬을 직교화하여 아핀 변환을 제공하고 선택적으로 정규화를 통해 볼륨을 보존합니다.

  • 원래의 강체 매트릭스와 혼합하여 원하는 대로 부드러운/강한 모양을 만듭니다.

자세한 내용은 모양 일치를 기반으로 한 메시 없는 변형을 참조하세요.

LittleBigPlanet은 캐릭터에 AO 조명을 추가하여 5개의 구(2피트, 2개 다리, 1개 사타구니)로 모델링된 캐릭터의 폐색을 분석하고 계산할 수 있습니다. 구체적인 파생 내용은 구형 앰비언트 오클루전(AO) - 2006을 참조하세요.

접점 AO의 원리(위)와 ON 전후의 효과 비교(아래)

엘프 광원 효과.

이중 레이어 반투명 효과.

워터 그리드.

Zen of Multicore Rendering은 당시 멀티코어 콘솔 하드웨어에 대한 효율적인 렌더링 기술의 편집 및 실습을 공유했습니다.

기사에서는 멀티 코어 시대의 처리 능력을 이전 세대와 비교하여 트라이앵글 처리량 70배, 픽셀 필레이트 450배, 텍스처 레이트 390배, 대역폭 110배, 비디오 메모리 16배라고 언급했습니다.

채우기 속도는 완전 비동기식 비순차 VPU(벡터 처리 장치) 계산을 통해 달성됩니다. Larrabee에는 코어당 4개의 하드웨어 스레드가 있고 각 스레드는 비순차적이지만 스레드 하나가 실행될 때 정점과 픽셀이 동기화됩니다. 따라서 기본적으로 256개의 비순차적 프로세스가 있으며, 각 프로세스는 약 16개의 동기화된 픽셀 또는 버텍스 세트로 구성되어 언제든지 실행됩니다. 셰이더 플립플롭이 가장 많이 성장할 것으로 예상되며, 속도는 더 높은 클럭 속도에서 나올 것으로 예상되지 않으며, 속도는 다수의 저전력 코어에서 나오며, 메모리는 셰이더 플롭을 따라잡을 수 없을 것으로 예상됩니다. ALU 또는 VPU가 300배 증가합니다. 미래는 ALU 대신 텍스처 획득으로 제한됩니다. 동형 컴퓨팅은 ALU 또는 VPU가 일관된 로컬 데이터를 캐싱하는 데 계속 사용됩니다.

멀티 코어의 영향 또는 적용에는 동적 기하학적 폐색, 동적 가시성 계산, 공간적으로 변화하는 BRDF, 고주파 조명, 고품질 해상도, 나머지 절충안 제거 등이 있습니다. 실용적인 팁에는 방향성 라이트 매핑 기본 사항, 영역 조화, 스크린 스페이스 앰비언트 오클루전(SSAO), 그림자 매핑 등이 포함됩니다. 동적 방사조도의 계산 과정은 다음과 같습니다.

위 그림의 웨이블릿 래디언스 캐시에는 Haar 웨이블릿 기반, 가시성, 방사선 분해의 3단계가 포함되어 있습니다. Haar 웨이블릿 기반의 경우 구형 고조파가 복사 전달에 사용할 수 있는 유일한 기반은 아닙니다. 복사휘도와 면광의 합은 Haar 웨이블릿으로 표현될 수도 있습니다. Haar 웨이블릿의 방사적으로 가시적인 삼중 통합은 GPU에서 실시간으로 실행될 만큼 빠릅니다.

하르 웨이블릿.

2D Haar 웨이블릿 및 가시성의 경우 가시성 함수 V(x, theta)도 이진 함수이며 가시성에 웨이블릿 복사를 곱하는 것은 공간적으로나 물리적으로 웨이블릿을 켜고 끄는 방정식의 일부입니다. 웨이블릿 휘도 및 가시성 제품의 통합으로 런타임 방정식도 단순화됩니다. 어떤 면에서 구면 고조파는 방향성 조명 맵의 기본 주파수 보정 분포이며, 대역 고조파는 한 방향으로 편향되지 않고 복사 기여도를 올바르게 샘플링하고 저장합니다.

멀티코어로 활용할 수 있는 것은 그림자뿐만 아니라 각 채널이 빛이 아닌 전체 복사 조명 모델로, tfetchCube에서 희소 웨이블릿 데이터를 효율적으로 샘플링합니다. 방사능을 계산하는 과정에서 차원을 줄이고 계산을 단순화하기 위해 **주성분 분석(PCA)**을 사용해야 합니다. 다음 그림은 PCA 이후의 복합 입력 데이터입니다. PCA가 하는 일은 직교 변수와 그 결과 분포를 추출하는 것입니다.

14.3.3.3 모바일 생태학

모바일 기반 게임 개발에 대한 튜토리얼은 Game Developer - 2005년 10월에 게재되었습니다(아래 그림). 매거진에서는 모바일 게임에서 멋진 3D 효과를 만드는 것이 생각보다 쉽다고 언급했습니다. Sony Ericsson Developer World에서는 기술 문서부터 반응형 기술 지원까지, Mascot Capsule v3 및 JSR-184(m3g)용 모바일 플러그인까지 모바일 JavaTM 3D 개발 속도를 높이는 데 필요한 모든 것을 찾을 수 있어 개발자가 3D Studio Max, Maya 및 Lightwave와 같이 선호하는 도구를 사용할 수 있습니다.

OpenGL ES의 발전과 모바일 기기의 개선으로 모바일 생태계가 구체화되기 시작했으며, 모바일 3D 생태계는 2007년에 모바일 플랫폼의 기술과 생태에 대해 자세히 설명합니다. 이 기사 시리즈는 OpenGL ES 1.x 및 2.0에 중점을 두고 그 특성과 응용 프로그램을 설명합니다. 기사에 따르면 2007년에는 모바일 사용자가 30억 명이 될 것이라고 합니다.

2009년에는 무선 광대역 사용자가 10억 명을 넘어설 것이며, 2010년에는 60억 인구 중 90%가 모바일 네트워크를 갖게 될 것입니다.

모바일 장치의 특성은 전력이 궁극적인 병목 현상이라는 것입니다. 일반적으로 벽에 연결되지 않고 배터리에만 연결됩니다. 배터리는 무어의 법칙을 따르지 않으며 연간 5~10%만 용량을 증가시킵니다. 유전자의 법칙에 따르면 집적 회로의 전력 소비는 시간이 지남에 따라 기하급수적으로 감소하므로 배터리 수명이 길어집니다. IC를 구동하는 데 필요한 전력은 1994년 이후 2년마다 10배씩 감소했지만 2년 전의 성능으로는 충분하지 않아 더 빠르고 더 많은 전력을 절약해야 합니다. 다른 제약으로는 열 방출과 디스플레이가 있습니다.

일반적인 모바일 기기(위)와 2005년 이후 화면의 진화(아래).

당시 모바일 그래픽 API의 아키텍처 다이어그램은 다음과 같습니다.

OpenGL ES 1.0은 OpenGL 구조를 보존하고 불필요한(중복/비싼/사용하지 않는) 기능을 제거하고 50KB 미만의 설치 공간과 하드웨어 FPU가 필요하지 않은 컴팩트하고 효율적인 기능을 제공합니다. 또한 혁신을 주도하고 확장을 허용하며 조화를 이루고 다른 모바일 3D API(M3G/JSR-184)와 일관성을 유지하며 Symbian OS, S60, Brew, PS3/Cell 아키텍처 등과 같은 시스템에서 지원됩니다. 현재 렌더링 파이프라인은 다음과 같습니다.

OpenGL ES 1.1에는 버퍼 개체, 더 나은 텍스처(2개보다 큰 텍스처 단위, 결합(+,-,interp), dot3 범프, 자동 밉맵 생성), 사용자 클리핑 평면, 포인트 스프라이트(쿼드 대신 점으로 입자, 거리에 따라 크기 감소), 상태 쿼리(상태 저장/복원 가능, 미들웨어에 적합)가 추가되었습니다. 다음은 모바일 GPU의 일부 매개변수와 기능입니다.

당시 모바일 장치에는 다음과 같은 다양한 유형과 플랫폼이 있었습니다.

  • CPU 속도와 사용 가능한 메모리는 다양합니다. 전류 범위는 30Mhz~600MHz, ARM7~ARM11, FPU 없음.

  • 다른 해상도. QCIF(176x144) - VGA(640x480), 고급 장치에서 앤티앨리어싱, 채널당 색 심도 4-8비트(12-32bpp).

  • 이식성 문제. 다양한 CPU, 운영 체제, Java VM, C 컴파일러...

GPU 그래픽 기능은 다음과 같습니다.

  • 일반 멀티미디어 하드웨어. 순수 소프트웨어 렌더러(모두 CPU 및 정수 ALU를 사용하여 수행됨), 소프트웨어 + DSP/WMMX/FPU/VFPU, 멀티미디어 가속기.

  • 전용 3D 하드웨어. 소프트웨어 T&L + 하드웨어 삼각형 설정/래스터라이제이션, 전체 하드웨어 가속.

  • 성능: 50K – 2M 트리스, 1M – 100M 픽셀/초.

OpenGL ES는 하드웨어 추상화 계층으로 작동하여 프로그래밍 인터페이스(API)와 다양한 장치에 대한 동일한 기능 세트 및 통합 렌더링 모델을 제공할 수 있습니다(그러나 성능은 보장되지 않습니다).

OpenGL ES 1.x 렌더링 효과.

OpenGL ES는 셰이더 단계를 사용합니다.

모바일 게임 Playman Winter Games – Mr. Goodliving의 2D 및 3D 스크린샷입니다.

다음 두 가지는 당시의 일반적인 게임 개발 과정과 M3G 프레임워크를 활용한 모바일 게임 개발 과정을 보여준다.

다음 그림은 고급, 중간, 저가형 장치의 M3G 프레임워크 아키텍처 다이어그램입니다.

OpenGL ES 2.0 출시 초기에 게임 개발자들은 새로운 하드웨어에 앞서 게임 엔진을 개발해야 하는 과제에 직면했습니다. OpenGL ES 2.0에서는 휴대용 개발자가 엔진을 크게 변경해야 할 수도 있습니다. 셰이더 기반 API는 더 많은 부담을 애플리케이션에 전가하므로 프로그래밍 가능성을 통해 유연성이 향상됩니다. 그러나 렌더링 효과는 OpenGL ES 2.0 Emulator 및 Render Monkey(아래 그림)를 통해 시뮬레이션할 수 있습니다.

당시 AMD의 Sushi 엔진은 Open ES 2.0 통합을 최초로 지원했습니다. 직면한 주요 과제는 다양한 기능 세트를 갖춘 여러 API를 대상으로 하는 엔진 설계, 셰이더 기반 엔진 설계, 플랫폼 호환성, 휴대용 플랫폼 기능이 크게 다양하고 다양한 제한 사항으로 인해 이식성이 문제가 되는 것이었습니다.

위: 2005년 Sushi는 OpenGL에 없는 기능을 지원하는 확장 기능을 사용하여 DX9 기능 세트를 추상화했습니다. 하단: 2007년에는 그래픽 API가 여러 개 더 있었고 선택이 더 이상 쉽지 않았습니다. 특히 게임 콘솔이 믹스에 추가된 경우에는 더욱 그렇습니다...

당시 Sushi Engine 추상화 API는 요구 사항에 따라 구동되었습니다. 모든 API의 최신 기능을 사용해야 했고, 가장 낮은 공통 분모를 노출하는 것은 옵션이 아니었고, 모든 API에서 동일한 데모를 실행하는 것은 요구 사항이 아니었으며, 콘텐츠가 API 추상화가 아닌 기능 세트를 구동하도록 해야 했습니다. API 추상화는 리소스, 뷰, 지오메트리 셰이더, 스트림 출력, 모든 최신 및 뛰어난 기능 등 DX10과 매우 유사합니다. 각 API 구현은 이러한 기능의 하위 집합을 지원합니다. API 추상화에 대한 대체 경로도 있습니다. 엔진은 Lua를 사용하는 스크립팅 시스템을 기반으로 하며 Lua 스크립트는 대체 렌더링 경로를 제공합니다. Sushi의 경우 콘텐츠 이식성과 고급 기능을 비교하는 것이 좋은 절충안입니다.

휴대용 플랫폼에는 표준 템플릿 라이브러리 없음, C++ 예외 없음, 수동 스택 정리, 불완전한 표준 라이브러리, 제한된 메모리 공간, 부동 소수점 단위 없음 등 많은 제한 사항이 있습니다. Sushi는 플랫폼 이식성을 보장하기 위해 표준 추상화 계층(수학, I/O, 메모리, 창 등), 사용자 정의 템플릿 클래스(목록, 벡터, 매핑 테이블 등)를 사용하고 C++ 사용을 제한합니다(예외 및 STL 없음).

14.3.4 렌더링 기술

이 섹션에서는 20000년대에 탄생한 몇 가지 중요한 렌더링 기술에 대해 설명합니다.

14.3.4.1 구형 고조파

  • SH 기본

**구형 조화(SH)**는 구면 조화, 구면 조화, 구면 조화 함수로 번역되며, 이는 1차원 원의 푸리에 변환과 유사하게 구S의 직교 기반을 정의합니다.

S가 Cadir 좌표계와 구면 좌표계를 사용하는 경우 매개변수화된 공식은 다음과 같습니다.

s=(x, y, z)=(sinθcosφ, sinθsinφ, cosθ)

구형 기초 함수는 다음과 같이 정의됩니다.

Ym(θ,φ)=NeimφPm(cosθ), lN,lml

여기서 l은 차수(밴드 인덱스라고도 함)이고, m은 차수(lml) 내의 인덱스이고, Pm는 관련 르장드르 다항식입니다. Klm이 다음 형식의 정규화 상수라고 가정합니다.

K_l^m ={\sqrt {\frac {(2\ell +1)}{4\pi }{\frac {(\ell -|m|)!}{(\ell +|m|)!&#125;&#125;

위의 정의는 복소수 기반을 구성하며 간단한 변환을 통해 실수 기반을 얻을 수 있습니다.

KlmYm(θ,φ)에 대입하면 다음을 얻을 수 있습니다.

Y_{\ell }^{m}(\theta ,\varphi )={\sqrt {\frac {(2\ell +1)}{4\pi }{\frac {(\ell -m)!}{(\ell +m)!&#125;&#125;\,P_{\ell }^{m}(\cos {\theta })\,e^{im\varphi }

다음 그림은 l=6(처음 7개 차수)일 때 SH 직교 기준을 시각화한 것입니다.

반구형 고조파 기능도 있습니다.

  • SH 투영 및 재구성

SH 기저가 정규직교이기 때문에 구 S에 정의된 스칼라 함수 f는 적분을 통해 해당 계수에 투영될 수 있습니다.

flm=f(s) ylm(s) ds

일부 논문에서는 시작점이 다른 i의 다른 유사한 투영 공식을 사용합니다(그러나 본질은 동일하지만 표현이 다릅니다).

ci=sf(s)yi(s)ds, i=l(l+1)m

이러한 계수는 n 차수의 재구성 함수를 제공합니다.

f=l=0n1m=llf(s) ylm(s)

n 차수가 증가할수록 f에 가까워집니다. 저주파 신호는 몇 개의 SH 대역만으로 정확하게 표현될 수 있으며, 고주파 신호는 저차 투영을 통해 대역이 제한됩니다(즉, 앨리어싱 없이 원활하게 처리됨). n 차수 투영에는 n2 계수가 포함되며 $\overset{\frown} {f} $를 투영 계수와 기저 함수의 단일 인덱스 벡터로 다시 작성하는 것이 다음과 같이 편리할 때가 많습니다.

f=i=1n2fi yi(s)

그중i=l\(l+1)+m+1. 이 공식은 명백합니다. s에서 재구성 함수의 평가는 n2 구성요소 계수 벡터 fi와 평가된 기저 함수 yi(s)의 벡터의 단순한 내적을 나타냅니다.

SH 투영 예. 두 개의 표면 조명으로 구성된 함수를 사용하여 SH는 이를 4개 밴드 = 16개 계수로 투영합니다.

저주파수 신호(사진의 저주파 광원)의 경우 이러한 계수만을 사용하여 신호를 재구성하여 원래 신호(광원)의 저주파 근사치를 찾을 수 있습니다.

SH 신호 재구성은 단순히 각 SH 기본 함수와 해당 계수의 곱을 선형 결합한 것입니다.

  • SH 속성

SH 투영의 주요 특징은 회전 불변성입니다. 즉, g(s)=f(Q(s))가 주어지면 QS에 대한 회전이므로 다음을 충족합니다.

g=f(Q(s))

1차원 푸리에 변환의 변환 불변성과 유사합니다. 실제로 이 속성은 f의 샘플이 회전된 샘플 점 집합에서 수집될 때 SH 투영이 앨리어싱 왜곡을 일으키지 않음을 의미합니다.

SH 투영은 회전 불변입니다.

SH 기저의 직교성은 유용한 속성을 제공합니다. 즉, S에 대한 임의의 두 함수 ab가 주어지면 해당 투영은 다음을 충족합니다.

a(s)b(s)ds=i=1n2aibi

즉, 대역 제한 함수 곱의 적분은 해당 투영 계수의 내적으로 감소합니다.

원형 대칭 커널 함수 h(z)와 함수 f의 컨볼루션은 hf로 표현됩니다. h는 원형 대칭이어야 합니다(따라서 s가 아닌 z의 간단한 함수로 정의될 수 있음). 따라서 결과는 고차원 회전 그룹 SO(3)이 아닌 S에 정의됩니다. 컨볼루션 투영은 다음을 충족합니다.

\Big(h * f \Big)_l^m = \sqrt{\cfrac{4\pi}{2l+1} \ h_l^0 \ f_l^m = \alpha_l^0 \ h_l^0 \ f_l^m

즉, 투영된 컨볼루션의 계수는 개별 투영 함수의 단순한 스케일링 제품입니다. hz에 대해 원형 대칭이므로 m=0인 경우에만 투영 계수가 0이 아닙니다. 컨볼루션 기능은 h(z)=max(z,0)으로 정의된 반구형 코사인 커널을 사용하여 환경 맵을 컨볼루션하여 조도 맵을 얻는 빠른 방법을 제공합니다. 여기서 hl0는 분석 공식으로 제공됩니다. 컨벌루션 기능은 더 좁은 커널로 사전 필터링된 환경 맵을 생성하는 데에도 사용할 수 있습니다.

a는 알려져 있고 b는 알려지지 않은 한 쌍의 구형 함수 c(s)=a(s)b(s)의 곱을 투영하는 것은 행렬 a^를 통한 투영 계수 bj의 선형 변환으로 간주될 수 있습니다.

ci=a(s)(bjyj(s))yi(s)ds=(a(s)yi(s)yj(s)ds)bj=(akyi(s)yj(s)yk(s)ds)bj=a^ijbj

여기서 반복되는 jk 인덱스는 암시적으로 합산됩니다. a^는 대칭 행렬이고 a^의 구성요소는 유명한 Clebsch-Gordan 급수에서 파생된 재귀를 사용하여 기저 함수의 삼중 곱을 통합하여 계산할 수 있습니다. 또한 함수 a의 사전 SH 투영 없이 수치 적분을 사용하여 계산할 수도 있습니다. 곱의 n차 투영에는 최대 2n1차까지 두 요인 함수의 계수가 포함됩니다.

구면 고조파는 구면 좌표계의 함수로 표시되는 구면의 부호 있는 직교 함수 시스템으로, 구면 좌표계 또는 암시적 Cadir 좌표계로 표시될 수 있습니다.

SH 함수는 함수의 근사치를 생성하는 데 사용할 수 있는 신호 조각인 기본 함수입니다.

이러한 계수를 사용하여 원래 신호의 근사치를 재구성할 수 있습니다.

SH 조명의 경우 주로 계수 자체에 직접 작용하는 연산이 사용됩니다.

SH 함수는 서로의 발자국이 겹치지 않는 함수와 같은 특별한 속성을 가진 함수 계열인 직교 기저 함수입니다. 이는 푸리에 변환이 함수를 정현파 성분으로 분해하는 방식과 유사합니다.

  • SH 조명

SH는 물체 표면에 전역 조명 솔루션을 캡처하고 표시하는 효율적인 방법입니다. 이는 동적 조명을 사용하는 정적 모델에 자주 사용됩니다. 렌더링 속도는 광원의 수와 크기에 관계없이 매우 빠릅니다. 무료로 높은 동적 범위 조명. 이는 확산 조명을 임시로 대체하는 것입니다.

우리는 조명 렌더링 방정식이 다음과 같다는 것을 알고 있습니다.

광원과 장애물이 있는 다음 장면이 있다고 가정합니다.

조명용 구형 신호로서 상황은 다음과 같습니다.

H(s)와 V(s)는 전달 함수 T(s)로 결합될 수 있습니다.

광원과 전달 함수를 모두 SH 계수의 벡터로 표현하면 조명 적분은 다음과 같습니다.

Ip=sL(s)Tp(s)ds

계수 사이의 내적으로 계산할 수 있습니다.

Ip=LTp

즉, 조명 계산은 광원의 수나 크기와 무관하며, 부드러운 그림자는 하드 가장자리 그림자보다 비용이 저렴하고, 전달 함수는 오프라인으로 계산할 수 있습니다. 복잡한 조명 기능의 경우 추가 비용 없이 HDR 라이트 프로브를 대신 사용할 수 있습니다.

보조 조명(간접 조명)의 경우 조명 점을 사용하여 확산 간 색상 번짐을 캡처할 수도 있습니다.

Diffuse GI의 장점: SH 계수는 전역 조명 솔루션에서 에너지를 전달하는 완벽한 방법입니다. 직접 조명이 계산된 후에는 자체 전송을 위해 추가 레이 트레이싱이 필요하지 않습니다. 확산 GI의 단점: 멀리 있는 모든 지점은 동일한 조명 기능을 갖는 것으로 가정됩니다(예: 객체의 절반 이상이 그림자가 없음).

SH는 광원이 무한대에 있다고 가정하므로 광원은 p점과 차단기 사이에 올 수 없습니다.

조명 기능을 SH 투영하려면 먼저 구의 임의 지점에서 SH 기능을 계산한 다음 조명 기능과 SH 값의 곱을 합산합니다.

샘플이 고르게 분포되어 있는지 확인할 수 있으면 가중치를 합계 밖으로 이동할 수 있습니다.

렌더링 방정식의 단순화된 확산 버전:

코사인 항은 물리적으로 정확한 렌더링에 필수적이며 에너지 전달 공식에서 파생될 수 있습니다.

렌더링 방정식을 두 부분으로 변환하면 SH 투영에 대한 전달 함수를 얻습니다. 전달 함수는 반사도, 표면 노멀 및 그림자를 단일 함수로 인코딩하므로 각 정점에 대한 표면 노멀을 저장할 필요가 없습니다.

아래 코드는 모델의 각 정점에 대한 SH 계수를 계산하는 간단한 레이 트레이싱기인 SH 전처리기입니다.

cpp
for(int i=0; i<n_samples; ++i) 
{
    double H = DotProduct(sample[i].vec, normal);
    if(H > 0.0) 
    {
        if(!self_shadow(pos,sample[i].vec)) 
        {
            for(int j=0; j<n_coeff; ++j) 
            {
                value = H * sample[i].coeff[j];
                result[j] += albedo * value;
            }
        }
    }
}

const double factor = 4.0*PI / n_samples;
for(i=0; i<n_coeff; ++i)
    coeff[i] = result[i] * factor;

위의 빛 추적에 문제가 있습니다. 그림자 테스트는 일반적으로 모델 내부에서 광선을 방출합니다. 단면 광선-삼각형 교차점에 주의하세요. 구멍이 없는 매니폴드 모델이 선호됩니다.

렌더링 결과는 다음과 같습니다.

이를 바탕으로 더 나은 결과를 얻을 수 있는 방법은 자체 전송을 추가하는 것입니다. 전체 렌더링 방정식은 조명된 표면이 어떻게 서로를 비추고 색상 번짐을 생성하는지 설명합니다.

자체 전송 다이어그램은 다음과 같습니다. A 지점은 코사인 항에 비례하는 B 지점으로부터 조명을 받습니다.

이는 다음과 같이 그래픽으로 표현될 수 있습니다.

A 지점은 기술적으로 그 방향이 보이지 않더라도 위에서부터 빛을 받습니다. SH 조명을 사용하면 빛 전송이 일련의 곱셈 덧셈에 불과합니다. 렌더링 결과:

SH 계수는 런타임에 사용됩니다. 달성된 목표에 따라 SH 계수를 사용하여 이미지를 재구성하는 방법에는 단색 조명 또는 컬러 조명, 다시 칠할 수 있는 표면, 고정 색상 또는 자체 전송 등 다양한 방법이 있습니다.

cpp
for(int j=0; j<n_coeff; ++j) 
{
    vertex[i].red += light[j] * vertex[i].sh_red[j];
    vertex[i].green += light[j] * vertex[i].sh_green[j];
    vertex[i].blue += light[j] * vertex[i].sh_blue[j];
}

조명 함수의 정확도는 순서에 따라 영향을 받으며, 더 높은 주파수 신호를 인코딩하는 계수가 많아집니다.

또는 SH 조명을 조명 계산의 일부로 사용할 수 있습니다. 하늘 구에 SH 조명을 사용하고 포인트 라이트와 하드 그림자를 추가하여 태양을 시뮬레이션하고 SH 조명을 표면 반사의 확산 부분에 사용할 수 있습니다. SH 광원을 생성하는 방법은 다음과 같습니다.

  • 극좌표 함수의 숫자 값입니다.

  • 레이 트레이싱 다각형 모델 또는 장면.

  • HDR 라이트 프로브 또는 환경 맵에서.

  • 분석 솔루션에서 직접. 예를 들어 디스크 광원 각도 t에 대한 솔루션은 다음과 같습니다.

디스크 광원 분석: Maple 또는 Mathematica의 구에서 이 디스크 조명 기능의 기호 통합을 수행하여 분석 표현을 찾습니다.

5차 디스크 라이트의 25개 계수 중 4개만이 0이 아니며 SH 회전은 최종적으로 광원 위치를 지정하는 데 사용됩니다.

프록시 그림자는 객체 사이의 그림자를 가짜로 만드는 방법입니다. 장면의 각 객체에 고유한 조명 기능이 있는 경우 분석 "차단기"를 사용하여 다른 객체의 방향에서 빛을 뺄 수 있습니다. $b_t(s) $를 1dt(s)로 정의하면 SH 계수를 마스킹하는 전달 행렬을 구성할 수 있습니다.

SH 조명의 미해결 문제:

  • 더 빠른 SH 회전 방식. 전산화학 연구로부터 도출.

  • SH는 비정적 물체를 조명합니다. 객체가 서로 상대적으로 이동하면 가시성 함수 V(s)가 근본적으로 변경됩니다. 어떻게 인코딩하나요?

  • SH 벡터의 희소성을 활용하세요. SH 벡터에는 일반적으로 0이 아닌 계수가 거의 포함되지 않습니다.

  • 고광택 미러 SH 조명. 임의의 BRDF를 인코딩하고 사용하는 우아한 방법이지만 일반적인 경우에는 여전히 너무 느립니다.

요약하자면, SH 조명은 3D 모델 조명을 위한 새로운 기술로, 영역 조명과 전역 조명을 실시간 게임에 제공하며 Gouraud 셰이딩을 수행할 수 있는 모든 플랫폼에 적합하고 정적 장면에서 확산 조명을 대체하는 데 사용할 수 있으며 2차 그림자는 4개의 계수만 사용합니다.

또한 SH 기반 PRT(Precomputed Radiance Transfer)는 디퓨즈 자체 전송과 광택 반사 자체 전송을 지원합니다. 계산 과정은 아래 그림과 같습니다.

*자체 전송 런타임 개요. 빨간색은 SH 계수의 양수 값을 나타내고 파란색은 SH 계수의 음수 값을 나타냅니다. 확산 표면(맨 위 행)의 경우 SH 조명 계수(왼쪽)에 표면(가운데)의 투과 벡터 필드를 곱하여 최종 결과(오른쪽)를 생성합니다. 표면의 특정 지점에서의 투과 벡터는 자체 그림자 및 자체 반사와 같은 전역 투과 효과를 포함하여 표면이 해당 지점에서 입사광에 반응하는 방식을 나타냅니다. 매끄러운 표면(하단 행)의 경우 모델의 각 지점(벡터 대신)에는 조명 계수를 투과된 방사선을 나타내는 구형 함수 계수로 변환하는 행렬이 있습니다. 결과는 모델의 BRDF 커널과 컨볼루션되고 뷰 종속 반사 방향 R에서 평가되어 모델의 특정 지점에서 조명 결과를 생성합니다. *

첨부된 내용은 전체 PRT 구현 코드입니다.

cpp
// 3D벡터
struct Vector3
{
    float x;
    float y;
    float z;
};
//
struct Spherical
{
    float theta;
    float phi;
}
//
struct Sample
{
    Spherical spherical_coord;
    Vector3 cartesian_coord;
    float* sh_functions;
};
// 샘플러(Sampler)
struct Sampler
{
    Sample* samples;
    int number_of_samples;
};

// 페치/가져오기(Fetch)
void GenerateSamples(Sampler* sampler, int N)
{
    Sample* samples = new Sample [N*N];
    sampler->samples = samples;
    sampler->number_of_samples = N*N;
    for (int i = 0; i < N; i++)
    {
        for (int j = 0; j < N; j++)
        {
            float a = ((float) i) + Random()) / (float) N;
            float b = ((float) j) + Random()) / (float) N;
            float theta = 2*acos(sqrt(1-a));
            float phi = 2*PI*b;
            float x = sin(theta)*cos(phi);
            float y = sin(theta)*sin(phi);
            float z = cos(theta);
            int k = i*N + j;
            sampler->samples[k].spherical_coord.theta = theta;
            sampler->samples[k].spherical_coord.phi = phi;
            sampler->samples[k].cartesian_coord.x = x;
            sampler->samples[k].cartesian_coord.y = y;
            sampler->samples[k].cartesian_coord.z = z;
            sampler->samples[k].sh_functions = NULL;
        }
    }
};

// 페치/가져오기(Fetch)랜덤/난수
float Random()
{
    float random = (float) (rand() % 1000) / 1000.0f;
    return(random);
}

//
float Legendre(int l, int m, float x)
{
    float result;
    if (l == m+1)
        result = x*(2*m + 1)*Legendre(m, m);
    else if (l == m)
        result = pow(-1, m)*DoubleFactorial(2*m–1)*pow((1–x*x), m/2);
    else
        result = (x*(2*l–1)*Legendre(l-1, m) - (l+m–1)*Legendre(l-2, m))/(l-m);
    
    return(result);
}

float DoubleFactorial(int n)
{
    if (n <= 1)
        return(1);
    else
        return(n * DoubleFactorial(n-2));
}

// 계산/산출(Calculate)함수
float SphericalHarmonic(int l, int m, float theta, float phi)
{
    float result;
    if (m > 0)
        result = sqrt(2) * K(l, m) * cos(m*phi) * Legendre(l, m, cos(theta));
    else if (m < 0)
        result = sqrt(2) * K(l, m) * sin(-m*phi) * Legendre(l, -m, cos(theta));
    else
        result = K(l, m) * Legendre(l, 0, cos(theta));
    
    return(result);
}

// 계산/산출(Calculate)정규화(Normalize)계수K
float K(int l, int m)
{
    float num = (2*l+1) * factorial(l-abs(m));
    float denom = 4*PI * factorial(l+abs(m));
    float result = sqrt(num/denom);
    return(result);
}

// 계산/산출(Calculate)SH함수 。
void PrecomputeSHFunctions(Sampler* sampler, int bands)
{
    for (int i = 0; i < sampler->number_of_samples; i++)
    {
        float* sh_functions = new float [bands*bands];
        sampler->samples[i].sh_functions = sh_functions;
        float theta = sampler->samples[i].spherical_coord.theta;
        float phi = sampler->samples[i].spherical_coord.phi;
        for (int l = 0; l < bands; l++)
            for (int m = -l; m <= l; m++)
            {
                int j = l*(l+1) + m;
                sh_functions[j] = SphericalHarmonic(l, m, theta, phi);
            }
        }
    }
}

// 컬러
struct Color
{
    float r;
    float g;
    float b;
};

// 라이팅
void LightProbeAccess(Color* color, Image* image, Vector3 direction)
{
    float d = sqrt(direction.x*direction.x + direction.y*direction.y);
    float r = (d == 0) ? 0.0f : (1.0f/PI/2.0f) * acos(direction.z) / d;
    
    float tex_coord [2];
    tex_coord[0] = 0.5f + direction.x * r;
    tex_coord[1] = 0.5f + direction.y * r;
    
    int pixel_coord [2];
    pixel_coord[0] = tex_coord[0] * image.width;
    pixel_coord[1] = tex_coord[1] * image.height;
    
    int pixel_index = pixel_coord[1]*image.width + pixel_coord[0];
    color->r = image.pixel[pixel_index][0];
    color->g = image.pixel[pixel_index][1];
    color->b = image.pixel[pixel_index][2];
}

// 투영/프로젝션 라이팅 함수
void ProjectLightFunction(Color* coeffs, Sampler* sampler, Image* light, int bands)
{
    for (int i = 0; i < bands*bands; i++)
    {
        coeffs[i].r = 0.0f;
        coeffs[i].g = 0.0f;
        coeffs[i].b = 0.0f;
    }
    
    for (int i = 0; i < sampler->number_of_samples; i++)
    {
        Vector3& direction = sampler->samples[i].cartesian_coord;
        for (int j = 0; j < bands*bands; i++)
        {
            Color color;
            LightProbeAccess(&color, light, &direction);
            float sh_function = sampler->samples[i].sh_functions[j];
            coeffs[j].r += (color.r * sh_function);
            coeffs[j].g += (color.g * sh_function);
            coeffs[j].b += (color.b * sh_function);
        }
    }
    
    float weight = 4.0f*PI;
    float scale = weight / sampler->number_of_samples;
    for (int i = 0; i < bands*bands; i++)
    {
        coeffs[i].r *= scale;
        coeffs[i].g *= scale;
        coeffs[i].b *= scale;
    }
}

//
struct Triangle
{
    int a;
    int b;
    int c;
};

//
struct Scene
{
    Vector3* vertices;
    Vector3* normals;
    int* material;
    Triangle* triangles;
    Color* albedo;
    int number_of_vertices;
};

// 투영/프로젝션 그림자(Shadow)의
void ProjectUnshadowed(Color** coeffs, Sampler* sampler, Scene* scene, int bands)
{
    for (int i = 0; i < scene->number_of_vertices; i++)
    {
        for (int j = 0; j < bands*bands; j++)
        {
            coeffs[i][j].r = 0.0f;
            coeffs[i][j].g = 0.0f;
            coeffs[i][j].b = 0.0f;
        }
    }
    
    for (int i = 0; i < scene->number_of_vertices; i++)
    {
        for (int j = 0; j < sampler->number_of_samples; j++)
        {
            Sample& sample = sampler->samples[j];
            float cosine_term = dot(&scene->normals[i], &sample.cartesian_coord);
            for (int k = 0; k < bands*bands; k++)
            {
                float sh_function = sample.sh_functions[k];
                int materia_idx = scene->material[i];
                Color& albedo = scene->albedo[materia_idx];
                coeffs[i][k].r += (albedo.r * sh_function * cosine_term);
                coeffs[i][k].g += (albedo.g * sh_function * cosine_term);
                coeffs[i][k].b += (albedo.b * sh_function * cosine_term);
            }
        }
    }
    
    float weight = 4.0f*PI;
    float scale = weight / sampler->number_of_samples;
    for (int i = 0; i < scene->number_of_vertices; i++)
    {
        for (int j = 0; j < bands*bands; j++)
        {
            coeffs[i][j].r *= scale;
            coeffs[i][j].g *= scale;
            coeffs[i][j].b *= scale;
        }
    }
}

// 광선(Ray) 및 교차(Intersection)
bool RayIntersectsTriangle(Vector3* p, Vector3* d, Vector3* v0, Vector3* v1, Vector3* v2)
{
    float e1 [3] = { v1->x – v0->x, v1->y – v0->y, v1->z – v0->z };
    float e2 [3] = { v2->x – v0->x, v2->y – v0->y, v2->z – v0->z };
    float h [3];
    cross(h, d, e2);
    float a = dot(e1, h);
    if (a > -0.00001f && a < 0.00001f)
        return(false);
    float f = 1.0f / a;
    float s [3] = { p->x – v0->x, p->y – v0->y, p->z – v0->z };
    float u = f * dot(s, h);
    if (u < 0.0f || u > 1.0f)
        return(false);
    float q [3];
    cross(q, s, e1);
    float v = f * dot(d, q);
    if (v < 0.0f || u + v > 1.0f)
        return(false);
    float t = dot(e2, q)*f;
    if (t < 0.0f)
        return(false);
    return(true);
}

// 함수
bool Visibility(Scene* scene, int vertexidx, Vector3* direction)
{
    bool visible (true);
    Vector3& p = scene->vertices[vertexidx];
    for (int i = 0; i < scene->number_of_triangles; i++)
    {
        Triangle& t = scene->triangles[i];
        if ((vertexidx != t.a) && (vertexidx != t.b) && (vertexidx != t.c))
        {
            Vector3& v0 = scene->vertices[t.a];
            Vector3& v1 = scene->vertices[t.b];
            Vector3& v2 = scene->vertices[t.c];
            visible = !RayIntersectsTriangle(&p, direction, &v0, &v1, &v2);
            if (!visible)
                break;
        }
    }
    return(visible);
}

// 투영/프로젝션 그림자(Shadow)의
void ProjectShadowed(Color** coeffs, Sampler* sampler, Scene* scene, int bands)
{
    ...
    for (int i = 0; i < scene->number_of_vertices; i++)
    {
        for (int j = 0; j < sampler->number_of_samples; j++)
        {
            Sample& sample = sampler->samples[j];
            if (Visibility(scene, i, &sample.cartesian_coord))
            {
                float cosine_term = dot(&scene->normals[i], &sample.cartesian_coord);
                for (int k = 0; k < bands*bands; k++)
                {
                    float sh_function = sample.sh_functions[k];
                    int materia_idx = scene->material[i];
                    Color& albedo = scene->albedo[materia_idx];
                    coeffs[i][k].r += (albedo.r * sh_function * cosine_term);
                    coeffs[i][k].g += (albedo.g * sh_function * cosine_term);
                    coeffs[i][k].b += (albedo.b * sh_function * cosine_term);
                }
            }
        }
    }
    ...
}
    
// 렌더링(Render)
void Render(Color* light, Color** coeffs, Scene* scene, int bands)
{
    glBegin(GL_TRIANGLES);
    for (int i = 0; i < scene->number_of_triangles; i++)
    {
        Triangle& t = &scene->triangles[i];
        Vector3& v0 = scene->vertices[t.a];
        Vector3& v1 = scene->vertices[t.b];
        Vector3& v2 = scene->vertices[t.c];
        Color c0 = { 0.0f, 0.0f, 0.0f };
        Color c1 = { 0.0f, 0.0f, 0.0f };
        Color c2 = { 0.0f, 0.0f, 0.0f };
        for (int k = 0; k < bands*bands; k++)
        {
            c0.r += (light[k].r * coeffs[t->a][k].r);
            c0.g += (light[k].g * coeffs[t->a][k].g);
            c0.b += (light[k].b * coeffs[t->a][k].b);
            c1.r += (light[k].r * coeffs[t->b][k].r);
            c1.g += (light[k].g * coeffs[t->b][k].g);
            c1.b += (light[k].b * coeffs[t->b][k].b);
            c2.r += (light[k].r * coeffs[t->c][k].r);
            c2.g += (light[k].g * coeffs[t->c][k].g);
            c2.b += (light[k].b * coeffs[t->c][k].b);
        }
        glColor3f(c0.r, c0.g, c0.b);
        glVertex3f(v0.x, v0.y, v0.z);
        glColor3f(c1.r, c1.g, c1.b);
        glVertex3f(v1.x, v1.y, v1.z);
        glColor3f(c2.r, c2.g, c2.b);
        glVertex3f(v2.x, v2.y, v2.z);
    }
    glEnd();
}

PRT 셰이더 코드가 첨부되어 있습니다.

cpp
// vertex shader
struct app2vertex
{
    float4 f4Position : POSITION;
    float4 f4Color : COLOR;
    float3 vf3Transfer [N*N];
};
struct vertex2fragment
{
    float4 f4ProjPos : POSITION;
    float4 f4Color : COLOR;
};
vertex2fragment VertexShader
(
    app2vertex IN,
    uniform float3 vf3Light [N*N],
    uniform float4x4 mxModelViewProj
)
{
    vertex2fragment OUT;
    OUT.f4ProjPos = mul(mxModelViewProj, IN.f4Position);
    OUT.f4Color = float4(0.0f, 0.0f, 0.0f, 1.0f);
    for (int i = 0; i < N*N; i++)
    {
        OUT.f4Color.r += (IN.vf3Transfer[i].r * vf3Light[i].r);
        OUT.f4Color.g += (IN.vf3Transfer[i].g * vf3Light[i].g);
        OUT.f4Color.b += (IN.vf3Transfer[i].b * vf3Light[i].b);
    }
    return(OUT);
}

// fragment shader
struct fragment2screen
{
    float4 f4Color : COLOR;
};
vertex2fragment PixelShader( vertex2fragment IN )
{
    fragment2screen OUT;
    OUT.f4Color = IN.f4Color;
    return(OUT);
}

14.3.4.2 가상 텍스처

**가상 텍스처(VT, Virtual Texture)**는 캐시 역할을 하는 밉맵 텍스처로, 텍스처 메모리에 부분적으로만 상주하면서 실시간 렌더링을 위해 고해상도 텍스처를 시뮬레이션합니다. 캐싱은 느린 메모리에 상주하는 더 큰 데이터 세트에 빠르게 액세스할 수 있게 해주는 일반적인 기술이며, 여기에 설명된 가상 텍스처는 전통적인 텍스처 매핑을 사용하여 느린 콘텐츠 장치의 데이터를 캐시합니다. 아래 다이어그램은 텍스처 저장을 위한 다양한 하드웨어 장치가 다양한 속도/개수 비율로 분류되는 방식을 보여줍니다.

하드웨어는 속도/갯수 비율에 따라 분류될 수 있습니다. 대용량부터 저용량, 주변기기, 메모리, 비디오 메모리 순으로 속도가 증가합니다. GPU 텍스처 조회 작업은 하나의 텍스처로 제한되며 크기3 제한 또는 높은 대기 시간으로 인해 전체 비디오 메모리에 무작위로 액세스할 수 없습니다. 따라서 그림의 다이어그램에는 "텍스처"와 "비디오 메모리"가 별도의 단위로 나열되어 있습니다.

2000년대 중후반에는 게임 씬, 패키지, 텍스처의 크기가 점점 커지고(8K) 32비트 메모리 주소로는 게임의 모든 데이터를 완전히 수용할 수 없으며, IO 대역폭이 가장 큰 병목 현상으로 인해 전체 데이터를 메모리나 비디오 메모리에 로드하는 것이 비현실적입니다. 제한된 물리적 메모리는 하드 드라이브의 가상 메모리를 사용하여 보상할 수 있습니다. 안타깝게도 이 옵션은 요청이 해결될 때까지 기존 가상 메모리(운영 체제 및 하드웨어 기능)가 차단되기 때문에 실시간 렌더링에는 적합하지 않습니다. 게임 콘솔과 같이 사용 가능한 하드 드라이브가 없는 경우 이러한 상황은 더욱 악화됩니다. 이 문제를 해결하고 빠른 레벨 로드 시간을 얻으려면 최신 엔진에 텍스처 스트리밍이 필요합니다.

가상 텍스처에서 텍스처의 관련 부분만 빠른 메모리에 유지하고 누락된 부분을 느린 메모리에서 비동기적으로 요청하는 것은(하위 밉맵의 내용을 폴백으로 사용하면서) 하위 밉맵을 메모리에 유지해야 하며 해당 부분을 먼저 로드해야 함을 의미합니다. 텍스처를 효율적으로 조회하기 위해 밉맵 텍스처를 합리적인 크기의 블록으로 나누어 일관된 메모리 액세스를 달성합니다. 모든 타일에 고정 크기를 사용하면 캐시 관리가 더 쉬워지고, 일정한 시간 및 메모리 특성으로 모든 타일 작업(예: 읽기 또는 복사)을 더 쉽게 관리할 수 있습니다. CryTek의 구현은 텍스처가 결코 멀지 않고 약간의 앨리어싱이 허용되는 지형 렌더링과 같은 애플리케이션의 경우 타일 크기보다 작은 밉맵을 처리하지 않습니다. 이 문제는 여러 개의 밉맵을 단일 타일로 간단히 압축하여 해결할 수 있습니다. 멀리 있는 개체에 대해 일반 밉맵 텍스처 매핑으로 대체하는 것도 가능하지만 이를 관리하려면 별도의 시스템이 필요합니다.

아래에 표시된 것처럼 일반적인 가상 텍스처는 텍스처 크기에 API 및 그래픽 하드웨어 제한을 적용하지 않고 전처리 중에 일부 소스 이미지 형식에서 생성됩니다. 데이터는 액세스 속도가 느린 모든 장치(예: 하드 드라이브)에 저장되며 가상 텍스처의 사용되지 않는 영역을 삭제하여 메모리를 절약할 수 있습니다. 비디오 메모리(타일 캐시)의 텍스처는 3D 보기를 렌더링하는 데 필요한 타일로 구성됩니다. 가상 텍스처 레이아웃을 효과적으로 재구성하려면 간접적인 정보도 필요합니다. 타일 ​​텍스처 캐시와 간접 텍스처(간접 텍스처)는 모두 동적이며 하나 이상의 뷰에 적용됩니다.

가상 텍스처의 작동 메커니즘에는 느린 장치(예: 하드 디스크)에 저장된 고정 크기 텍스처, 비디오 메모리에 위치한 타일 텍스처 캐시, 뷰 및 타일 텍스처 캐시와 관련된 간접 텍스처가 포함됩니다.

CryTek은 쿼드트리를 사용하여 가상 텍스처를 관리하므로 필요한 모든 작업을 일정한 시간 내에 구현할 수 있습니다. 트리의 상태는 현재 가상 텍스처에서 사용되는 텍스처 타일을 나타냅니다. 모든 노드와 잎은 텍스처 타일과 연결됩니다. 기본 구현에서는 쿼드트리에서 사용 가능한 가장 높은 해상도만 사용됩니다. 일부 나뭇잎이 삭제되면 저해상도 데이터가 대체 자료로 저장되고 점차적으로 고해상도 텍스처 타일로 전환하여 나뭇잎 수준에서만 가상 텍스처를 다듬거나 거칠게 만드는 데 사용할 수도 있습니다. 쿼드트리 외에도 추가 캐싱 전략(예: 가장 최근에 사용된 전략)을 구현해야 합니다.

픽셀 셰이더의 쿼드트리 탐색은 보다 효율적인 필터링되지 않은 단일 텍스처 조회로 대체되었습니다. 간접 텍스처는 메모리가 매우 작을 수 있으며 일관성 있는 텍스처 조회로 인해 대역폭 친화적이기도 합니다. 단일 텍스처 조회를 통해 타일 ​​캐시의 텍스처 좌표를 간단한 수학을 사용하여 일정한 시간에 계산할 수 있습니다. 연관된 타일 캐시 텍스처 좌표는 픽셀 셰이더에서 다음 HLSL 코드를 사용하여 지정된 가상 텍스처 좌표에 대해 계산할 수 있습니다.

hlsl
float4 g_vIndir; // 텍스처(Texture):w, h, 1/w, 1/h
float4 g_Cache;  // tile텍스처(Texture)캐싱(Cache):w, h, 1/w, 1/h
float4 g_CacheMulTilesize; // tile텍스처(Texture)캐싱(Cache)*tilesize:w, h, 1/w, 1/h

 // 샘플러(Sampler)(포인트 )
sampler IndirMap = sampler_state
{
    Texture = <IndirTexture>;
    MipFilter = POINT;
    MinFilter = POINT;
    MagFilter = POINT;
    // MIPMAPLODBIAS = 7; // using mip-mapped indirection texture, 7 for 128x128
};

// 로 의 텍스처(Texture)좌표 계산/산출(Calculate)의 tile캐싱(Cache)텍스처(Texture)좌표
float2 AdjustTexCoordforAT( float2 vTexIn )
{
    float fHalf = 0.5f; // half texel for DX9, 0 for DX10
    float2 TileIntFrac = vTexIn*g_vIndir.xy;
    float2 TileFrac = frac(TileIntFrac)*g_vIndir.zw;
    float2 TileInt = vTexIn - TileFrac;
    float4 vTiledTextureData = tex2D(IndirMap,TileInt+fHalf*g_vIndir.zw);
    float2 vScale = vTiledTextureData.bb;
    float2 vOffset = vTiledTextureData.rg;
    float2 vWithinTile = frac( TileIntFrac * vScale );

    return vOffset + vWithinTile*g_CacheMulTilesize.zw + fHalf*g_Cache.zw;
}

앞서 언급했듯이, 간접 텍스처가 하나만 있고 타일 캐시의 모든 타일이 동일한 크기라고 가정하면 가상 텍스처 해상도를 계산할 수 있습니다.

해상도가상 텍스처=해상도간접 텍스처 × 해상도경계가 없는 텍스처 타일

다음은 몇 가지 예입니다.

16k=128×12865k=256×256256k=256×1024

DXT와 같은 블록 압축 텍스처 사용으로 인한 이중선형 필터링 아티팩트를 방지하려면 추가로 4픽셀 테두리가 필요합니다. 손실이 있는 DXT 압축으로 인해 인접한 타일을 압축할 때 동일한 블록 내용이 필요합니다. 그렇지 않으면 잘못된 색상 값이 재구성되어 이음새가 보일 수 있습니다. 타일 캐시 텍스처를 업데이트하기 위한 요구 사항은 다음과 같습니다.

  • 낮은 대기 시간과 높은 처리량.

  • 대역폭 효율적입니다(필요한 부분만 복사됩니다).

  • 작은 메모리 오버헤드.

  • 업데이트 지연이 없어야 하며, 텍스처 상태가 올바르게 동기화되어야 합니다.

  • 콘텐츠가 CPU를 통해 업데이트될 때 GPU 메모리에서 CPU 메모리로 복사하면 안 됩니다(discard를 사용해야 합니다).

  • 타일 캐시 텍스처의 빠른 텍스처링을 위해서는 적절한 메모리 레이아웃(swizzled12)과 메모리 유형(비디오 메모리)이 있어야 합니다. 일부 하드웨어에서는 압축된 텍스처가 선형 형식(swizzle 없이)으로 저장된다는 점에 유의하세요.

  • 여러 타일 업데이트는 선형적이거나 더 나은 성능을 가져야 합니다.

당시 타일 캐시 텍스처 업데이트를 구현하는 방법에는 세 가지가 있었습니다.

  • 방법 1: CPU를 직접 업데이트합니다. 대상 텍스처는 D3DPOOL_MANGED에 있어야 하며 텍스처의 일부는 LockRect() 함수를 통해 업데이트되어야 합니다. 이 접근 방식은 주 메모리를 낭비하고 그리기 호출에서 텍스처를 사용할 때까지 전송을 지연시킬 가능성이 높습니다. 간단하지만 이상적이지는 않습니다.

  • 방법 2: (작은) 중간 타일 텍스처를 사용합니다. 이 방법을 사용하려면 LockRect() 또는 StretchRect() 함수를 사용하여 콘텐츠를 복사하고 타일(테두리 포함)을 유지하기 위한 잠글 수 있는 중간 텍스처 모자가 필요합니다. 이 방법은 압축 텍스처(DXT)에서는 렌더링 대상 형식으로 사용할 수 없고 그래픽 API 및 하드웨어 특성의 영향을 받기 때문에 작동하지 않습니다.

  • 방법 3: (대형) 중간 타일을 사용하여 텍스처를 캐시합니다. 이 방법에는 D3DPOOL_SYSTEM에 전체 텍스처 캐시 확장이 포함된 잠글 수 있는 중간 텍스처가 필요합니다. LockRect()를 사용하면 중간 텍스처는 필요할 때만 업데이트되며 후속 UpdateTexture() 함수 호출은 데이터를 대상 텍스처로 전송합니다. UpdateTexture()에서는 대상이 D3DPOOL_DEFAULT에 있어야 합니다.

새 타일이 텍스처 캐시로 업데이트되면 간접 텍스처도 업데이트될 수 있습니다. 간접 텍스처는 메모리가 거의 필요하지 않으므로 대역폭은 문제가 되지 않습니다. 그러나 여러 간접 텍스처를 사용하고 업데이트하면 성능 병목 현상이 발생할 수 있으며 새 텍스처를 잠그거나 업로드하여 CPU에서 텍스처를 업데이트할 수 있습니다. 간접 텍스처가 렌더 대상 텍스처 형식을 사용하는 경우 CPU에 의해 트리거되는 GPU 업데이트를 고려할 수도 있고, 업데이트는 그리기 호출을 통해 수행되며, 간단한 쿼드를 텍스처에 렌더링하여 넓은 영역을 효율적으로 업데이트할 수 있습니다. 타일 ​​캐시가 업데이트되고 두 캐시가 서로 의존하는 경우는 거의 없으므로 업데이트가 너무 많아서는 안 됩니다.

CryTek 구현에서 간접 텍스처에는 여전히 채널이 있으며 타일 혼합 값을 저장할 수 있습니다. 추가 셰이더 비용을 사용하여 타일을 천천히 혼합하여 텍스처 타일 교체를 숨길 수 있습니다. 필터링된 블렌드 값은 타일 사이의 이음새를 숨기기 때문에 더 좋습니다.

타일 ​​리소스의 소스에는 디스크 스트리밍, 네트워크를 통한 스트리밍, 프로그램 콘텐츠 생성이 포함됩니다. 절차적 콘텐츠 생성은 CPU 또는 GPU에서 발생할 수 있으며, 일반적으로 많은 게임 지형에서 사용되는 대규모 텍스처 생성으로 표현됩니다. 지형 세부 정보는 일반적으로 훨씬 낮은 해상도에서 보간 정보와 혼합된 몇 개의 타일 텍스처로 구성됩니다. 이는 특히 저해상도 텍스처와 결합하여 변형을 추가하고 타일 모양을 깨뜨릴 때 좋은 결과를 얻을 수 있습니다.

Crysis는 지형 재료 혼합(아래)을 사용하여 거대한 지형에 대한 자세한 지상 텍스처를 얻습니다. 이를 위해서는 픽셀 수준에서 여러 재료를 혼합해야 합니다. 각 지형 정점은 재료에 할당되므로(최대 3개, 삼각형 내에서 혼합하려면 최대 3개의 패스가 필요함) 오프라인 프로세스에서 표면 텍스처를 베이킹하면 더욱 복잡한 혼합이 가능하고 렌더링 성능을 유지할 수 있을 뿐만 아니라 도로나 타이어 트랙과 같은 세부 정보도 베이킹할 수 있습니다.

가상 텍스처 사용으로 이점을 얻을 수 있는 장면의 예: Crysis 게임의 데칼(도로, 타이어 자국, 흙)은 지형 재료 혼합 위에 사용됩니다.

CryTek과 약간 다른 id Tech 5는 희소 텍스처 피라미드 쿼드트리(아래)를 사용하여 가상 텍스처를 저장, 관리 및 최적화합니다.

id Tech 5는 희소 텍스처 피라미드의 쿼드트리를 사용하여 가상 텍스처를 관리합니다.

가상 텍스처 시각화.

id Tech 5는 이를 구현할 때 다음과 같은 가상 텍스처 문제에 주의를 기울였습니다.

  • **텍스처 필터링.**필터링을 시도하지 않았습니다. 경계 없는 이중선형 필터링을 시도했습니다. 경계선이 있는 이중선형 필터링은 잘 작동하고, 삼선형 필터링은 합리적이지만 여전히 비용이 많이 듭니다. 이방성 필터링은 TXD(texgrad)를 통해 사용할 수 있으며, 4텍셀 경계(최대 aniso=4)가 필요하고, 암시적 그라데이션이 있는 TEX도 작동합니다(일부 하드웨어에서).

  • **물리적 메모리 초과로 인해 충돌이 발생합니다.**때때로 기존 가상 메모리로는 달성할 수 없는 실제 페이지보다 더 많은 물리적 페이지가 필요할 수 있습니다. 가상 텍스처를 사용하면 작업 세트가 적합할 때까지 피드백 LOD 바이어스를 전역적으로 조정할 수 있습니다.

  • **긴 대기 시간 하에서 LOD 전환.**첫 번째 요구 사항과 가용성 사이의 대기 시간이 길어질 수 있습니다. 특히 검색이 100ms를 초과하여 디스크를 읽어야 하는 경우 더욱 그렇습니다. 업스케일된 텍스처가 LOD를 변경할 때 눈에 띄는 점프가 있으며, 삼선형 필터링을 사용하면 디테일 블렌딩이 쉽지만 블렌딩 데이터를 사용하여 물리 페이지를 지속적으로 업데이트할 수 있습니다.

거친 페이지를 즉시 업샘플링한 다음 가능한 경우 더 미세한 데이터를 융합합니다.

가상 텍스처 관리를 위해 피드백 정보 분석을 통해 어떤 페이지가 필요한지 알려주며, 실시간 애플리케이션이므로 차단이 허용되지 않습니다. 캐시는 적중을 처리하고, 미스를 디스패치하여 백그라운드에서 로드하며, 상주 페이지는 디스크 캐시와 독립적으로 관리되며, 물리적 페이지는 가상 텍스처당 쿼드트리로 구성되고, 무료 페이지, LRU 및 잠긴 페이지의 링크된 목록이 구성됩니다. 가상 텍스처의 피드백 분석 중에 우선순위에 해당하는 너비 우선 쿼드트리 시퀀스가 ​​생성됩니다.

가상 메모리의 트랜스코딩에는 확산, 반사, 범프 및 오버레이/알파와 같은 데이터가 포함됩니다. 범프 텍스처에 저장된 하이라이트 블록 스케일링은 일반적으로 2-6k 입력 및 40k 출력에 도달합니다. Map, Unmap 및 Transcode는 모두 텍스처 메모리에 직접 쓸 수 있는 플랫폼에서 병렬로 발생합니다. 다음 다이어그램은 메모리 프로필을 줄이기 위해 블록 또는 행 수준으로의 트랜스코딩 파이프라인입니다.

종속성이 있는 계산 집약적인 복잡한 시스템이지만 모든 다른 플랫폼에서 병렬로 실행되기를 원합니다. 가상 텍스처 파이프라인은 다음과 같습니다.

id Tech 5 id Tech 5의 운영 하위 시스템에는 멀티 코어의 장점을 재사용하고 가상 텍스처의 처리량을 향상시키는 가상 텍스처가 포함되어 있습니다.

14.3.4.3 톤 재현

톤 재현 및 물리적 기반 스펙트럼 렌더링 문헌은 렌더링 파이프라인의 양쪽 끝에서 관련된 두 가지 주요 문제 영역, 즉 실제 렌더링 프로세스 중에 빛을 설명하는 데 사용되는 데이터 구조와 그러한 방사선의 강도를 가치 있는 방식으로 표시하는 문제를 다루고 있습니다.

첫 번째 하위 문제의 관심은 빛의 강도와 표면 반사율을 설명하기 위해 RGB 색상 값을 사용하는 것이 일반적인 업계 관행이라는 사실에서 비롯됩니다. 이는 진정한 사실성을 추구하지 않는 접근 방식의 맥락에서 가능하지만, 자연을 예측하려면 이 접근 방식을 보다 정밀한 물리적 기술로 대체해야 합니다.

두 번째 하위 문제는 이미지 렌더링 방법에 대한 연구가 더 좋고 빠른 방법을 제공했지만 디스플레이 하드웨어의 제한으로 인해 완전한 효과를 반드시 볼 수는 없다는 것입니다. 표준 컴퓨터 모니터의 낮은 동적 범위에서는 지각적으로 정확한 이미지를 생성하기 위해 일종의 매핑이 필요하며, 실제 밝기 강도의 효과를 재현하려는 톤 재현 작업이 필요합니다.

또한 문헌에서는 스펙트럼 이미지 합성 방법에 대한 조사와 정확한 톤 재현의 필요성을 비롯하여 스펙트럼 렌더링 및 톤 재현 기술에 대한 작업을 검토할 뿐만 아니라 물리적으로 올바른 렌더링 및 중요한 톤매핑 알고리즘에 대한 주요 접근 방식에 대한 논의도 포함합니다. 스펙트럼 렌더링 및 톤 재현 기술은 향후 디스플레이 하드웨어 발전의 영향과 함께 고려될 것입니다.

이미지 생성 방법에 대한 연구를 통해 더 좋고 빠른 방법이 제공되지만 디스플레이 제한으로 인해 이러한 기술의 전체 효과를 볼 수 없는 경우가 많습니다. 정확한 이미지 분석 및 실제 비교를 위해서는 표시된 이미지가 원본 이미지와 최대한 유사해야 합니다. 예측 이미징이 필요한 상황에서는 시뮬레이션에서 도출된 결론이 올바른지 확인하기 위해 색조 재현이 중요합니다(아래).

이상적인 톤 재현 과정.

이 기사는 또한 이전 톤 재생 방법의 분류 다이어그램을 요약합니다.

톤 재현 방법 차트. 가로좌표는 시간 독립적이고 시간 종속적이며, 세로좌표는 공간적으로 균일하고 공간적으로 가변적입니다.

현재 널리 보급된 톤매핑 기술은 수십 년 전에 많은 선배들에 의해 연구되어 톤 재현 및 톤매핑의 후속 응용 및 대중화를 위한 기반을 마련했음을 알 수 있습니다.

14.3.4.4 프로그레시브 버퍼

**PB(Progressive Buffer)**는 대규모 다각형 모델을 렌더링하는 데 사용되는 데이터 구조 및 시스템입니다. 텍스처, 노멀 맵 및 LOD 간의 부드러운 전환(점프 없음)을 지원합니다. 프로그레시브 버퍼에는 모델 전처리, 모델을 클러스터로 분할, 클러스터 및 샘플 텍스처 매개변수화, 서로 다른 LOD에 대한 다중(예: 5개) 정적 버텍스/인덱스 버퍼 생성(각 버퍼는 상위의 1/4을 가짐)이 필요합니다. 이를 수행하려면 한 LOD에서 다음 LOD까지 한 번에 각 차트를 단순화하고, 이웃에 대한 경계 정점을 단순화하고, 경계 제약 조건을 준수하도록 단순화하고 텍스처 뒤집기를 방지하고 버텍스 캐시 최적화도 수행해야 합니다. 각 버퍼에.

프로그레시브 버퍼 예: 빨간색(최고 해상도)에서 녹색(최저 해상도)까지 5가지 세부 색상 코딩 수준.

샘플링 부족 문제를 해결하려면 그리드의 클러스터를 텍스처 매개변수화하고 계층적 알고리즘을 사용하여 텍스처 좌표를 생성해야 합니다. (아래 사진)

거친 클러스터를 처리할 때 클러스터 왜곡을 해결하려면 이전 수준의 미세한 곡선 경계를 선형화해야 합니다.

PB와 관련된 텍스처 패킹 계산에 대해서는 Tetris 패킹 [Levy 02] 및 다중 차트 기하학 이미지를 참조하십시오. 낭비되는 공간(아래 그림의 검은색)을 최소화하려면 차트를 한 번에 하나씩 배치하고(큰 것부터 작은 것까지) 최적의 위치와 회전을 선택하고(낭비되는 공간 최소화) 여러 정사각형 크기에 대해 위 작업을 반복한 후 최적의 레이아웃을 선택합니다.

A: 테트리스 패킹 알고리즘은 차트를 하나씩 삽입하여 프로세스(파란색)에서 차트를 "수평"으로 유지하고, 각 차트(녹색)는 아래쪽 수평선(분홍색)과 현재 수평선 사이의 "낭비된 공간"(검은색)을 최소화하는 위치에 삽입된 다음 현재 차트의 위쪽 수평선(빨간색)을 사용하여 수평선을 업데이트합니다. B: 샘플 모델(공룡) 데이터 세트에 대한 결과입니다.

PB의 각 정적 버퍼에는 인덱스 버퍼와 두 개의 버텍스 버퍼가 포함됩니다.

  • 미세한 버텍스 버퍼. 현재 LOD의 정점을 나타냅니다.

  • 대략적인 버텍스 버퍼. 각 정점이 다음 거친 LOD의 미세 버퍼의 "상위" 정점에 해당하도록 정점이 미세 버퍼에 정렬됩니다(참고: 버텍스 복사가 필요함).

PB의 다양한 수준의 LOD 계층 다이어그램은 다음과 같습니다.

런타임 시 정적 버퍼는 버텍스 셰이더로 스트리밍되고(LOD는 클러스터에서 카메라까지의 중심 거리를 기준으로 결정됨) 버텍스 셰이더는 위치, 노멀 및 UV를 원활하게 혼합합니다(혼합 가중치는 카메라로부터의 버텍스 거리를 기준으로 함).

다음 그림을 참조하여 버퍼 천이에 대해 설명합니다. LOD가 감소하면 주황색 PBi이 해당 노란색으로 전환된 다음 PBiPBi1가 교환되고 노란색 PBi1이 해당 녹색으로 전환됩니다. LOD를 늘리면 그 반대가 됩니다.

다음 그림은 LOD 선택 메커니즘과 프로세스는 물론 관련된 다양한 개념과 기호를 보여줍니다.

위 그림은 PB의 LOD 선택 메커니즘, 원리 및 프로세스를 자세히 보여줍니다. 가로 좌표는 카메라와의 거리가 왼쪽에서 오른쪽으로 점차 증가함을 나타내고, 세로 좌표는 LOD 레벨이 아래에서 위로 순차적으로 증가함을 나타냅니다. S는 객체의 가장 높은 LOD를 사용한 거리를 나타내고, r은 객체 경계 상자의 반경을 나타내고, e는 기하학적 전환의 거리를 나타내고, K는 각 LOD에 해당하는 거리 범위를 나타냅니다(예: K, 2K, 4K 등과 같이 LOD가 감소함에 따라 두 배가 됨). 중앙의 아래쪽 곡선 표면은 LOD 간의 부드러운 전환을 나타내며 점프를 숨깁니다. 이 기하학적 천이 방법은 CSM과 유사합니다. LOD 시리즈 및 가중치 계산은 아래 그림에 나와 있습니다.

위 그림에서 d는 객체와 카메라 사이의 거리를 나타내고, i는 LOD 시리즈를 나타내고, de는 LOD를 줄이는 현재 기하학적 전환의 맨 끝을 나타내고, ds는 LOD를 줄이는 현재 기하학적 전환의 가까운 끝을 나타냅니다.

텍스처 LOD는 버텍스 LOD와 유사합니다. 각 세부 수준에도 텍스처가 있습니다. 각각의 두꺼운 LOD는 이전 LOD에 비해 버텍스 수는 1/4, 텍셀 수는 1/4입니다. 기본적으로, 거칠게 만들 때 가장 높은 밉 수준이 제거되고, 얇게 만들 때 하나의 밉 수준이 추가됩니다. 텍스처는 정점처럼 혼합됩니다. 버텍스 변형 가중치는 픽셀 셰이더에 전달되고, 픽셀 셰이더는 두 번의 가져오기(각 LOD에 대해 한 번)를 수행하며, 픽셀 셰이더는 보간 가중치에 따라 결과 색상을 혼합합니다.

대략 버퍼 계층 구조(CBH) 모든 클러스터의 대략적인 LOD를 비디오 메모리의 단일 버텍스/인덱스/텍스처 버퍼에 저장하고 인접한 클러스터가 카메라에서 멀리 떨어져 있을 때 그룹 드로우 콜을 만듭니다.

CBH 텍스처를 처리할 때 가장 거친 LOD의 복셀 텍스처는 다음과 같이 그룹화됩니다.

CBH 텍스처는 항상 비디오 메모리에 저장되고, CBH 버퍼의 텍스처 좌표는 조정되며, 대략적인 정적 버퍼에서 CBH 버퍼로 전환할 때 눈에 띄는 점프가 없습니다.

데이터 구조의 한계: 버텍스 버퍼 크기가 두 배로 증가하고(그러나 데이터의 작은 부분만 비디오 메모리에 상주함), 클러스터 크기는 대략 동일해야 하며(대형 클러스터는 최소 LOD 수준 크기를 제한함), 순수 레이어링 알고리즘보다 그리기 호출이 더 많고(텍스처는 동일한 그리기 호출에서 전환할 수 없습니다. 거친 계층 구조는 이 문제를 부분적으로 해결함), 직선 경계는 텍스처 스트레칭을 유발합니다.

PB는 시스템 메모리, 비디오 메모리, 프레임 속도(매우 안정적이지 않음), 최대 레벨 크기 등에 의해 제한됩니다. ks 값은 위 제한 내에 유지되도록 그에 따라 천천히 조정됩니다(예: 자동 LOD 제어).

메모리 관리를 위해 별도의 스레드를 사용하여 데이터를 로드하고 다음과 같이 카메라까지의 거리에 따라 우선순위를 설정합니다.

그런 다음 각 버퍼의 연속 LOD를 계산하고 정수 부분을 가져와서 정적 버퍼를 가져와 우선 순위 3을 할당할 수 있습니다.

i=floor(log2(dsk+1))

연속 LOD가 다른 정적 버퍼 LOD의 지정된 임계값 내에 있는 경우 그에 따라 해당 버퍼의 우선 순위를 설정합니다.

렌더링되는 것보다 추가 데이터의 약 20%를 미리 가져오고 유지함으로써 렌더링에 필요한 적절한 클러스터의 LOD를 확보할 수 있습니다. 프리페치가 없으면 여러 버퍼를 사용할 수 없게 될 수 있습니다. 하드 디스크 탐색 시간, 백그라운드 작업, 기타 CPU 사용량에 따라 크게 달라질 수 있습니다.

아래 그림의 통계를 보면 가변 LOD가 고정 LOD보다 FPS와 메모리 측면에서 더 안정적인 성능을 보인다는 것을 알 수 있습니다.

14.3.4.5 기타

2000년대에 등장한 기술은 셀 수 없이 많다. 위의 내용은 설명해야 할 더 중요한 내용 중 일부에 불과합니다. 또한 Deferred Shading 및 Variant, SSAO, LPV 등의 빛과 그림자 기술은 물론 다양한 포스트 프로세스, 다층 재질, 다양한 텍스처 매핑 및 기타 특수 렌더링 기술이 등장하여 게임 엔진 및 퍼블리싱 게임에 적용되었습니다. 예를 들어, YARE 엔진은 점 광원, 스포트라이트, 방향성 조명은 물론 텍스처 매핑, 디스커버리 매핑, 병렬 매핑(Parallax Mapping), 릴리프 매핑(Relief Mapping), 변위 매핑(Displacement Mapping), Render to Cubemap(Render to Cubemap), 동적 큐브 맵 등을 포함한 많은 기본 렌더링 기술의 구현과 Bloom과 같은 일부 포스트 프로세스 파이프라인를 언급합니다.

YARE 엔진은 Bloom 효과 채널을 구현합니다. 왼쪽부터: 원본 이미지, 다운샘플링된 이미지, 수평으로 블러링된 이미지, 완전히 블러링된 이미지, 최종 이미지.

Half Life는 장면 변화에 따라 텍스처를 변경하는 기술을 사용합니다. 사진의 눈을 예로 들면 다섯 가지 눈 효과를 볼 수 있습니다.

14.3.5 성장기간 요약

2000년대 렌더링 엔진의 발전은 다음과 같이 요약된다.

  • 시각 효과가 개선되었습니다.

  • 그래픽 API 및 하드웨어 개발.

  • 멀티스레딩 및 동시성 기술.

  • 크로스 플랫폼.

  • 엔진 기능은 점점 더 복잡해지고, 모듈도 빠르게 성장하고 있습니다.

  • 이 글은 아직 끝나지 않았으며 계속됩니다.

특별 지침

  • 모든 참고문헌의 저자에게 감사드립니다. 일부 사진은 참고 자료와 인터넷에서 가져온 것이므로 삭제되었습니다.

  • 이 시리즈의 기사는 저자가 직접 작성한 것이며 블로그에만 게시됩니다. 이 글의 링크를 공유하셔도 좋지만 무단 전재는 허용되지 않습니다!

  • 계속되는 일련의 기사. 전체 목록을 보려면 콘텐츠 개요를 클릭하세요.

  • 계속되는 일련의 기사. 전체 목록을 보려면 콘텐츠 개요를 클릭하세요.

  • 계속되는 일련의 기사. 전체 목록을 보려면 콘텐츠 개요를 클릭하세요.








📚 참고 자료 및 공식 링크 (References)


🔗 연관 지식 베이스 (Wiki & Tools)

Based on the legendary technical series by Timlly (0向往0 / 毛星云)