Skip to content

언리얼 렌더링 시스템 분석(15)-XR 특별주제

🌐 원문 링크: 剖析虚幻渲染体系(15)- XR专题 (cnblogs.com/timlly)
📅 원문 발행일: 2022-06-09 | ✍️ 저자: Timlly (00 / )

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


가상 현실(VR)은 1960년대 Ivan Sutherland의 작업으로 널리 알려져 있습니다. 지난 50년 동안 가상 현실 기술의 인기는 변동을 거듭했습니다. 특히 최근 몇 년간 가상현실과 그에 대응하는 증강현실(AR)에 대한 투자가 많은 기술 대기업들의 관심을 끌었습니다. 예를 들어, 2014년 페이스북은 가상 현실 기술 회사인 오큘러스(Oculus)를 20억 달러에 인수하고 가상 현실을 새로운 시대로 촉진하기 시작했습니다. 오늘날 Apple, Google, Sony, Samsung을 포함한 모든 주요 기업은 가상 현실에 접근 가능하고 저렴하게 만들기 위해 이 분야에 막대한 투자를 하고 있습니다.

15.1.1 이 글의 내용

본 글에서는 주로 다음과 같은 XR의 내용을 설명합니다.

  • XR 개요

  • XR 기술

  • XR 엔진 통합

  • XR 생태계

15.1.2 XR 개념

XR과 관련된 개념과 관계는 다음과 같습니다.

AR과 VR의 기술적 비교는 다음과 같습니다.

15.1.2.1VR

가상 현실이란 무엇입니까? 전자 세계를 현실적이고 인터랙티브하게 보이게 하세요. 영화가 아닌 정적이 아닌 3D 이미지가 3D 세계에서 움직일 수 있고, 3D 세계의 개체를 조작할 수 있습니다. 가상 현실 경험에는 몰입형 공간과 몰입형 경험이라는 두 가지 모드가 있습니다. 그 중 몰입형 공간은 360도 파노라마 이미지/비디오, 높은 시각적 품질, 제한된 상호작용성, 시점 방향 변경, 사용자가 고개를 돌려 다른 뷰를 볼 수 있음, 위치 고정 등의 특징을 가지고 있습니다. 몰입형 경험은 3차원 그래픽, 낮은 시각적 품질, 높은 상호작용성, 공간 이동 및 가상 객체와의 상호작용이 특징입니다. VR 장비에는 PC 기반과 모바일 기반의 두 가지 방식이 있습니다.

가상 현실 하드웨어에는 머리 장착형 디스플레이, 동작 추적, 머리 추적 및 컨트롤러와 같은 하드웨어 구성 요소가 포함됩니다.

초기 VR 장비에는 Desktop VR, Stereoscopy, Head-Coupled Perspective(HCP), Head-Mounted Display(HMD) 등과 같은 여러 모드가 포함됩니다. 이들의 기술 비교는 다음과 같습니다.

가상 현실 하드웨어에는 PC HMD, 모바일 HMD(올인원), 휴대폰 내장 장치의 세 가지 유형이 있습니다.

PC HMD는 외부 디스플레이 역할을 하는 데스크탑 주변 장치를 말하며, 가장 깊고 몰입도가 높은 가상 현실을 제공하고 위치와 방향을 추적하며 위치 추적을 위해 하나 이상의 케이블을 사용하여 컴퓨터에 연결합니다.

모바일 HMD는 일반적으로 맞춤형 Android 빌드/Oculus 모바일 SDK로 방향 추적 전용이며 곧 출시될 S6 – Samsung Gear VR을 지원하고 LG G5(LG VR), 110 대각선 시야각(Gear VR), 1000hz 재생률(Gear VR)을 지원합니다.

Phone Nest 장치는 모바일 VR을 위한 개방형 사양으로, 방향 추적만 가능하고, 표준 Android, iOS를 지원하며, 간단한 스테레오 렌더링 및 가속도계 추적을 사용하고, 스마트폰만 추가하면 되며, 90도 시야각(판지), 200hz 재생률을 제공합니다.

확장 가능한 3D(X3D) 그래픽은 웹에서 대화형 3D 모델을 게시, 보기, 인쇄 및 보관하기 위한 로열티 없는 개방형 표준입니다. X3D 및 HAnim 표준은 Web3D Alliance에서 개발하고 유지 관리합니다. X3D를 활용한 HMD 가상현실 서비스는 아래와 같습니다.

VR 장비를 측정하기 위한 기본 매개변수로는 3차원 렌더링, 6DOF(위치 및 방향을 포함한 6자유도), 시야(FOV) 등이 있습니다. 다음 표는 2017년경 VR 장비의 기본 매개변수입니다.

VR 장비의 고전적인 대표자인 Quest 2의 매개변수는 다음과 같습니다.

  • 패널 유형: 퀵 스위치 LCD.

  • 디스플레이 해상도: 눈당 1832x1920.

-지원 새로 고침 빈도: 60Hz, 72Hz, 80Hz, 90Hz.

  • 기본 SDK 색 공간: Rec.2020 영역, 2.2 감마, D65 화이트 포인트.

  • USB 인터페이스: USB 3.0 1개.

  • 추적: 내부에서 외부로 6자유도.

  • 오디오: 통합, 내장.

  • SoC: Qualcomm® Snapdragon™ XR2 플랫폼.

  • 메모리: 총 6GB.

  • 렌즈 거리: 조정 가능한 IAD(58, 63, 68mm의 세 가지 설정)

15.1.2.2 AR

**증강 현실(AR)**은 가상 개체를 현실 세계에 오버레이하여 현실 세계와 가상 세계를 원활하게 혼합합니다. 현실 세계를 컴퓨터 시뮬레이션 가상 세계로 대체하는 VR과 달리 AR은 현실 세계에 대한 사람들의 지속적인 인식을 변화시킵니다. Pokémon Go와 Snapchat 필터는 AR의 두 가지 예입니다. AR은 우리가 보는 것과 컴퓨터 생성 정보를 겹쳐서 현실 세계에 대한 시각을 향상시킵니다. 오늘날 이 기술은 사용자가 휴대폰을 앞으로 들고 있어야 하는 스마트폰 AR 앱에서 매우 인기가 높습니다. 카메라에서 이미지를 가져와 실시간으로 처리함으로써 앱은 상황에 맞는 정보를 표시하거나 현실 세계에 뿌리를 둔 것처럼 보이는 게임 및 소셜 경험을 제공할 수 있습니다.

스마트폰 AR은 지난 10년 동안 크게 발전했지만 그 적용 분야는 여전히 제한적입니다. 웨어러블 스마트 글래스를 통해 보다 포괄적인 AR 경험을 제공하려는 관심이 높아지고 있습니다. 이러한 장치는 초저전력 프로세서와 깊이 감지 및 추적을 포함한 여러 센서를 장시간 착용할 수 있을 만큼 가볍고 편안한 폼 팩터 내에 결합해야 합니다.

AR 스마트 안경은 사용자가 움직이는 동안 항상 켜져 있고 직관적이며 안전한 탐색이 필요합니다. 이를 위해서는 깊이, 폐색(3차원 공간의 한 개체가 다른 개체의 보기를 차단하는 경우), 의미, 위치, 방향, 위치, 포즈, 제스처 및 시선 추적과 같은 기능의 주요 발전이 필요합니다.

2021년에는 Snap의 안경 스마트 안경, Lenovo ThinkReality A3, Vuzix의 차세대 스마트 안경 등 새로운 스마트 안경이 많이 출시될 예정입니다. AR 스마트 안경은 아래 영상에서 설명하는 것처럼 미래의 우리 삶을 개선하는 것을 목표로 합니다. 또한 가상 요소와 현실이 교차하는 메타버스에 대한 포털 역할을 할 수도 있습니다.

15.1.2.3MR

**혼합 현실(MR)**은 VR과 AR 기술을 혼합한 것입니다. MR은 현실 세계와 가상 세계를 혼합한 혼합 현실이라고도 불리며, 현실과 디지털 객체가 공존하고 실시간으로 상호 작용할 수 있습니다. AR과 유사하게 MR은 현실 세계 위에 가상 객체를 겹쳐 놓습니다. VR과 마찬가지로 이러한 중첩된 가상 개체는 대화형이므로 사용자가 가상 ​​개체를 조작할 수 있습니다. Microsoft HoloLens는 MR의 좋은 예입니다. MR은 현실과 가상 세계를 혼합하기 때문에 AR과 VR 사이에 위치합니다. 이러한 유형의 XR 기술에는 세 가지 주요 시나리오가 있습니다. 첫 번째는 스마트폰이나 AR 웨어러블을 통해 가상 개체와 캐릭터를 실제 환경에 겹쳐 놓거나 그 반대의 경우도 가능합니다.

2016년 현실 세계의 가상 포켓몬과 스마트폰 카메라를 겹쳐 세계를 휩쓴 모바일 게임 포켓몬고는 혁신적인 AR 게임으로 평가되기도 하지만 실제로는 현실 세계 환경과 컴퓨터 생성 개체를 혼합하는 MR의 좋은 예입니다. 혼합 현실 기술은 또한 실제 VR 플레이어를 비디오 게임에 오버레이하는 데 사용되기 시작하여 Twitch 또는 YouTube와 같은 게임 스트리밍 플랫폼에 실제 개성을 부여합니다.

15.1.2.4 XR

**확장 현실(XR)**은 컴퓨터 텍스트와 그래픽을 실제 환경과 가상 환경에 오버레이하거나 몰입시키거나 두 가지를 조합하여 우리의 세계관을 향상하거나 대체하는 기술을 가리키는 "포괄적" 용어입니다. XR에는 증강 현실(AR), 가상 현실(VR), 혼합 현실(MR)이 포함되며, 이 세 가지 "현실"은 모두 겹치는 특성과 요구 사항을 공유하지만 각각은 서로 다른 목적과 기본 기술을 가지고 있습니다.

XR은 메타버스에서 근본적인 역할을 하도록 설정되었습니다. "인터넷의 차세대 진화"는 현실, 디지털, 가상 세계를 새로운 현실로 통합할 것이며, 이는 VR 장치나 AR 스마트 안경과 같은 Arm 기반 "게이트웨이" 장치를 통해 액세스할 수 있습니다. XR 기술에는 몇 가지 기본적인 유사점이 있습니다. 모든 XR 웨어러블의 핵심 부분은 객체, 제스처, 시선 추적과 같은 시각적 입력 방법을 사용하여 세상을 탐색하고 상황에 맞는 정보를 표시하는 기능입니다. 깊이 인식 및 매핑도 깊이 및 위치 기능을 통해 가능합니다. 그러나 XR 장치는 AR, MR, VR 경험의 유형과 이를 지원하도록 설계된 사용 사례의 복잡성에 따라 다릅니다.

15.1.3 XR 개요

가상 현실의 역사는 1960년대로 거슬러 올라가는 경우가 많으며, 그 개념의 개념은 1938년 이후까지 거슬러 올라갑니다. 즉, 당시의 가상 현실은 지금 우리가 갖고 있는 가상 현실과 매우 달랐습니다. 널리 알려진 최초의 가상 현실 헤드 마운트 디스플레이(HMD)는 컴퓨터 과학자 Ivan Sutherland와 그의 학생 Bob Sproull, Quitin Foster 및 Danny Cohen이 발명한 Sword of Damocles였습니다. VR 시스템은 전체 장치가 사용자에게 너무 무거워서 안전 장치를 착용해야 하며, 이로 인해 다모클레스의 검을 실험실에서만 사용하도록 제한되었습니다.

그 이후로 VR HMD는 진화했습니다. 2019년 페이스북은 독립적인 무선 가상 현실 HMD 오큘러스 퀘스트(Oculus Quest)를 출시했습니다. 사용자는 카메라 기반 위치 추적(Oculus Insight)을 작동하고 사용하기 위해 더 이상 PC나 휴대폰이 필요하지 않습니다. 컴퓨터 장치가 내장된 이 새로운 장치는 가상 현실에 새로운 이동성과 자유의 시대를 선사합니다. Oculus Quest 기기는 일주일 만에 여러 소매점에서 매진되었으며, 처음 2주 만에 VR 콘텐츠 매출이 500만 달러에 이르렀습니다.

2020년에는 AR/VR 지출의 거의 50%가 상업적 사용 사례에 사용되었으며, 교육에 26억 달러, 산업 유지 관리에 9억 1,400만 달러가 지출되었습니다. 소비자 지출은 전체 AR/VR 지출의 1/3을 차지하며, VR 게임과 VR 기능 시청은 각각 33억 달러와 14억 달러를 차지합니다. 2019년부터 2023년까지 전 세계 AR 및 VR 지출 예측과 관련하여 가장 빠른 지출 증가율 상위 3위는 연평균 성장률(CAGR) 190.1%의 대학 연구실 및 현장, CAGR 168.7%의 K-12 연구실 및 현장, CAGR 129.5%의 현장 조립 및 안전입니다. 교육 사용 사례는 2023년에 가장 큰 예측 지출이 될 것입니다.

가상 현실 기술의 개발 잠재력을 고려하여 Apple, Google, Microsoft, Facebook, Samsung, IBM 및 기타 주요 기업은 2020년에 가상 현실 기술에 상당한 투자를 했습니다. 가상 현실에는 확실히 유망한 지지자와 신봉자가 있습니다. 주로 쇼핑객 경험을 향상시켜 상거래에서 가상 현실을 사용하는 것도 추세입니다. 일반적으로 가상 도구는 쇼핑객이 실제 매장에서 상품을 찾을 수 있도록 실제 환경을 강화하는 데 사용됩니다. VR 기술을 통해 쇼핑객은 관심 있는 제품을 맞춤 설정할 수도 있습니다. 더욱 흥미로운 점은 쇼핑객이 이제 매장의 디지털 트윈을 원격 및 가상으로 탐색하고 실제 생활에서와 마찬가지로 제품을 구매할 수 있다는 것입니다.

가상 현실 기술은 게임, 엔터테인먼트 및 체험 활동 산업에서 주요 수요가 발생하는 소비자 시장의 급속한 채택에 힘입어 최근 몇 년간 강력한 글로벌 성장을 경험했습니다. 가상 현실은 우리가 콘텐츠를 소비하는 방식을 변화시켜 사용자에게 더욱 상호 작용적이고 몰입감 있는 경험을 제공합니다. 2022년까지 전 세계 가상현실 산업 규모는 2092억 달러에 이를 것으로 예상된다. AR/VR의 상용 애플리케이션은 진입 비용이 낮아지고 전체 배포의 이점이 더욱 분명해짐에 따라 계속해서 확장될 것입니다. 기술 이점에 대한 논의에서 생산성 및 효율성 향상, 지식 전달, 직원 안전, 더욱 매력적인 고객 경험 등 실제적이고 측정 가능한 비즈니스 결과를 입증하는 것으로 초점이 옮겨지고 있습니다.

XR의 간략한 역사.

좋은 VR 사용자 경험은 매력, 참여, 기억에 남는 순간이라는 세 가지 측면에 반영됩니다. 참여의 의미는 존재감, 연결성, 기억이라는 세 가지 측면을 포함합니다. 이것이 VR을 사용하는 좋은 이유이기도 합니다. 부정적인 예로는 읽기, 입력 및 정밀 작업이 있습니다.

좋은 VR 애플리케이션은 성능과 품질, 배터리 수명, 콘텐츠 출시, 백업, 물류 등의 요구 사항을 충족하는 올바른 기술을 선택해야 합니다.

또한 다음과 같은 VR 모범 사례를 따라야 합니다.

게임플레이를 짧고 명확하게 유지하세요.

아직 VR에 대해 잘 모르고 초보자인 사용자가 많기 때문에 시뮬레이션된 부정적인 효과를 최대한 현실감 있게 만들기 위해 테스트를 거쳐 연령과 시력에 걸쳐 테스트해야 합니다. 사용자 경험에 대한 명확한 예가 없습니다. 모바일 VR의 경우 새로운 하드웨어 개발로 인해 사용자가 오래된 하드웨어를 사용하고 있어 지연이 현기증이 나고 기기, 모델, 버전에 따라 차이가 있습니다. 룸 스케일 VR의 경우 스트레스 테스트, GPU 간 테스트, 스토리지 및 메모리 테스트 등을 수행합니다.

모든 사람이 VR을 처음 접하고 인내심을 갖고 친절해야 하며, 무슨 일이 일어날지 설명하고, 어떻게 작동하는지 알려주고, 잠시 동안 함께 있어주고, 진행 상황을 모니터링하고, 피드백을 받고, 문자 메시지 알림을 받고, 전환을 제안해야 한다고 가정합니다. VR은 즉각적으로 직관적이지 않으며 안내가 필요합니다. 브랜드가 주의해야 할 10가지 VR 팁:

VR(현기증)의 부정적인 영향을 최소화하려면 빠른 프레임 속도(90+가 가장 좋음), 머리가 움직일 때의 최소 대기 시간(20ms 이하), 모든 시각적 신호의 올바른 획득, 가속 최소화, 시야와 전정 시스템의 상호 작용 방식에 기반한 창의적인 솔루션이 필요합니다. 그 중 가속을 피하고 최대한 일정한 속도를 사용하며 즉시 변경하십시오. 곡선이라면 미리 표시하고 궤적을 표시하고 속도를 줄여 원격으로 전송을 시도하되 랜드마크를 표시하고 플레이어가 제어하게 하세요. 고정점의 시야 중심은 2도 시야입니다. 주변 시력은 움직임의 핵심입니다. 빠르게 움직일 때 주변 시야가 흐려지거나 없어집니다.

시선 추적을 통해 포비티드 렌더링이 가능해지며 VR과 궁극적으로 모바일에도 적용됩니다. 핵심은 시각 시스템/뇌가 어떻게 상호 작용하는지 배우는 것입니다. 필요한 최소값은 얼마입니까? VR은 정확하고 좋은 경험을 보장하기 위해 프레임 속도, 머리 추적, 시야, 수렴(Vergence), 3D 렌더링, 원근감, 시차, 거리 안개, 질감, 크기, 폐색 등 많은 요소가 필요합니다. 아래 그림은 디스플레이, 광학 등 VR장비 동향 전망이다.

렌더링 기술 예측은 다음과 같습니다.

VR 경험을 제한하는 요소에는 다음과 같은 측면이 포함됩니다.

떠다니는 아이콘과 인터페이스를 사용하여 존재감을 파괴하는 것을 주의하세요. 인터페이스를 3D 월드 디지털 인터페이스에 넣고 시각 시스템을 연구합니다. 높은 프레임 속도를 유지하려면 적을수록 현실감이 필요하지 않습니다. 2017년부터 2019년까지 VR장비 시장점유율 변화 추이는 다음과 같습니다.

Oculus는 150만 개의 Rift 장치를 판매했으며 100만 달러 이상의 매출을 올린 12개의 OCULUS 게임을 보유하고 있습니다. OCULUS QUEST가 출시되어 399에 판매되었습니다. 경이로운 게임의 대표자는 Beat Saber입니다.

Oculus의 향후 목표는 10억 명의 VR 사용자에게 다가가는 것입니다.

15.1.4 XR 생태계

VR/AR 산업은 하드웨어, 시스템, 플랫폼, 개발 도구, 애플리케이션, 소비자 콘텐츠 등 다양한 측면을 다루고 있습니다. VR/AR 산업은 아직 성숙되지 않은 산업으로, 참여 제조업체(특히 콘텐츠 제공업체)가 상대적으로 적고 투자도 많지 않아 산업 체인이 상대적으로 얇습니다. 핵심 콘텐츠 제작 도구는 360° 파노라마 촬영 카메라 등 주요 R&D 및 제작 병목 현상에 직면해 있으며, 시중에 나와 있는 제품은 소수에 불과합니다.

현재 XR에는 수많은 회사와 브랜드가 관련된 하드웨어, OS, 엔진, 장비 제조 및 기타 산업이 포함됩니다.

최근 몇 년간 XR을 중심으로 투자계가 활발해짐에 따라 2010년쯤에는 스마트 모바일 기기처럼 생태계가 점점 더 완성될 것이라고 믿습니다.

15.1.5 XR 애플리케이션

현재 전 세계는 코로나19 팬데믹으로 인해 위기에 직면해 있습니다. 예를 들어 중국의 재택근무 시나리오에는 새로운 작업 방식이나 협업이 필요합니다. VR은 기존 업무 모델에 대한 또 다른 솔루션을 제공하며 가상 회의, 아바타, 대면 회의 등 사무실 및 커뮤니케이션 요구를 실현할 수 있습니다.

인터넷의 탄생 이후 정보통신과 미디어 기술은 기하급수적으로 성장해 왔으며, 가상현실 기술 혁신이 중요한 역할을 하고 있다. 과거에는 가상 현실이 주로 소비자 게임 영역에 있었습니다. 이러한 추세는 계속되겠지만 VR의 상업적 응용 분야는 급속도로 성장하고 있습니다. 이는 주로 소비자 습관의 변화와 VR 하드웨어 비용의 상당한 감소로 인한 것입니다. VR의 진입 장벽은 불과 수십 년 전과 비교하여 상대적으로 낮아 VR을 실행 가능한 솔루션으로 만들고 다양한 사용 사례 부문을 향상시킵니다.

생산성을 높이고 효율성을 높이며 운영 비용을 절감하기 위해 싱가포르의 많은 기업에서는 교육부터 업무까지 다양한 응용 분야에 최신 가상 현실 기술을 적용하고 있습니다. 반면, 일반 소비자, 특히 젊은 층은 쇼핑, 엔터테인먼트 등 다양한 소비 경험을 향상시키기 위해 가상 현실을 활용하려는 의지가 점점 더 커지고 있습니다.

정부 기관도 VR 기술을 일부 주요 업무에 통합하고 있습니다. 예를 들어, 경찰과 시민 보호 당국은 훈련에 가상 현실 기술을 사용합니다. VR은 일부 정부 건설 프로젝트에 필수 요구 사항입니다. 우리는 커뮤니케이션, 협업 및 조정, 교육, 시각화 분야에서 이러한 애플리케이션에 대해 더 자세히 설명할 것입니다.

XR 응용 분야는 주로 다음 사항에 반영됩니다(단, 이에 국한되지는 않음).

  • **소통, 협업 및 조정.**인터넷을 사용하면 서로 다른 지리적 위치에 있는 사람들과 실시간으로 협업할 수 있습니다. 그러나 가상현실은 새로운 소통과 협업 방식을 제공합니다. 사람들을 서로 더 가깝게 만들어 가상 현실 화상 회의를 위한 몰입형 대화형 경험을 제공합니다. 가상 현실 환경에 몰입하면 사람들은 신체적 방해 없이 회의에 더 집중할 수 있습니다. 연구에 따르면 가상 현실 몰입형 회의는 기존 화상 회의에 비해 관심도를 25% 높일 것으로 예상됩니다. 여행 비용과 실제 회의 공간 측면에서 그 밖에도 상당한 절감 효과가 있습니다. 코로나19 팬데믹과 사회적 거리두기 조치로 인해 많은 사회 및 지역사회 행사와 행사가 완전히 취소되었습니다. 이러한 활동 중 일부는 가상 현실 공간에서 진행될 수 있는 조립식과 같이 참가자에게 매우 중요하며, 졸업생 및 행사 참가자는 휴대폰 및 가상 현실 장치를 사용하여 전 세계의 가상 졸업식에 참여할 수 있습니다.

  • **훈련.**가상 현실은 훈련생이 몰입감 있고 안전한 환경에서 실제 상황을 경험할 수 있도록 해주기 때문에 훈련에 자주 사용됩니다. 기존 실습 교육에는 물리적 장비, 공간 및 운영 중단 시간이 필요한 경우가 많습니다. 어떤 경우에는 훈련생이 준비되지 않은 작업장 위험에 노출될 수도 있습니다. 가상 현실을 사용하면 언제 어디서나 교육을 실시할 수 있어 리소스 비용과 대기 시간이 줄어듭니다. 강사는 또한 가상 현실 환경을 맞춤화하여 다양한 시나리오에서 직원을 교육할 수 있으며, 이를 통해 교육생은 다양한 문제를 해결하기 위해 많은 양의 지식을 습득할 수 있습니다. 예를 들어, 범죄 현장의 최초 대응자는 수많은 시뮬레이션 시나리오를 처리하고 의사 결정 기술을 반복적으로 연마하도록 교육을 받을 수 있습니다. 또한 이 구현을 통해 홈 팀은 물리적 저장소와 공간을 보다 효율적으로 사용할 수 있으므로 물리적 소품, 모델 및 값비싼 공간이 필요한 기존 시나리오 모델에 비해 더 많은 사람들이 더 자주 교육을 받을 수 있습니다.

  • **시각화.**AEC 사업에서 가상 현실은 사용자가 웹 사이트 구축 전에 디자인을 시각화하는 데 도움을 주어 비용을 절감하고 더 나은 결과를 얻을 수 있도록 해줍니다. 2017년부터 2019년까지 가상설계·시공(VDC) 분야에서 VR입찰 요건이 높아졌다. VDC 역량이 없는 기업은 해당 프로젝트 경쟁에서 완전히 배제되지는 않더라도 큰 불이익을 받게 될 것입니다. 건물 설계자는 VR 사용을 시작하기 전에 VR의 이점, 즉 건물을 짓기 전에 상당한 비용을 절감하고 안전 문제를 완화할 수 있는 프로세스를 검증할 수 있습니다.

  • **게임 및 엔터테인먼트.**VR 게임, 영화, TV의 적용은 VR 개발의 주요 원동력 중 하나입니다. 리듬 광선검 등 게임의 인기도 VR 장비의 인기를 끌어올렸다. VR 기술이 게임 산업에서 처음 시작되면서 VR이 게임에 큰 영향을 미칠 것이라는 것은 놀라운 일이 아닙니다. XR은 플레이어에게 매력적인 가상 개체를 제공하고, 게임 환경을 풍부하게 하며, 원격 플레이어가 동일한 게임 환경에서 실시간으로 플레이하고 상호 작용할 수 있도록 하며, 플레이어가 신체 움직임을 통해 게임 내 위치를 변경할 수 있도록 하며, 게임이 2차원 공간에서 3차원 공간으로 이동할 수 있도록 합니다.

  • **스트리밍.**XR은 몰입형 경험을 제공하고 6DoF(자유도) 기능으로 미디어 스트리밍 경험을 향상시킵니다. 이를 통해 사용자는 가상 현실 환경이나 스포츠 이벤트 또는 콘서트에서 이동하고 상호 작용할 수 있습니다.

오늘날의 XR은 10년 전 스마트폰과 마찬가지로 아직 초기 단계에 있으며, XR의 개발에는 수년이 걸리겠지만... 기회는 엄청날 것입니다.

요컨대, 국가가 디지털 경제로 전환함에 따라 가상 현실 기술은 여러 산업에서 널리 채택되고 사용되었습니다. 가상 현실 기술에 대한 수요는 상업 시장과 소비자 시장 모두에서 계속해서 증가할 것입니다. 앞으로 몇 년 안에 가상 현실은 더욱 접근 가능하고 저렴해짐에 따라 모든 중국인의 일상 생활과 회사 운영에서 널리 사용될 것입니다. 이러한 이유로 일부는 VR에서 AR로 전환하기 시작할 수 있으므로 상업 시장에 약간의 변화가 있을 것으로 예상됩니다. 그렇지 않으면 거대 기술 기업들이 기존 VR 애플리케이션을 새롭게 개선하고 몰입형 기술의 기술적 한계를 더욱 높이기 위해 노력하고 있기 때문에 향후 2년은 확실히 VR에 중요한 기간이 될 것입니다.

15.2 XR 기술

15.2.1 XR 기술 개요

데스크탑 **가상 현실(VR)**은 역사적으로 소비자급 3D 컴퓨터 그래픽의 주요 디스플레이 기술이었습니다. 최근에는 입체 시각 및 헤드 마운트 디스플레이와 같은 보다 정교한 기술이 더욱 널리 보급되었습니다. 그러나 대부분의 3D 소프트웨어는 여전히 데스크톱 VR을 지원하도록 설계되었으며 이러한 디스플레이를 기술적으로 지원하고 사용 모범 사례를 따르도록 수정되어야 합니다. 최신 3D 게임/그래픽 엔진을 평가하고 다양한 유형의 저렴한 VR 디스플레이에 출력을 적용하는 정도를 결정하여 입체 비전이 기본적으로 또는 기존 적용을 통해 널리 지원된다는 것을 보여주어야 합니다. 헤드 마운트 디스플레이, 헤드 결합 관점(및 후속 수조 VR)과 같은 기타 VR 기술은 기본적으로 거의 지원되지 않습니다.

2013년 가상현실 디스플레이 기술에는 데스크탑 VR(스트리밍), 입체 비전, 헤드 결합 관점, 헤드 마운트 디스플레이 등이 포함됩니다. 시뮬레이션 모델과 사용자 인식의 차이점은 다음과 같습니다.

입체경은 양안 시야에 적용되는 Tabletop VR 패러다임의 확장입니다. 스테레오스코프는 각 눈에 한 번씩 장면을 두 번 렌더링한 다음 각 이미지가 사용자의 한쪽 눈에만 표시되도록 이미지를 인코딩하고 필터링하여 이를 수행합니다. 이 필터링은 일치하는 디스플레이에서 생성된 두 코드 중 하나를 선택적으로 통과하도록 렌즈가 설계된 특수 안경을 통해 가장 쉽게 달성됩니다. 현재 인코딩 방법은 색상 스펙트럼, 편광, 시간 또는 공간을 통해 이루어집니다. 이러한 인코딩 방법은 수동형, 능동형 또는 무안경 입체형으로 분류되는 경우가 많습니다. 수동 인코딩과 능동 인코딩의 차이는 안경이 전기 활성인지 여부에 따라 다릅니다. 따라서 수동 인코딩 시스템은 색상과 편광인 반면 유일한 능동 인코딩은 시간입니다. 무안경 입체 디스플레이는 공간적으로 인코딩되므로 안경이 필요하지 않은 디스플레이입니다. 즉, 눈 사이의 물리적 거리가 이미지를 필터링하는 데 충분합니다.

소비자 입체 디스플레이는 데스크톱 VR 디스플레이와 동일한 방식으로 컴퓨터와 인터페이스합니다(VGA 또는 DVI와 같은 비디오 인터페이스를 통해). 이러한 인터페이스 대부분에는 특별한 입체 보기 모드가 없기 때문에 두 개의 입체 이미지가 디스플레이 하드웨어에서 인식되는 형식으로 단일 이미지로 압축됩니다. 이러한 프레임 패킹 형식에는 인터레이스, 상단-하단, 병렬, 2D+깊이 및 인터레이스가 포함됩니다. 이러한 표준화된 인터페이스는 소프트웨어가 렌더링된 이미지를 디스플레이 하드웨어에 전달하는 방식이기 때문에 소프트웨어 응용 프로그램은 인코딩 시스템의 디스플레이 하드웨어를 이해하거나 적응할 필요가 없습니다. 대신, 입체적인 관점을 지원하기 위해 그래픽 엔진에 필요한 것은 서로 다른 가상 카메라 위치에서 동일한 시뮬레이션 상태로 두 개의 이미지를 렌더링하고 이를 디스플레이에서 지원하는 프레임 패킹 형식으로 결합하는 기능입니다.

**헤드 결합 관점(HCP)**은 가상 카메라 대신 가상 창을 정의한다는 점에서 데스크톱 VR 및 입체 비전과 약간 다르게 작동하며, 그 경계는 사용자 디스플레이 가장자리에 매핑된 가상 창입니다. 따라서 가상 환경의 객체가 사용자의 눈 방향으로 디스플레이에 투사되므로 디스플레이의 이미지는 사용자 머리의 상대적인 위치에 따라 달라집니다. 이 투영은 데스크톱 VR에 사용되는 투영 수학의 축외 버전을 사용하여 수행할 수 있습니다.

이를 위해서는 디스플레이를 기준으로 한 사용자의 머리 위치를 실시간으로 정확하게 추적해야 합니다. 이러한 목적으로 사용되는 추적 시스템에는 뼈대, 전자기/초음파 추적기 및 이미지 기반 추적이 포함됩니다. HCP의 한 가지 한계는 표시되는 이미지가 사용자의 위치에 따라 달라지기 때문에 동일한 디스플레이를 보는 다른 사용자가 올바른 위치에서 볼 수 없기 때문에 왜곡된 이미지를 인식하게 된다는 것입니다.

**헤드 마운트 디스플레이(HMD)**는 향상된 입체 시야와 넓은 시야 및 HCP와 유사한 헤드 커플링을 결합한 또 다른 단일 사용자 VR 기술입니다. HMD 뒤에 있는 지각 모델은 사용자 눈의 시각적 입력을 완전히 오버레이하고 이를 가상 환경의 포함된 보기로 대체하는 것입니다. 이는 사용자의 눈에 매우 가깝게 하나 또는 두 개의 작은 디스플레이를 장착하는 렌즈 시스템을 통해 달성되어 보다 자연스러운 초점을 맞출 수 있습니다. 디스플레이가 사용자의 눈에 너무 가깝기 때문에 한쪽 눈만 디스플레이의 모든 부분을 볼 수 있어 시스템에 무안경 입체 느낌을 줍니다.

또한 헤드기어에는 사용자 머리의 회전을 추적할 수 있는 방향 추적기가 내장되어 있어 사용자가 가상 ​​카메라의 방향을 사용자의 머리 방향에 바인딩하여 자연스러운 머리 움직임을 사용하여 가상 환경을 둘러볼 수 있습니다. 방향보다는 위치를 추적하는 HCP와 다릅니다. HMD를 지원하기 위한 소프트웨어 요구 사항은 입체 보기와 동일하지만 추가 요구 사항은 그래픽 엔진이 HMD의 방향은 물론 렌즈 시스템으로 인한 왜곡을 수정해야 한다는 것입니다.

지원 수준은 필요한 VR 디스플레이 기술을 구현하는 데 사용할 수 있는 확장 메커니즘을 결정하여 측정됩니다. 이는 무시할 수 있는 차이(예: 스크립트 및 플러그인)가 있는 확장 메커니즘과 결합되었으며 확장이 필요하지 않은(기본 지원) 및 엔진 내 지원이 없는(재설계) 두 가지 추가 수준이 도입되었습니다. 확장 메커니즘은 VR 지원을 구현하는 비엔진 코드에 대한 엔진 코드의 비율에 따라 정렬됩니다. 결과 지원 수준과 순서는 다음과 같습니다.

**5. 기본 지원.**VR 기술을 기본적으로 지원하는 엔진에서 엔진 개발자는 사용자가 최소한의 노력으로 VR 렌더링을 활성화할 수 있도록 특별히 작성된 렌더링 파이프라인을 가지고 있습니다. 필요한 것은 개발자 도구에서 옵션을 확인하거나 엔진의 스크립팅 환경에서 변수를 설정하는 것뿐입니다. 기술을 쉽게 활성화하는 것 외에도 이러한 엔진은 데스크탑 VR 디스플레이에서는 명확하지 않지만 보다 복잡한 기술에서 명백해지는 일반적인 최적화 및 바로 가기를 방지하도록 설계되었습니다. 일반적인 예는 올바른 폐색이지만 깊이가 잘못된 개체를 렌더링하는 것입니다. 이로 인해 입체경 아래에서 깊이 큐가 충돌하게 됩니다.

**4. 엔진 내 그래픽 사용자 정의(노드 그래프 포함)를 통해.**일부 엔진은 그래픽 인터페이스가 있는 사용자 정의 도구를 사용하여 렌더링 프로세스를 변경할 수 있는 방식으로 설계되었습니다. 한 가지 방법은 렌더링 파이프라인의 다양한 구성 요소를 여러 구성에서 재배치, 수정 및 다시 연결할 수 있는 노드 그래프를 이용하는 것입니다. 지원되는 노드 유형에 따라 노드는 때때로 특정 VR 기술의 효과를 생성하도록 구성될 수 있습니다. 아래 이미지는 포스트 프로세스 파이프라인로 빨간색-청록색 입체 렌더링을 사용하도록 구성된 언리얼 엔진의 머티리얼 편집 인터페이스를 보여줍니다.

**3. 엔진 내 코딩(스크립트 또는 플러그인)을 통해.**각 엔진은 잘 정의되어 있지만 제한된 확장 지점을 사용하여 사용자 정의 코드로 확장할 수 있습니다. 두 가지 일반적인 형태는 제한된 환경에서 실행되는 스크립트와 엔진이 외부에서 컴파일된 코드를 로드하고 실행하는 플러그인입니다. 두 가지 양식 모두 엔진 기능의 하위 집합에 액세스할 수 있지만 플러그인은 스크립트가 액세스할 수 없는 외부 API에도 액세스할 수 있습니다. 메커니즘은 애플리케이션 기능에 따라 구현되는 경우가 많기 때문에 사용자 정의 코드에 사용할 수 있는 엔진 기능은 정확한 렌더링 프로세스를 제어하기보다는 인공 지능, 게임 로직 및 이벤트 순서 지정에 더 적합할 수 있습니다.

**2. 엔진 소스 코드를 통해 수정합니다.**무료 오픈 소스 엔진 외에도 일부 상용 엔진은 적절한 라이센스 계약을 통해 사용자에게 완전한 소스 코드를 제공합니다. 전체 소스 코드에 액세스하면 모든 VR 기술을 구현할 수 있지만 필요한 수정량이 상당할 수 있습니다.

**1. 엔지니어링 혁신을 통해.**위의 사용자 정의 진입점을 제공하지 않는 엔진의 경우에도 재설계를 통해 일부 변경이 이루어질 수 있습니다. 엔지니어링 수정은 프로그램의 일부 작동 원리를 학습하는 것 외에도 일부 기능을 수정하는 리버스 엔지니어링의 한 형태입니다. 렌더링 파이프라인을 완전히 리버스 엔지니어링하는 데 필요한 작업량이 상당할 수 있으므로 최소 침습적 형태의 리엔지니어링이 바람직합니다. 그러한 접근 방식 중 하나는 내부 또는 라이브러리 함수에 대한 호출을 가로채서 사용자 지정 동작으로 바꾸는 함수 후킹입니다. 실시간 그래픽 엔진의 상당 부분은 하드웨어 그래픽 가속을 위해 OpenGL 또는 Direct3D 라이브러리를 사용하므로 이러한 라이브러리는 기능 후크를 통해 순수 시각적 VR 기술을 구현하기 위한 안정적인 진입점을 제공합니다. 이 접근 방식은 3D 게임에 입체 시각을 추가하는 데 효과적인 것으로 입증되었습니다. 또한 이 기사에서는 투영 행렬(glFrustum 및 glLoadMatrix)을 로드하는 OpenGL 함수를 연결하고 원래 프로그램에서 제공하는 고정 원근 행렬을 헤드 결합 행렬로 대체함으로써 이러한 방식으로 헤드 결합 원근을 구현하는 것이 가능하다는 것을 보여줍니다.

사용자 경험에 영향을 미치는 요소는 많습니다. 품질 요소는 본질적으로 디스플레이 하드웨어와 관련되어 있지만 적절한 소프트웨어 디자인은 이러한 문제를 완화할 수 있지만 부주의한 디자인은 새로운 문제를 일으킬 수 있습니다. 소프트웨어로 완화할 수 있는 하드웨어 품질 요소의 예로는 누화(스테레오), A/C 결함(스테레오) 및 추적 지연(HCP 및 HMD)이 있습니다. 이러한 요인들은 각각의 디스플레이 기술에 대해 잘 정립되어 있으므로, 이로 인해 발생하는 문제를 최소화하기 위한 기술은 잘 알려져 있습니다. 해결책은 장면 대비를 줄이고, 시차를 줄이고, 렌더링 대기 시간을 최소화하는 것입니다.

잘못된 소프트웨어 구현은 부주의로 인해 또는 데스크톱 VR 최적화로 인해 VR 효과의 품질에 영향을 미칠 수도 있습니다. 이에 대한 예는 하늘, 그림자, 1인칭 플레이어의 신체 등 어디에서나 특수 레이어에 대한 다양한 채널의 깊이입니다. 데스크톱 VR에서 올바른 폐색을 생성하는 동안 입체경 아래에 양안 시차 큐를 추가하면 잘못된 깊이가 표시되고 두 깊이 큐 사이에 충돌이 발생합니다. 데스크톱 VR의 지배적인 특성으로 인해 이는 드문 문제가 아니며 단순한 타사 구현이 기본 VR만큼 지원되지 않을 수 있음을 보여주는 또 다른 예입니다. 이러한 측면에서 비네이티브 VR 구현이 필요한 기술 요구 사항을 충족할 수 있지만 다른 요소도 고려해야 한다는 점에 유의해야 합니다.

다음 표는 2013년 주류 엔진의 VR 지원을 보여줍니다.

엔진 입체시 헤드 결합 전송 헤드마운트 디스플레이

UDK 4: 그래픽 사용자 정의. Unreal Kismet을 사용하여 듀얼 카메라 리그를 생성하고 머티리얼 편집기를 사용하여 출력용으로 패키징할 수 있습니다. 1: 엔지니어링 혁신. 커스텀 카메라 프로젝션은 엔진에서 액세스할 수 없으므로 소스 코드 액세스 없이 엔지니어링이 필요합니다. 3: 엔진 인코딩. 입체화는 사용자 정의를 통해 이루어지며, 머리 방향은 사용자 정의 DLL을 통해 획득하고 스크립트를 통해 카메라에 바인딩할 수 있습니다.

유니티 3: 엔진 인코딩. 3: 엔진 인코딩. 3: 엔진 인코딩.

크라이엔진 5: 네이티브. 3: 엔진 인코딩. 3: 엔진 인코딩.

오우거 3: 엔진 인코딩. 3: 엔진 인코딩. 3: 엔진 인코딩.

가상 현실에서 가장 중요한 요소는 낮은 지속성, 대기 시간, 현실성입니다.

VR 소프트웨어 및 하드웨어 아키텍처는 일반적으로 C/C++ 인터페이스, 드라이버 DLL, VR 서비스 레이어(애플리케이션 간 공유 및 가상 현실 변환) 등 여러 레이어로 구성됩니다. 다음 그림은 Oculus의 초기 아키텍처 다이어그램입니다.

SDK의 일반적인 작업 흐름(Oculus를 예로 들어):

  • ovrHmd_CreateDistortionMesh. UV를 통해 이미지를 변환하는 것은 픽셀 셰이더 렌더링보다 더 효율적이므로 Oculus가 왜곡을 더 유연하게 수정할 수 있습니다.

  • ovrHmd_BeginFrame.

  • ovrHmd_GetEyePoses.

  • EyeRenderPose(게임 장면 렌더링) 기반의 입체 렌더링.

  • ovrHmd_EndFrame.

Oculus SDK는 통합이 쉽고 장치/시스템 포인터와 눈 텍스처를 통해 셰이더와 메시를 만들 필요가 없으며 OpenGL 및 D3D9/10/11을 지원하고 다음 프레임에 렌더링 상태를 다시 적용해야 합니다. 이점: 향후 Oculus 하드웨어 및 기능과의 호환성 향상, 그래픽 카드 설정 오류 감소, 전면 버퍼 렌더링 등과 같은 대기 시간이 짧은 드라이버 디스플레이 액세스 지원, 자동 적용 범위 지원: 지연된 테스트, 카메라 가이드, 디버그 데이터, 관점, 플랫폼 적용 범위. SDK 렌더링을 사용하기 위해 Unreal Engine 3, Unreal Engine 4 및 Unity와 같은 주류 게임 엔진을 지원합니다. 확장 모드 지원: 헤드셋은 OS 디스플레이로 표시되고, 앱은 Rift 모니터에 창을 배치해야 하며, 아이콘과 창이 잘못된 위치에 있고, Windows 컴포지터가 Present를 처리하며, CPU 및 GPU 동기화가 완료되지 않은 경우 일반적으로 최소 1프레임 지연이 발생합니다. 또한, 디스플레이가 데스크탑의 일부가 되지 않고 Rift로 출력되는 Direct To Rift 기능도 지원합니다. 헤드셋은 운영 체제에서 표시되지 않습니다. 창과 아이콘 점프를 방지하고, OS 컴포지터에서 Rift 수직 동기화(v-sync)를 분리하고, 추가 GPU 버퍼링을 방지하고, 대기 시간을 최소화하고, ovrHmd_AttachToWindow를 사용하고, 창 스왑 체인 출력이 Rift로 전달됩니다. 직접 모드가 장기적인 솔루션이 되기를 바랍니다.

VR 개발 시 주의할 점:

  • 플레이어의 머리를 통제하지 마세요!

  • 1인칭 액션에 주의하세요.

  • 사진 사실주의는 필요하지 않습니다.

  • 영화적 렌더링을 사용하지 마세요! 가변 초점, 필터, 렌즈 플레어, 블룸, 필름 그레인, 비네팅, 뎁스 오브 필드(DoF) 등.

스테레오 렌더링 품질 확인:

  • 왼쪽, 오른쪽 방향이 맞나요?

  • 양쪽 눈의 성분이 동일한가요?

  • 두 이미지가 모두 같은 시간을 나타내는가?

  • 저울이 맞나요?

  • 깊이가 일정합니까?

-빠른 깊이 변화를 피했나요?

좋은 가상 현실 엔진은 다음 조건을 충족해야 합니다.

  • **고품질 시각 효과.**고품질의 시각적 요소는 게임 플레이에 방해가 되지 않으며, 좋은 음영 처리(그러나 반드시 사실적일 필요는 없음)를 의미하며 일반적으로 좋은 앤티앨리어싱을 의미합니다. 좋은 앤티앨리어싱이 중요한 이유는 무엇입니까? 인간 인식의 특성상 우리는 고주파 소음으로 인해 쉽게 주의가 산만해지며, 주의가 산만해지면 존재감이 줄어들 수 있고, 입체 렌더링을 사용할 때 앨리어싱 아티팩트가 더 심해질 수 있고, 망막 경쟁을 유발할 수 있으며, 좋은 앤티앨리어싱이 기본 해상도보다 더 중요합니다. 앤티앨리어싱 방법은 다음과 같습니다. 가장자리 기하학 AA, 일반적으로 하드웨어 가속; FXAA, MLAA, SMAA 등과 같은 대부분의 렌더링 파이프라인에 매우 적합한 이미지 공간 AA 시간적 슈퍼샘플링을 위해 재투영을 사용하는 시간적 AA.

  • **일관되게 높은 프레임 속도.**일관적인 높은 프레임 속도가 중요한 이유는 무엇입니까? VR에서 낮은 프레임 속도는 모양과 느낌이 좋지 않으며, 높은 프레임 속도가 없으면 테스트가 어렵습니다. 개발 전반에 걸쳐 높은 프레임 속도를 유지하면서 V-Sync가 없다는 점은 더욱 눈에 띄므로 V-Sync가 활성화되어 있는지 확인하십시오.

현재 엔진에서는 반사 렌더링, 그림자 렌더링, 포스트 프로세스 등 "채널" 개념이 널리 수용됩니다. 각 채널마다 요구 사항이 다르며 각 채널에서 병목 현상을 찾아야 합니다. CPU? 드로우콜? 상태 설정? 리소스 설정? GPU? 버텍스 처리가 제한되어 있습니까? 기하학 처리가 제한되어 있습니까? 픽셀 처리가 제한되어 있나요?

그리기 호출, 상태 설정 또는 리소스 설정 중에 CPU가 제한됩니까? 지오메트리 셰이더를 사용하면 총 그리기 호출 수, 섀도우 캐스케이드 렌더링(drawCallCount/n), 여기서 n은 캐스케이드 수, 큐브맵 렌더링(drawCallCount/6) 수를 줄일 수 있는 방법을 고려하세요. 리소스 설정 비용을 줄이고 CPU에서 처리를 이동하는 데 도움이 되는 기타 기능을 갖추고 있습니다.

지오메트리 렌더링 유닛은 픽셀 셰이더 이전, 즉 직접 버텍스 픽셀 그리기 호출의 버텍스 셰이더 이후 또는 테셀레이션이 활성화된 경우 헐 셰이더 이후에 발생하는 하나의 기본 스트림을 다른, 가능한 더 큰 기본 스트림으로 변환합니다.

지오메트리 셰이더 기능, 렌더 대상 인덱스/뷰포트 인덱스, 단일 패스 큐브맵 렌더링, 섀도우 캐스케이드, S3D, GS 인스턴스를 통해 이전 셰이더 단계를 다시 실행할 필요 없이 동일한 지오메트리 셰이더를 기본 단위별로 여러 번 실행할 수 있습니다.

입체 3D 렌더링을 위한 지오메트리 셰이더는 엔진을 입체 3D와 호환되게 만드는 간단한 방법으로 다음과 같이 각 재질에 GS를 추가하거나 기존 재질의 GS를 조정합니다.

cpp
[maxvertexcount(3)]
void main(
    inout TriangleStream<GS_OUTPUT> triangleStream,
    triangle GS_INPUT input[3])
{
    for(uint i = 0; i < 3; ++i)
    {
        GS_OUTPUT output;
        output.position = (input[i].worldPosition , g_ViewProjectionMatrix);
        triangleStream.Append(output);
    }
}

버텍스/형상이 제한되어 있습니까? 속성을 압축하고 셰이더 단계 사이의 모든 속성을 패킹하여 버텍스 크기를 줄입니다. 이는 업스케일링 또는 테셀레이션 파이프라인에 지오메트리 셰이더를 사용하는 경우 중요합니다. 늦은 가져오기 접근 방식 사용을 고려하세요. 버텍스 어트리뷰트 데이터를 사용된 셰이더 단계의 버퍼로 바인딩하고 하드웨어에 크게 의존하며 항상 성능을 디버깅하고 영향이 있는지 확인하세요! GPU 주위로 이동하는 데이터를 줄입니다.

픽셀이 제한되어 있나요? 픽셀 셰이더 복잡성 감소, 프레임당 음영 처리된 픽셀 수 감소, 더 작은 렌더 타겟을 사용한 실험적 업샘플링은 고품질 비주얼과 충돌하여 후광, 쉬머 및 망막 충돌을 도입합니다.

재투영을 사용하여 입체 3D 렌더링 속도를 높이는 것을 고려해보세요. PlayStation 3 스테레오 3D 게임에서 큰 성공을 거두었지만 시차가 작은 경우에만 성공적으로 사용할 수 있습니다.

  • **우수한 추적 및 교정.**추적은 일반적으로 SDK에서 제공하는 추적 매트릭스를 사용하여 SDK에서 처리됩니다. 게임 정의 기본 보기 위치 및 방향:

카메라에서 플레이어 머리의 오프셋을 추적합니다.

머리 매트릭스를 기준으로 한 플레이어 눈의 오프셋:

추적기 재설정 기능: 게임 카메라의 위치 및 요에 맞춰 머리 위치를 설정합니다. 위치 및 방향 추적을 재설정하여 게임 세계와 현실 세계 사이의 관계를 재정렬하여 고정된 플레이어 위치, 패스 앤 플레이가 다양한 높이의 플레이어와 일치하도록 합니다.

추적을 사용하면 매우 비싼 효과를 달성하고 "모퉁이에 있는 작은 일일 뿐"이라고 주장할 수 있는 능력 없이 사용자가 추적 볼륨에 있는 모든 것에 가까이 다가갈 수 있습니다. 심지어 가장 낮은 품질이라도 기존 창작물보다 더 높은 충실도가 필요합니다. 추적 볼륨에 있는 경우 높은 충실도가 있어야 합니다.

  • **낮은 대기 시간.**지연 시간을 줄이는 것이 왜 그렇게 중요한가요? 지연 시간은 입력과 응답 사이의 시간이며, 중요한 것은 지속적으로 높은 프레임 속도만이 아닙니다. VR 헤드 트래킹뿐만 아니라 게임에서도 반응성을 향상시키는 것이 매우 중요합니다. 게임 프로그래머는 반응형 제어의 필요성을 이해하고, 네트워크 프로그래머는 반응형 상대의 필요성을 이해하고 있습니다.

이제 VR에 최적화된 매우 효율적이고 높은 프레임 속도, 낮은 대기 시간, 초고품질 차세대 엔진에서 뛰어난 추적 기능을 갖추고 있다고 가정하면... 엔진의 작업이 완료된 것인가요? 물론 그렇지 않습니다! 또한 플랫폼별 최적화, 주변 장치 추적, 사회적 측면, 게임 플레이/디자인 요소 등에 대한 작업도 있습니다.

Valve는 이미 2014년부터 하드웨어 및 소프트웨어 엔지니어, VR용으로 특별히 설계된 맞춤형 광학 구성 요소, 디스플레이 기술 - 낮은 지속성, 글로벌 디스플레이, 추적 시스템(기본 기반 위치 추적, 포인트 기반 데스크톱 추적 및 컨트롤러, 레이저 추적 HMD 및 컨트롤러), SteamVR API - 크로스 플랫폼, OpenVR을 결합하여 수년간의 VR 연구 경험을 보유하고 있습니다.

HTC Vive 개발자 에디션 사양: 새로 고침 빈도는 90Hz(프레임당 11.11ms), 낮은 지속성, 전역 디스플레이, 프레임 버퍼 해상도는 2160x1200(눈당 1080x1200), 오프스크린 렌더링은 약 1.4배 더 넓고 높음: 눈당 1512x1680 = 254만 음영 픽셀(무차별), FOV ~110도, 360⁰ 실내 규모 추적, 다중 추적 컨트롤러 및 기타 입력 장치.

음영을 위한 초당 추정 가시 픽셀 수: 30Hz에서 720p: 2,700만 픽셀/초, 60Hz에서 1080p: 1억 2,400만 픽셀/초, 30인치 모니터 2560x1600@60Hz: 2억 4,500만 픽셀/초, 4k 모니터 4096x2160@30Hz: 2억 6,500만 픽셀/초, VR은 90Hz 1512x1680x2: 4억 5,700만 픽셀/초(3억 7,800만 픽셀/초)로 줄일 수 있습니다. 이는 VR이 아닌 렌더러를 사용하는 100Hz의 30인치 모니터와 동일합니다.

GPU 최소 사양을 최소화하세요. 최소 사양이 낮을수록 고객이 앨리어싱을 더 많이 인지하게 됩니다. 고객은 앨리어싱을 "깜박임"이라고 부르며 알고리즘은 여러 GPU로 확장되어야 합니다.

Desktop VR은 입체 렌더링(다중 GPU)을 시도할 수 있습니다. AMD와 NVIDIA는 모두 여러 GPU에서 입체 렌더링을 가속화하기 위해 DX11 확장을 제공합니다. AMD는 거의 두 배의 프레임 속도를 달성했지만 NVIDIA의 구현을 테스트하지 않았습니다. 개발자에게 적합하며 팀의 모든 구성원은 개발 상자에서 다중 GPU 솔루션을 사용하여 불편할 정도로 낮은 프레임 속도 없이 프레임 속도를 이길 수 있습니다.

VR 인터랙티브 기술에는 선택, 조작, 탐색, 시스템 제어 등이 포함됩니다. 3D 선택에는 컬렉션에서 하나 이상의 객체 선택, 실제 은유(터치/잡기, 고정점), "자연스러운" 기술(간단한 가상 손, 레이 캐스팅) 등이 포함됩니다. 3D 선택에 영향을 미치는 요소는 기술(추적 지터, 정확도, 지연), 인간(손 지터), 환경(거리, 폐색) 등입니다. 기술은 최적이 아닙니다. Double Bubble을 사용할 수 있습니다: 확장된 레이 캐스팅, 동적 볼륨 커서, 점진적 최적화. 3D 선택 기술의 적용 시나리오는 다음과 같습니다.

Ray Cast와 비교하여 Double Bubble은 선택 시간 및 오류 측면에서 더 나은 성능을 발휘합니다.

실제 은유, 기술 및 실제 제약 조건을 사용하여 실제 가정을 분석합니다.

각 작동 모드의 다양한 단계는 아래에 설명되어 있습니다.

3D 상호 작용에 대한 최종 생각은 자연주의 대 마법(초자연적, 초자연적), 부정확한 도구와의 정확한 상호 작용(점진적 최적화, 동적 C/D 이득, 가상 마찰)입니다.

가상 개체의 영향도 중요합니다. 아바타의 영향에 대한 많은 연구는 사회적 상호 작용과 존재감(일부 사람들의 경우)에 결정적입니다.

몸짓이나 몸짓에 있어서 설명을 하기 위해서는 몸짓이 필요하고, 몸짓을 자주 하는 사람도 있습니다. 언어학 연구자들은 제스처가 어려운 개념을 설명하는 능력에 미치는 영향을 연구해 왔습니다.

왼쪽부터: 아바타 없음, 아바타는 있으나 움직임 없음, 완전한 아바타 및 움직임.

아바타가 있으면 개체 기억 작업을 수행하는 능력이 크게 향상되어 아바타가 있는 사람이 아바타가 없는 사람보다 더 많은 제스처를 취하게 됩니다. 지연 시간은 매우 중요합니다. 지연 시간이 짧을수록 "자연스러운" 인터페이스가 선호되지만 모든 경우에 있어 이것이 가장 효율적인 인터페이스는 아닐 수도 있습니다. 가상 신체는 특정 사용자에게 매우 중요하며 "가상 현실" 연구는 다양한 분야에서 찾아볼 수 있습니다. 그 영향과 요구 사항이 매우 광범위하고 매우 다양한 연구 커뮤니티가 흥미롭고 흥미로운 연구 협력을 촉진하기 때문입니다.

실제 물리적 공간과 가상 현실의 공간 매핑에서 가상 현실의 물리적 법칙은 가변적이며 인간의 인식은 가소적입니다. 이를 사용하여 유용성을 향상하고 초현실적이고 마법 같은 경험을 만들 수 있습니다.

XR에는 일반적으로 모션 추적 - 깊이 감지 - 영역 학습, 표면 재구성 - 평면 및 구멍 감지 등의 공간 인식 기술이 있습니다.

공간 인식 관련 장비:

Microsoft HoloLens의 경우 적외선 카메라 공간 매핑이 사용됩니다.

모션 및 제스처 추적 지원:

Music,、。

공간 처리 HoloToolkit은 기본 공간 매핑(공간 데이터 액세스/시각화, 공간 저장/로드) 및 공간 처리(면 메쉬에서 평면, 벽, 천장, 바닥, 테이블, 알 수 없음, 바닥 버퍼, 천장 버퍼, 사용자 정의 모양 정의)를 지원합니다.

Tango 모션 추적은 드리프트, 메모리 없음, 조명 등의 제한 사항이 있는 시각적 관성 주행 거리 측정(이미지 차이를 추적하는 VIO와 관성 모션 센서가 결합되어 정확도 향상)을 지원합니다. 2016년 Tango와 HoloLens의 비교는 다음과 같습니다.

2017년 VR 게임 Climb은 새로운 플랫폼 문제를 성공적으로 해결하고, 획기적인 움직임을 달성하고, 때로는 문제가 되는 디자인 중심 기능을 갖춘 보수적인 기술 접근 방식을 사용하기 위해 엄격한 계획을 채택했습니다.

Robinson은 성능과 메모리를 분석하고, 플랫폼 도구는 잘 실행되고 있으며, 아트 팀은 프로그램 시각적 분석 도구를 성공적으로 채택했으며, 화면상의 OOM 크래시 트랙 메모리를 분석하고, 새로운 기능을 통해 그날의 저장된 피크를 분석할 수 있습니다.

각 눈에 대해 PS4 근거리/와이드 렌더링 지원을 활용하여 내부 및 외부 뷰를 렌더링하는 주석 포인트(렌즈 매칭) 렌더링.

성능과 해상도 사이의 최적점을 찾는 데 큰 도움이 되는 PS4는 내부 영역을 렌더링 스케일(1620p)의 1.5배와 동일하게 렌더링하고, PS4 Pro는 더 큰 내부 직경을 통해 이를 1.9배로 늘립니다.

외부 루프는 PS4 Pro 1:1에서 언더샘플링되어 장면을 4번 렌더링해야 합니다. 렌더 스레드에서 장면 드로콜을 기록하는 것은 장면과 조명에 대해 동일한 명령 버퍼를 다시 제출하므로 비용이 4배 더 비쌉니다.

후처리는 여전히 4번 기록되고 일부 데이터는 패치가 필요하며 기존 뷰별 상수 버퍼는 각 커밋 후에 덮어쓰이고 포스트 프로세스 중에 필요한 렌더 대상(객체 속도)은 각 커밋 후에 복사됩니다. GPU 비용을 절약하기 위해 버텍스 셰이더는 4번 실행되지만 Post를 비동기 작업으로 인터리브하여 GBuffer의 버텍스 오버헤드를 흡수하고 HTILE 마스크를 채워 관련 영역 외부의 픽셀을 거부함으로써 오버헤드가 허용됩니다.

요약하자면, 로빈슨의 계획/시간표는 불안정하여 개발자(성능, 콘텐츠, 게임플레이)를 위한 여지가 남아 있고, 모바일 및 사용자 옵션에 대한 혼합된 결과, 매우 성공적인 기술 혁신, 미디어/플랫폼의 가시성 기준이 높아졌습니다. 인공적인 움직임은 존재했고 앞으로도 존재할 것이며, UI/UX는 아직 갈 길이 멀고, VR 성능은 어렵지 않으며, 정점에 달할 수 있습니다.

주류 하드웨어에서 높은 충실도를 달성하는 것은 지금까지 단지 표면적인 것에 불과했습니다. 다음 그림은 VR 시스템 장면의 구성 요소를 보여줍니다.

입력 프로세서, 시뮬레이션 프로세서, 렌더링 프로세서 및 월드 데이터베이스 간의 관계는 다음과 같습니다.

VR 분류는 아래와 같이 사용된 기술 유형과 정신적 몰입 정도라는 두 가지 요소를 기반으로 할 수 있습니다.

미래의 주요 XR 기술 과제 해결에는 디스플레이, 조명, 동작 추적, 전력 및 열 방출, 연결성 등이 포함됩니다.

15.2.1.1 소프트웨어 아키텍처

VR 애플리케이션에는 종종 모든 일이 일어나게 만드는 작업이 포함됩니다! 하드웨어 오류 등 모든 것을 지원해야 하고(archarch), SDK에서 입력을 추출하고, SDK, UI 프레임워크를 관리해야 합니다. VR 계층 아키텍처 중 하나는 다음과 같습니다.

  • SDK별 입력 클래스: 하드웨어/SDK당 하나의 클래스, 프로젝트별 로직 없음! 장치 입력을 듣고, 추상 처리기를 호출하고, 도구를 호출/보류/업하고, 일반 입력을 호출/보류/업합니다.

  • 입력 구성 요소: SDK 특정 구성 요소 클래스에는 하드웨어 기능에 대한 특정 참조가 포함되어 있습니다.

cpp
public class ViveControllerComponents : WandComponents 
{
    public SteamVR_Controller.Device viveController;
}

범주별 구성 요소 클래스에는 하드웨어 유형에 대한 공용 속성이 포함되어 있습니다.

cpp
public class WandComponents : InputComponents 
{
    public Transform handTrans;
    public override Vector3 Position { get { return handTrans.position; } }
    public override Vector3 Forward { get { return handTrans.forward; } }
    public override Quaternion Rotation {get{ return handTrans.rotation; }}
}

InputComponents 기본 클래스에는 대부분의 추상 데이터가 포함되어 있습니다.

cpp
public class InputComponents 
{
    public virtual bool Valid { get { return true; } }
    public virtual Vector3 Position { get { return Vector3.zero; } }
    public virtual Vector3 Forward { get { return Vector3.forward; } }
    public virtual Quaternion Rotation { get { return Quaternion.identity; } }
}
  • 도구 기본 클래스.
cpp
// 개 SDK의 //
public virtual void DoToolDown_Sixense(SixenseComponents sxComponents) 
{
    DoToolDown_Wand(sxComponents);
}
public virtual void DoToolDown_Leap(LeapComponents leapComponents) 
{
    DoToolDown_Optical(leapComponents);
}
public virtual void DoToolDown_Tango(TangoComponents tangoComponents) {
    DoToolDown_PointCloud(tangoComponents);
}

// 개 의 //
public virtual void DoToolDown_Wand(WandComponents wandComponents) 
{
    DoToolDown_Core(wandComponents); 
}
public virtual void DoToolDown_Optical(OpticalComponents opticalComps) 
{
    DoToolDown_Core(opticalComps); 
}
public virtual void DoToolDown_PointCloud(PCComponents pcComponents) 
{
    DoToolDown_Core(pcComponents); 
}

// 함수
DoToolDown_Core
DoToolDownAndHit*
DoToolHeld_Core
DoToolUp_Core
DoToolDisplay_Core
    
public virtual void DoToolDown_Core(InputComponents comp) 
{
    if (Physics.Raycast(comp.Position, comp.Forward, out hit, dist, layers)) 
    {
        DoToolDownAndHit(comp);
    }
}
  • 특정 도구 유형.
cpp
// 로써 DoToolDown_Core,구현 플랫폼로직
public class MoveTool : Tool 
{
    protected override void DoToolHeldAndHit(InputComps comps) 
    {
        selectedTrans.position = hit.point;
    }
}

// 로써 또는 SDK의 ,로써 구현 의 로
public class MoveTool : Tool 
{
    // ...
    protected override void DoToolHeld_Optical(OpticalComps comps) 
    {
        // Move mechanic that’s more appropriate for optical control
    }
}
  • 하드웨어 입력 기본 클래스. 일반적인 게임 기능 및 일회성 상호 작용에 적합한 비도구 추상 입력, 세 가지 처리 방법:
cpp
// Unity의 입력
bool HardwareInput.ButtonADown/Held/Up
// 개 의
event HardwareInput.OnButtonADown
// 내에서 입력/로직
void HardwareInput.HandleButtonADown()
  • 게임플레이/일반 입력 카테고리….
cpp
// 회 입력
public class GameplayController : MonoBehaviour 
{
    void Update() 
    {
        if(HardwareInput.TriggerDown) 
        {
            WorldConsole.Log("Fire ze missiles!");
        }
    }

    void Awake() 
    {
        HardwareInput.OnButtonADown += HandleButtonA;
    }

    void HandleButtonA() 
    {
        WorldConsole.Log("Boom!"); // Btw: use a “world console”!
    }
}

// 의 입력, 갱신(Update)의 메서드 :
// 추가(Add)입력/위치 /
HardwareInput.ButtonADown/Held/Up
// 의 이벤트 파라미터
HardwareInput.OnButtonADown(args)
HandleButtonADown(components)

모든 SDK는 Libs/dir 형식으로 프로젝트에 존재합니다.

SDK가 너무 많습니다! SDK 간의 AndroidManifest와 플러그인 충돌은 경우에 따라 매니페스트를 병합하여 해결할 수 있습니다(예: Cardboard+Nod). 대부분의 경우 충돌하는 SDK를 빌드 파이프라인에 연결할 수 있는 자산 폴더 안팎으로 이동하면 됩니다. 다중 SDK 장면 설정의 경우 모든 SDK를 지원하도록 장면을 설정하고 Player 개체에는 ViveInput, GamepadInput, CardboardInput, NodeInput, LeapInput, TangoInput...에 대한 구성 요소가 포함됩니다. 장면 간에 작업 중복이 없고 모든 장치를 동시에 활성화할 수 있다는 이점이 있습니다(예: Vive+Leap). 이점은 여러 장치 유형이 상호 작용한다는 것은 새로운 디자인 과제를 의미하고, 더 많은 플랫폼 == 더 복잡한 장면을 의미하며, 플레이어를 더 관리하기 쉬운 프리팹으로 분할할 수 있고 이러한 프리팹을 런타임에 또는 편집기 스크립트를 사용하여 조립할 수 있다는 것입니다. SDK Manager 편집기 스크립트의 경우 편집기에서 또는 빌드 시 플랫폼별로 구성 요소 및 개체를 활성화/비활성화합니다.

cpp
public void SetupForCardboard() 
{
    Setup(
        // Build settings
        bundleIdentifier: "io.archean.cardboard",
        vrSupported: false,
        // GameObjects
        cameraMasterActive: true,
        sixenseContainerActive: false,
        // MonoBehaviours
        cardboardInputEnabled: true
    );
}

또한 사용자 정의 가능한 입력 모듈을 통해 uGUI에 새로운 하드웨어 지원을 추가할 수 있습니다. 블록 및 포인터 UI(블록 및 포인터 UI)를 클릭하거나 누르면 <device>는 UI(예: Vive) 또는 단순 충돌(예: Leap)에 광선을 투사합니다. 맞춤 버튼 구성요소:

ButtonHandler 클래스에는 모든 작업을 매핑하는 거대한 스위치 문이 있습니다.

cpp
switch(button.action) 
{
    case ButtonStrings.Action_TogglePalette: TogglePalette(button, state); break;
    case ButtonStrings.Action_ChangePage: ChangePage(button, state); break;
    case ButtonStrings.Action_ChangePagination:ChangePagination(button); break;
    case ButtonStrings.Action_SelectProp: SelectProp(button, state); break;
    case ButtonStrings.Action_SelectTool: Tool.HandleSelectToolButton(button); break;

조작을 위한 상수 문자열이 포함된 파일도 있습니다.

cpp
public const string Action_TogglePalette = "togglePalette";
public const string Action_ChangePage = "changePage";
public const string Action_ChangePagination = "changePagination";
public const string Action_SelectProp = "propSelect";
public const string Action_SelectTool = "toolSelect";
....

매개변수 필드를 통해 더욱 발전되고 재사용 가능한 기능을 얻을 수 있고, 사용자 인터페이스 코드가 중앙 집중화되었으며, 버튼이 모든 데이터 유형을 전달할 수 있어 매우 편리합니다. 따라서 다중 플랫폼 가상 현실 애플리케이션을 만들고 싶다면 SteamVR과 Cardboard가 Unity의 기본 VR 지원을 추가하므로 처음부터 다중 플랫폼을 계획해야 합니다.

가상 현실 시스템은 하드웨어와 소프트웨어라는 두 가지 주요 하위 시스템으로 구성됩니다. 하드웨어는 다시 컴퓨터나 VR 엔진, I/O 장치로 나눌 수 있고, 소프트웨어는 아래와 같이 응용 소프트웨어와 데이터베이스로 나눌 수 있습니다.

아래 이미지는 CalVR이라는 VR 프레임워크의 다양한 모듈을 보여줍니다. 이 프레임워크 자체는 OSG 위에 구축되고 OpenGL 위에 구축됩니다. 메뉴 API는 현재 보드 메뉴와 버블 메뉴라는 두 가지 메뉴 위젯 라이브러리를 지원합니다. CalVR은 사용자 정의 플러그인을 실행할 수 있는 Kinect 또는 링 마우스와 같은 장치 드라이버 세트를 사용합니다.

아래 사진은 VR 시스템을 3인칭 시점으로 본 모습입니다. 엔지니어링 하드웨어와 소프트웨어가 완전한 VR 시스템이라고 가정하는 것은 실수입니다. 유기체와 하드웨어와의 상호 작용도 똑같이 중요합니다. 또한 VR 체험 중에는 주변의 물리적 세계와의 상호작용이 지속적으로 일어난다.

버퍼링은 일반적으로 시각적 렌더링 파이프라인에서 프레임 찢어짐 및 삭제를 방지하는 데 사용됩니다. 그러나 지연 시간이 길어져 VR에 해롭습니다.

15.2.1.2 퀘스트 2 개발

Quest 2는 Oculus가 2020년에 출시한 올인원 VR 머신입니다. Qualcomm Snapdragon XR2 칩셋을 사용합니다. Snapdragon XR2 칩셋의 기본 하드웨어 매개변수는 다음과 같습니다.

CPU 옥타코어 Kryo 585(1 x 2.84GHz, 3 x 2.42GHz, 4 x 1.8GHz)

GPU아드레노 650

Quest 2에서 대화형 애플리케이션을 개발할 때 실행 가능한 그리기 호출 예산은 다음과 같습니다. 메시/객체당 호출 1개, 해당 객체의 고유 재질(또는 재질 인스턴스)당 호출 1개, 메시의 재질 수를 제한하고, Atlas 텍스처로 재질 수를 줄일 수 있으며, 메시 위치를 병합할 수 있습니다.

RenderDoc을 사용하여 작업에 연결하여 작업에서 그린 프레임을 캡처할 수 있습니다. 총 드로우 콜 수를 참조하고 개별 드로우 콜을 단계별로 진행하여 잠재적인 성능 문제를 찾아야 합니다.

OVRMetrics/FPS 카운터: FPS는 가장 중요한 성능 지표입니다! 이상적으로는 72를 유지하지만 최소한 65 이상인 경우 FPS 카운터를 사용하여 달리는 동안 프레임 속도를 확인하세요.

포워드 렌더링, 깊이 렌더링 없음, 단일 패스 스테레오 렌더링, 앤티앨리어싱 및 포비티드 렌더링을 사용합니다.

UE 및 Unity에서 포워드 렌더링을 설정합니다.

UE 및 Unity에서 싱글 패스 스테레오 렌더링을 설정합니다.

Oculus Quest는 FFR(Fixed Foveated Rendering)을 지원합니다. FFR을 사용하면 아이 버퍼의 가장자리를 아이 버퍼의 중앙 부분보다 낮은 해상도로 렌더링할 수 있습니다. 다른 형태의 포비티드 기술과 달리 FFR은 안구 추적을 기반으로 하지 않으며 고해상도 픽셀이 안구 버퍼 중앙에 "고정"됩니다. **FFR 사용의 시각적 효과는 거의 눈에 띄지 않지만 FFR의 성능 이점은 다음과 같습니다.

  • GPU 채우기 성능이 크게 향상되었습니다.

  • 전력 소모를 줄여 발열을 줄이고 배터리 수명을 연장합니다.

  • 애플리케이션이 눈 텍스처의 해상도를 높여 성능과 전력 소비 수준을 유지하면서 시청 환경을 개선할 수 있습니다.

FFR을 사용할 때 몇 가지 장단점이 있습니다.

  • FFR은 배경 이미지 및 대형 개체를 포함하여 대비가 낮은 텍스처에 가장 유용합니다.

  • FFR은 텍스트, 세밀한 이미지 등 고대비 항목에는 유용성이 떨어지며 이미지 품질이 눈에 띄게 저하될 수 있습니다.

  • 복잡한 조각 셰이더는 FFR의 이점을 얻습니다.

FFR 수준은 프레임별로 조정되어 성능과 시각적 품질 사이에서 최상의 균형을 이룰 수 있습니다. 일반적으로 FFR을 최대한 많이 사용하고 가능한 가장 높은 수준으로 설정해야 하지만, 콘텐츠를 테스트하고 원치 않는 시각적 아티팩트가 있는지 찾아야 합니다. FFR은 가능할 때마다 사용해야 하기 때문에 GPU 로드 및 애플리케이션 요구 사항에 따라 FFR 수준을 자동으로 설정하는 동적 FFR을 사용하는 것이 좋습니다.

FFR이 제공하는 이득(또는 손실)은 일반적으로 응용 프로그램의 픽셀 셰이더 비용에 따라 달라집니다. FFR은 픽셀 집약적인 애플리케이션의 성능을 25% 향상시킬 수 있습니다. 반면에 매우 간단한 셰이더(GPU 패딩에 바인딩되지 않음)를 사용하는 애플리케이션에서는 FFR이 크게 향상되지 않을 수 있습니다. ALU 바인딩이 높은 애플리케이션은 아래 이미지에 표시된 것처럼 장면에서 GPU 비율을 수집하는 이점을 누릴 수 있습니다. GPU 사용률의 16%가 타임워프에서 비롯된다는 점(따라서 FFR의 영향을 받지 않음)을 고려하면 이 그래프는 낮은 설정에서 6.5%, 중간 설정에서 11.5%, 높은 설정에서 21%의 성능 향상을 보여줍니다.

이는 FFR을 사용하는 최상의 시나리오를 보여줍니다. 매우 간단한 픽셀 셰이더를 사용하여 응용 프로그램에서 동일한 테스트를 수행하는 경우 실제로 낮은 설정에서 순 페널티가 발생할 수 있습니다. FFR을 사용하는 고정 오버헤드가 상대적으로 적은 픽셀의 렌더링 절감 효과보다 높을 수 있기 때문입니다. 실제로 이 경우 높은 설정에서 약간의 이득을 경험할 수 있지만 이미지 품질이 저하될 가치는 없습니다. 기존의 2D 화면과 달리 VR 장치는 시청자에게 표시되는 이미지가 HMD의 렌즈 곡률에 맞게 왜곡되어야 합니다. 이러한 왜곡을 통해 우리는 단순히 원시 디스플레이를 보는 것보다 더 넓은 시야를 인식할 수 있습니다. 아래 이미지는 2D 평면(수평선)이 구로 비틀어지는 왜곡 효과를 보여줍니다.

왜곡으로 인해 눈 텍스처를 구성하는 픽셀의 표현이 매우 고르지 않습니다. 뒤틀린 영역은 FOV 중앙보다 FOV 가장자리에 생성된 후 더 많은 픽셀을 필요로 하며, 이로 인해 중앙보다 FOV 가장자리의 픽셀 밀도가 더 높아집니다. 사용자는 주로 화면 중앙을 바라보기 때문에 반발이 클 수 있습니다. 게다가 렌즈는 시야의 가장자리를 흐리게 하기 때문에 눈 텍스처의 이 부분에 많은 픽셀이 렌더링되더라도 이미지의 선명도가 손실됩니다. GPU는 FOV 가장자리에서 명확하게 볼 수 없는 픽셀을 렌더링하는 데 많은 시간을 소비하므로 매우 비효율적입니다.

포비티드 렌더링은 계산 중에 출력 이미지의 해상도를 줄여 낭비되는 일부 GPU 처리 리소스를 회수합니다. GPU에서 개별 렌더링 타일의 해상도를 제어하여 이를 수행합니다. Oculus Quest는 타일 렌더러를 사용합니다. FFR은 개별 타일의 해상도를 제어하고 아이 버퍼 가장자리에 있는 타일의 해상도가 중앙보다 낮은지 확인하는 방식으로 작동합니다. 이렇게 하면 왜곡 후 이미지의 품질을 크게 저하시키지 않고 GPU가 채워야 하는 픽셀 수가 줄어듭니다. 따라서 많은 수의 픽셀을 렌더링하는 애플리케이션의 경우 GPU 성능이 매우 크게 향상됩니다.

아래 스크린샷은 1024x1024 아이 버퍼에 대한 타일식 해상도 배가 플롯을 보여줍니다. 색상은 FFR 설정을 보여주기 위해 아래 예제 이미지에서 다음 해상도 수준을 나타냅니다.

  • 흰색 = 전체 해상도: 이는 FOV의 중심이며 텍스처의 각 픽셀은 GPU에 의해 독립적으로 계산됩니다.

  • 빨간색 = 1/2 해상도: GPU는 픽셀의 절반만 계산합니다. GPU가 계산 결과를 범용 메모리에 저장할 때 누락된 픽셀은 구문 분석 시 계산된 픽셀에서 보간됩니다.

  • 녹색 = 1/4 해상도: GPU는 픽셀의 1/4만 계산합니다. GPU가 계산 결과를 범용 메모리에 저장할 때 누락된 픽셀은 구문 분석 시 계산된 픽셀에서 보간됩니다.

  • 파란색 = 1/8 해상도: GPU는 픽셀의 1/8만 계산합니다. GPU가 계산 결과를 범용 메모리에 저장할 때 누락된 픽셀은 구문 분석 시 계산된 픽셀에서 보간됩니다.

  • 분홍색 = 1/16 해상도: GPU는 픽셀의 16분의 1만 계산합니다. GPU가 계산 결과를 범용 메모리에 저장할 때 누락된 픽셀은 구문 분석 시 계산된 픽셀에서 보간됩니다.

Quest는 동적 포비에이션 기능을 지원합니다. 이를 통해 포비에이션 수준을 구성하고 동적 포비에이션을 활성화하여 GPU 활용도에 따라 자동으로 조정할 수 있습니다. 동적 foveation이 활성화되면 foveation 수준은 지정된 foveation 수준을 최대값으로 자동 조정됩니다. GPU 활용도 및 애플리케이션 요구 사항에 따라 시스템은 선택한 포비티드 수준까지 올라가지만 이를 초과하지는 않습니다. **Unreal의 동적 해상도 기능 대신 동적 FFR을 사용해 보십시오.**FFR 수준을 설정하고 동적 포베테이션을 활성화하는 방법에는 여러 가지가 있습니다.

  • **프로젝트 설정.**FFR 레벨은 Unreal 프로젝트 설정의 OculusVR 플러그인 페이지에서 설정할 수 있습니다.

  • **API 설정.**FFR 수준은 다음을 사용하여 다음 인덱스 중 하나로 설정할 수 있습니다.
cpp
void UOculusFunctionLibrary::SetFixedFoveatedRenderingLevel(EFixedFoveatedRenderingLevel level, bool isDynamic)
  • **청사진 설정.**다음 청사진 노드를 통해 FFR 수준을 가져오고 설정합니다.

멀티뷰는 Android 기반 Oculus 플랫폼의 고급 렌더링 기능입니다. 애플리케이션이 CPU 바인딩된 경우 성능을 향상시키기 위해 다중 보기를 사용하는 것이 좋습니다. 일반적인 입체 렌더링에서는 각 아이 버퍼를 순차적으로 렌더링해야 하므로 애플리케이션 및 드라이버 오버헤드가 두 배로 늘어납니다. 다중 보기가 활성화되면 객체는 왼쪽 눈 버퍼에 한 번 렌더링된 다음 버텍스 위치 및 보기 관련 변수(예: 반사)가 적절하게 수정되어 오른쪽 눈 버퍼에 자동으로 복사됩니다. OpenGL 및 Vulkan API는 멀티뷰 렌더링을 지원합니다. 멀티뷰를 활성화하려면 Unreal 설정 페이지(Edit > Project Settings > Engine > Rendering)를 열고 다음 옵션을 확인하세요.

위상 동기화는 지연 시간을 적응적으로 관리하기 위한 프레임 타이밍 관리 기술이며 UE4.23 이상에서 Quest 및 Quest 2 애플리케이션의 옵션으로 사용할 수 있습니다. 위상 동기화는 Oculus Quest 및 Quest 2 애플리케이션에 프레임 타이밍 관리를 위한 기존 고정 대기 시간 모드의 대안을 제공합니다. 고정 대기 시간 모드는 현재 프레임 손실을 방지하고 사용자 경험에 부정적인 영향을 미칠 수 있는 오래된 프레임을 재사용해야 하는 것을 방지하기 위해 가능한 한 빨리 프레임을 합성하는 것을 의미합니다. 고정 지연과 달리 위상 동기화는 애플리케이션의 작업 부하에 따라 프레임 타이밍을 적응적으로 처리합니다. 위상 동기화의 목표는 합성기가 완료해야 하는 프레임보다 먼저 렌더링 프레임을 완료하는 것입니다. 이렇게 하면 프레임 손실 없이 렌더링 대기 시간을 줄일 수 있습니다. Quest 및 Quest 2를 대상으로 하는 애플리케이션은 Phase Sync에서 제공하는 적응형 프레임 타이밍을 활성화해야 합니다. Quest 2에는 Quest보다 더 많은 CPU 및 GPU 리소스가 있으며 프레임을 너무 일찍 렌더링하여 대기 시간이 늘어날 수 있으며 위상 동기화는 이 대기 시간을 줄이는 데 도움이 됩니다. 아래 이미지는 일반적인 멀티 스레드 VR 애플리케이션에 대해 활성화된 고정 대기 시간과 위상 동기화 간의 차이점을 보여줍니다.

위상 동기화를 활성화할 때 다음 사항에 유의하십시오.

  • 추가 성능 오버헤드가 없습니다.

  • 애플리케이션의 작업 부하가 크게 변동하거나 급증이 자주 발생하는 경우 단계 동기화로 인해 단계 동기화가 활성화되지 않은 경우보다 오래된 프레임이 사용될 수 있습니다.

  • Late-Latching과 위상 동기화는 일반적으로 서로를 보완합니다.

  • Extra Delay Mode와 Phase Sync가 모두 활성화된 경우 Extra Delay Mode는 무시됩니다.

Unreal Engine에서 위상 동기화를 활성화하려면 편집 > 프로젝트 설정 > 플러그인 > OculusVR을 열고 모바일 섹션에서 위상 동기화 확인란을 선택하세요.

단계 동기화 테스트: 애플리케이션에서 단계 동기화를 활성화한 후 logcat 로그를 확인하여 활성화되었는지 확인하고 대기 시간이 얼마나 절약되는지 확인할 수 있습니다.

cpp
adb logcat -s VrApi

위상 동기화가 활성화되지 않은 경우 Lat 값은 Lat=0 또는 Lat=1이며 추가 지연 모드를 나타냅니다. 위상 동기화가 활성화된 경우 Lat 값은 Lat=-1이며 이는 지연이 동적으로 관리됨을 나타냅니다.

Prd 값은 런타임으로 측정된 렌더링 대기 시간을 나타냅니다. 위상 동기화로 인해 얼마나 많은 지연 시간이 절약되는지 계산하려면 위상 동기화가 활성화될 때와 활성화되지 않을 때의 Prd 값을 비교하십시오. 예를 들어, 위상 동기화가 있는 Prd가 35ms이고 위상 동기화가 없는 Prd가 45ms인 경우 위상 동기화를 사용하면 10ms의 지연을 절약할 수 있습니다. 위상 동기화 유무에 따른 성능을 보다 쉽게 ​​비교하기 위해 adb 쉘 setprop를 사용하여 위상 동기화를 켜거나 끌 수 있습니다. setprop를 변경한 후 변경 사항을 적용하려면 애플리케이션을 다시 시작해야 합니다.

  • 닫기: adb shell setprop debug.oculus.phaseSync 0.

  • 열기: adb 쉘 setprop debug.oculus.phaseSync 1.

특정 톤매핑 효과는 톤매핑과 관련된 기존 성능 비용을 발생시키지 않고 Quest의 Unreal Engine에서 사용할 수 있습니다. Oculus 통합은 Unreal Engine의 모바일 HDR 모드나 추가 렌더 패스 대신 Vulkan 서브패스를 사용하기 때문에 최소 600마이크로초의 추가 렌더링 시간으로 톤매핑을 렌더링할 수 있습니다. **이 기능은 Vulkan을 사용하는 UE 4.26의 Oculus 브랜치에서만 사용할 수 있습니다.**구체적인 내용은 언리얼 엔진의 톤매핑을 참조하세요.

Quest는 UE에서 VR 컴포지터 레이어를 지원합니다. Unreal을 사용하면 투명 또는 불투명 쿼드, 큐브맵 또는 원통형 오버레이를 컴포지터 레이어로 레벨에 추가할 수 있습니다. 비동기 시간 왜곡 합성기 레이어(예: 월드 잠금 오버레이)는 애플리케이션 프레임 속도가 아닌 합성기와 동일한 프레임 속도로 렌더링됩니다. 흔들리는 경향이 적고 렌즈를 통해 레이 트레이싱이 이루어지므로 표시되는 텍스처의 선명도가 향상됩니다.

**텍스트에는 컴포지터 레이어를 사용하는 것이 좋습니다. 컴포지터 레이어에서 렌더링된 텍스트가 더 선명합니다.또한응시 커서와 UI는 쿼드 컴포지터 레이어로 렌더링하는 데 적합합니다.**실린더는 부드러운 곡선 UI 인터페이스에 유용할 수 있으며, 큐브맵은 시작 장면이나 스카이박스에 사용할 수 있습니다. 로딩 장면에서 큐브맵 컴포지터 레이어를 사용하는 것이 좋습니다. 그러면 애플리케이션이 업데이트를 수행하지 않더라도 항상 안정적인 최소 프레임 속도로 표시되어 애플리케이션 시작 시간을 크게 줄일 수 있습니다.

쿼드, 원통 및 큐브맵 레이어는 Unreal 4.13 이상에서 지원됩니다. 기본적으로 VR Compositor 레이어는 항상 장면의 다른 모든 객체 위에 나타납니다. 깊이 지원을 활성화하여 깊이 위치 지정에 응답하도록 합성기 레이어를 설정할 수 있습니다. 여러 레이어를 사용하는 경우 우선순위 설정을 사용하여 레이어가 나타나는 깊이 순서를 제어합니다. 값이 낮을수록 우선순위가 높습니다(예: 1보다 0). 지원 깊이를 활성화하면 성능에 영향을 미칠 수 있으므로 주의해서 사용하고 그 영향을 평가하십시오.

오버레이를 만들려면 다음을 수행하세요.

  • Pawn을 생성하고 레벨에 추가합니다. UMG UI 디자이너를 사용하여 원하는 UI 요소를 Pawn에 추가할 수 있습니다.

  • Pawn을 선택하고 구성 요소 추가를 선택한 다음 스테레오 레이어를 선택합니다.

  • 스테레오 레이어 옵션에서 스테레오 레이어 유형을 쿼드 레이어, 실린더 레이어 또는 등가 레이어로 설정합니다.

  • "스테레오 레이어 유형"을 "얼굴 잠금", "몸통 잠금" 또는 "세계 잠금"으로 설정합니다.

  • 쿼드 스테레오 레이어 속성 또는 실린더 스테레오 레이어 속성에서 오버레이 크기를 월드 단위로 설정합니다.

  • 컴포지터 레이어가 항상 다른 장면 형상 위에 나타나지 않도록 설정하려면 스테레오 레이어에서 깊이 지원을 선택합니다. 이 설정은 성능에 영향을 미칠 수 있습니다.

  • 필요에 따라 텍스처 및 기타 속성을 구성합니다.

  • VR 이미지를 렌더링할 때 추가적인 충실도를 즐기기 위해 Quest 디스플레이에 맞게 조정된 GPU 하드웨어 바이큐빅 필터링을 활성화하려면 "바이큐빅 필터링" 확인란을 선택합니다.

참고: 커널 공간이 증가함에 따라 바이큐빅 필터링에는 더 많은 GPU 리소스가 필요합니다. 특히 삼선형 축소의 경우 별도의 밉 수준에서 두 개의 바이큐빅 계산이 필요하기 때문입니다. 컴포지터 레이어에서 직접 사용하는 경우 증가된 GPU 비용이 합성 타이밍에 나타나 잠재적으로 프레임 드롭을 일으키고 VR 경험에 부정적인 영향을 미칠 수 있습니다. 향상된 시각적 충실도는 최적의 VR 사용자 경험을 제공하는 데 필요한 추가 GPU 리소스와 비교하여 평가되어야 합니다.

구성 요소가 속한 Pawn은 쿼드 또는 원통의 중심에 고정됩니다. 모바일 앱에는 최대 3개의 VR 컴포지터 레이어를 추가할 수 있으며, Rift 앱에는 최대 15개의 VR 컴포지터 레이어를 추가할 수 있습니다.

빛과 그림자의 측면에서 더 높은 성능의 조명 효과를 위해 조명을 굽습니다. 하지만 라이트맵을 굽는 데는 시간이 걸린다는 점을 염두에 두고 팀 규모와 환경 수에 따라 시간의 균형을 맞추세요. 한 번에 하나의 동적 조명만 사용하는 경우에도 정적 영역에서 베이킹하는 것을 고려해 보세요. 동적 그림자는 매우 비싸며, 한 번에 하나의 그림자 조명만 투사할 수 있고, 단단한 그림자만 투사할 수 있으며, 게임 렌더링이 매우 가볍지 않은 한 가능하면 그림자 투사를 피합니다. Unlit 셰이더의 성능은 매우 우수하여 조명 및 라이트맵 베이킹에 소요되는 시간을 없애고 전체적으로 필요한 텍스처가 더 적습니다. 조명을 사용하지만 대안으로 툰 그림자를 사용하는 것은 조명을 전혀 사용하지 않고도 계산 비용이 덜 듭니다.

텍스처 해상도를 낮게 유지하고, 가능한 한 적은 수의 아틀라스를 사용하고, 특정 맵 없이 수행해 보거나, RGBA 채널로 압축하고, 최대한 많이 재사용하고 타일링하고, mip하는 것을 잊지 마세요! 명령의 수는 성능에 영향을 미치고, 텍스처의 수는 성능에 영향을 미칩니다. 특히 화면 전체에 타일로 배치할 때 셰이더는 아름답고 독특한 모양을 만드는 데 실제로 도움이 될 수 있습니다. 경량 셰이더를 실험하고 그들이 어떤 예상치 못한 일을 할 수 있는지 알아보세요. 머티리얼과 셰이더 전환은 드로우 콜당 성능에 약간의 영향을 미치며, Atlas는 드로우 콜을 줄이지 않더라도 고유 머티리얼 수를 최소화합니다. 가능하다면 셰이더를 결합하여 고유한 셰이더 수를 제한하세요.

메모리에서 인스턴스화하는 것만으로는 그리기 호출이 줄어들지 않으며 일부 유형의 인스턴스는 병합/일괄 그리기 호출을 수행하며 LOD는 여전히 그대로 유지되어 작동합니다! 특정 유형의 인스턴스는 LOD 및 일괄 그리기 호출도 사용합니다. 특히 높은 충실도에서 낮은 충실도로 전환할 때 업계에서 좋은 관행을 따르십시오. 새로운 스타일을 발견해보세요! 어려운 문제에 대한 솔루션으로 유용하고 적용 가능한 로우 폴리 스타일을 게임에 통합하세요. 예: 기존 방법을 사용할 때 성능 문제가 있는 경우 로우 폴리 스타일에서 나무나 나뭇잎이 처리되는 방식을 확인하세요.

일괄 처리를 사용하여 한 번의 호출로 여러 개체를 그릴 수 있습니다! 엔진마다 유형과 방법이 다르지만 일괄 처리에는 모두 약간의 오버헤드가 있다는 점을 명심하세요. 일괄 처리하는 것이 더 저렴한지 또는 통합과 같은 다른 솔루션을 사용하는 것이 더 저렴한지 알아보세요. Unity에는 동적 배칭(300개 버텍스 미만의 동일한 재질을 사용한 동일한 메쉬, 약간의 오버헤드가 있지만 작은 반복 객체에 매우 좋음)과 정적 배칭(폴리 모델은 높지만 더 많은 메모리를 사용함)이 있습니다. UE에는 인스턴싱된 스태틱 메시(하나의 드로우 콜이지만 다른 측면에서 성능이 많이 절약되지는 않음)와 계층적 인스턴싱된 스태틱 메시(계층적 인스턴싱된 스태틱 메시, LOD 및 자르기가 적용될 수 있지만 사용하기 어렵기 때문에 사용자를 돕기 위한 도구를 만들어야 함)가 있습니다.

반투명의 경우 투명도가 있는 작은 개체의 경우 성능이 매우 좋으며, 큰 투명 개체를 겹치면 성능에 가장 큰 영향을 미치며, 투명도를 최대한 적게 사용하고 장치에서 반투명도를 완전히 테스트하세요! !

왼쪽: 소프트 알파 카드 효과에 몇 가지 문제가 있습니다. 오른쪽: Vertex Fog는 여전히 작동하는 오래된 방법입니다.

반투명 빈 픽셀 영역을 줄이면 성능이 효과적으로 향상될 수 있습니다.

전환을 통해 창의력을 발휘해보세요! 모든 것이 페이드될 필요는 없습니다.

후처리의 경우 셰이더에서 색상 교정을 수행할 수 있으며, 일부 장소에 블룸이 꼭 필요한 경우 일부 카드를 사용하여 위장할 수 있습니다. 뎁스 오브 필드(DoF), 화면 오버레이 및 팬시 포스트 셰이더를 사용하지 못할 수도 있습니다. 후처리에서 필요한 것이 무엇인지 생각해보고 이를 다른 방법으로 달성해 보세요.

15.2.1.3 OpenXR

OpenXR은 Kronos에서 제작한 XR 표준 API입니다. OpenXR은 크로스 플랫폼, 고성능 액세스를 제공하며 여러 플랫폼에서 XR 장치 런타임에 직접 액세스할 수 있습니다.

함수 호출 시퀀스, 객체 생성, 세션 상태 변경, 렌더링 루프(아래 이미지)를 포함한 일반적인 OpenXR 애플리케이션에 대한 높은 수준의 개요입니다.

OpenXR에 대한 자세한 내용은 공식 웹사이트(https://khronos.org/openxr)를 참조하세요.

15.2.2 광학 및 이미징

모든 렌즈에는 이미지 왜곡, 색수차 및 기타 왜곡이 발생하므로 소프트웨어에서 이를 최대한 수정해야 합니다! HMD 렌즈를 통해 보이는 격자는 이미지의 측면(xy) 왜곡과 색수차를 드러냅니다. 왜곡은 파장에 따라 달라집니다!

두 가지 형태의 이미지 왜곡(압착 변형): 렌즈 왜곡 및 배럴 왜곡:

위 사진의 왼쪽은 광학부품(렌즈)에 의한 것이고, 위 사진의 오른쪽은 광학적 왜곡을 방지하기 위해 의도적으로 적용한 것입니다. 전반적인 작동 원리는 다음과 같습니다.

조정 가능한 안경 착용자는 조정 없이 동공간 거리의 변화에 적응할 수 있습니다.

아래 그림은 중요(상단) 및 허용 가능한(하단) 매개변수의 다이어그램입니다.

입체 3D의 경우 사진 입체 및 헤드 마운트 디스플레이에 대한 고정 설정에서 필요한 초점 거리 요구 사항은 다음과 같습니다.

아래 그림과 결합하면 (a) 대부분의 HMD는 좁은 시야각을 가지며, (b) 넓은 시야를 얻으려면 더 높은 해상도의 디스플레이가 필요하며, (c) 더 큰 픽셀이 필요합니다. (d) 두 세계의 장점을 모두 활용하는 방법은 무엇입니까? 눈의 다양한 시력을 활용하여 (e) 워프 셰이더를 사용하여 이미지의 가장자리를 압축하고, (f) 광학 장치가 역왜곡을 적용하여 가장자리가 다시 올바르게 보이도록 하고, (g) 중앙 픽셀은 더 작고 가장자리 픽셀은 더 큽니다.

광학 및 워프: 워프 채널은 RGB에 대해 각각 3세트의 UV를 사용하여 공간 및 색상 왜곡을 설명합니다.

1.4x 렌더 타겟을 시각화합니다. 위쪽 사진은 왜곡 전, 아래쪽 사진은 왜곡 후입니다.

템플릿 그리드(숨겨진 영역 그리드): 템플릿을 사용하여 실제로 렌즈를 통해 볼 수 없는 픽셀을 마스크합니다. GPU는 템플릿을 미리 거부하는 속도가 매우 빠릅니다. 또는 z에 가까운 깊이 버퍼로 렌더링하여 모든 픽셀에 사전 z 테스트가 활성화되고 렌즈가 방사상 대칭 변형을 생성하도록 할 수 있습니다. 즉, 패널에 투영된 원형 영역을 효과적으로 볼 수 있습니다.

템플릿 그리드 범례. 위에서 아래로, 왼쪽에서 오른쪽으로 왜곡된 보기, 이상적인 왜곡된 보기, 낭비된 공간, 왜곡되지 않은 보기, 왜곡되지 않은 보기(잘못된 픽셀 마스킹), 최종 왜곡되지 않은 보기, 최종 왜곡되지 않은 분리된 보기입니다.

템플릿 그리드(숨겨진 영역 그리드): SteamVR/OpenVR API는 이 그리드를 제공합니다. **채우기 속도를 17%까지 줄일 수 있습니다!**스텐실 메시: VR 1512x1680x2@90Hz: 4억 5,700만 픽셀/초, 눈당 254만 픽셀(총 508만 픽셀), 템플릿 메시 포함: VR 1512x1680x2@90Hz: 3억 7,800만 픽셀/초, 눈당 약 210만 픽셀(총 420만 픽셀).

왜곡 메시 순서: 렌즈 왜곡 메시, 폭력, 0-1 이외의 UV 제거, 템플릿 메시 제거, 수축 왜곡.

VR에는 렌즈 왜곡 및 수차 보정도 포함됩니다.

아래 그림은 Oculus Rift의 렌즈 구조와 원리를 보여줍니다.

인간의 시각 색상 시스템은 다음과 같습니다. 인간의 눈은 그것을 측정할 수 없고, 뇌는 빛의 각 파장을 측정할 수 없습니다. 대신, 눈은 S, M, L 원뿔의 반응 함수에 따라 세 가지 반응 값(S, M, L)을 측정합니다.

인간의 단안 및 양안 시야는 아래와 같습니다. 각 눈의 시야는 약 160°입니다(전체 시야는 약 200°입니다. 참고: 안와에서 눈이 회전하는 능력은 고려되지 않습니다).

인간의 시각을 갖춘 VR 디스플레이:

제한된 VR 디스플레이 새로 고침 빈도를 고려하십시오.

사례 2: 눈을 기준으로 움직이는 물체:

사례 3: 움직이는 물체를 추적하기 위해 눈이 움직입니다.

프레임 속도를 높이면 지터가 줄어들고, 프레임 속도가 높을수록(아래 맨 오른쪽 그래프) 실제 진실에 더 가까워집니다.

감소된 지터: 낮은 지속성 디스플레이. 낮은 지속성 디스플레이: 프레임의 작은 부분에 대해 픽셀이 빛납니다. Oculus DK2 OLED 낮은 지속성 디스플레이: 75Hz 프레임 속도 = 프레임당 ~13ms, 픽셀 지속성 = 2-3ms.

15.2.3 지연 시간 및 히스테리시스

VR의 지연 시간 요구 사항은 까다롭습니다. VR 그래픽 시스템의 목표는 "존재감"을 달성하고 뇌가 보는 것이 실제라고 생각하도록 속이는 것입니다. 현재 상태를 유지하려면 대기 시간이 매우 짧은 시스템이 필요합니다. 머리를 움직이면 보이는 것이 바뀌어야 합니다! 종단 간 대기 시간: 머리가 움직이는 시점부터 새로운 광자가 눈에 도달하는 시점까지의 시간입니다. 사용자의 머리 움직임을 측정하고, 장면/카메라 위치를 업데이트하고, 새 이미지를 렌더링하고, 이미지를 HMD에 전달한 다음 HMD의 디스플레이에 전달하고 실제로 디스플레이에서 빛을 방출합니다(광자가 사용자의 눈에 닿음). VR 지연 시간 목표: 10~25ms. 이는 매우 낮은 지연 시간의 머리 추적과 매우 낮은 지연 시간의 렌더링 및 디스플레이가 필요합니다.

1도당 10픽셀로 100° 시야에 걸쳐 있는 1000 x 1000 디스플레이를 생각해 보세요. 머리가 1초에 90° 움직인다고 가정하면(보통 속도에서만) 시스템의 종단 간 대기 시간은 50밀리초(1/20초)입니다. 따라서 표시되는 픽셀은 이상적인 시스템의 픽셀과 4.5°~45픽셀 다르며 대기 시간은 0입니다. VR에서 대기 시간을 줄이는 것은 부정적인 효과(현기증 등)를 방지하는 데 중요할 뿐만 아니라 작업 실행에도 중요하기 때문에 18ms의 지연 시간은 감지하기 어렵다고 하지만 여전히 성능에 영향을 미치며 3D 상호 작용을 최적화하려면 지연 시간을 분석해야 합니다. 그렇지 않으면 결과가 전송되지 않을 수 있습니다. 문제는 낮은 대기 시간과 높은 해상도에는 높은 렌더링 속도가 필요하며 VR 장치는 렌더링 성능이 낮은 경향이 있다는 것입니다.

피츠의 법칙: 간단한 고정 소수점 작업에 대한 인간 동작 시뮬레이션, 100개의 학술 논문(원하는 모바일 장치 선택), 1990년대와 2000년대의 대부분의 VR 결과는 30ms~200ms의 지연 시간으로 처리되었습니다. "성능"은 약 30ms에서 최고조에 달하며, 모터 시스템에 "대기 시간"이 있다고 가정하면 30ms 미만이 더 "자연스럽"지만 느립니다(이 작업에서는).

VR 지연은 모션-광자 지연이라고도 하며 모션, 센서, 처리 및 병합, 렌더링, 스캔아웃, 전송, 픽셀 변경 시간, 픽셀 지속성 등 여러 단계가 포함됩니다.

지연 시간을 낮게 유지하는 것은 좋은 VR 경험을 제공하는 데 핵심이며 목표는 20ms 미만, 희망적으로는 5ms에 가깝습니다. 대기 시간 감소 방법 개요 대기 시간을 줄이고 남은 대기 시간의 부작용을 최소화하려면 다음 전략을 조합하여 사용하십시오.

  • 가상 세계의 복잡성을 줄입니다.

  • 렌더링 파이프라인 성능을 향상시킵니다.

  • 이미지 렌더링에서 픽셀 전환까지의 경로 지연을 제거합니다.

  • 예측을 사용하여 향후 관찰 지점과 세계 상태를 추정합니다.

  • 최종 시점 오류 및 프레임 손실을 보상하기 위해 렌더링된 이미지를 이동하거나 왜곡합니다.

위 방법은 Steven M. LaValle이 저술한 VIRTUAL REALITY 책의 7.4 지연 시간 및 프레임 속도 개선 장에 자세히 설명되어 있습니다. 관심 있는 학생들은 이 책을 주의 깊게 읽어보시기 바랍니다.

렌더링 지연 - 시간 왜곡: 인지된 지연을 줄이기 위해 변형과 동시에 렌더링을 이후 시점으로 지연합니다. DK2 롤링 셔터를 담당합니다. SDK(예: Oculus VR SDK)는 방향과 위치를 처리할 수 있습니다. 프레임이 끝나기 전에 센서를 사용할 수 있는 다른 방법이 있나요? Time Warp – 예측 렌더링(John이 개척).

엔진 관점에서 대기 시간을 줄이는 한 가지 방법은 지연된 컨텍스트를 사용하여 여러 스레드에서 비동기적으로 명령 목록(명령 버퍼라고도 함)을 구축하는 것입니다. 즉각적인 컨텍스트로서, 명령 버퍼에 명령을 대기열에 넣을 때 렌더링 오버헤드가 있습니다. 이에 비해 재생 중에는 명령 목록이 훨씬 더 효율적으로 실행됩니다. "채널"의 개념이 적용됩니다. 다중 컨텍스트 렌더링을 사용하면 GPU가 프레임 초기에 처리를 시작하여 대기 시간을 줄일 수 있습니다.

단일 컨텍스트(상단)와 다중 컨텍스트(하단) 렌더링 비교.

각 눈의 보기에 대한 명령 목록을 병렬로 생성하고 제출하면 CPU 프레임 시간이 즉시 줄어듭니다. 즉, 엔진이 CPU에 바인딩된 경우 프레임 대기 시간이 즉시 줄어듭니다. 엔진이 GPU에 바인딩되어 있지만 GPU가 더 일찍 시작되면 프레임에서 더 일찍 완료됩니다. 즉, 프레임 지연 시간이 즉시 줄어듭니다. 지연 시간을 처리하는 VR 관련 방법이 있나요? 추적 데이터 샘플링과 해당 데이터를 사용하여 프레임을 렌더링하는 사이의 시간은 최대한 짧아야 하며, 버퍼를 두 배 이상 사용하지 않아야 합니다. 최신 방향 데이터를 사용하여 이미지를 다시 투영하면 대기 시간과 프레임 속도가 눈에 띄게 향상될 수 있습니다. 추적되는 주변 장치의 대기 시간을 최대한 줄이기 위해 대기 시간을 처리하는 플랫폼별 방법이 있습니까?

여전히 CPU에 묶여 있다면 Compute가 병렬화 가능한 작업을 GPU로 오프로드하는 데 도움이 될 수 있습니다. 여전히 GPU에 묶여 있는 경우 Compute를 사용하면 GPU가 충분히 활용되지 않는 경우 GPU 작업을 다른 보다 일반적인 관점에서 생각할 수 있습니다. 그림자 렌더링에는 버텍스/형상이 필요한 경우가 많으므로 비동기 컴퓨팅 작업을 예약하기에 좋은 장소입니다.

15.2.3.1 예측

Project Morpheus가 포함된 PlayStation 4는 하드웨어에 지연이 있고 라이브러리/소프트웨어에 지연이 있는 것으로 알려진 시스템이며 이러한 지연을 줄이는 방법을 찾아야 합니다. 이미지가 표시될 때 HMU의 위치를 ​​예측하는 데 사용할 수 있는 게임 지연 시간을 개발자가 계산하고 줄일 수 있는 CPU 및 GPU 성능 분석 도구를 제공합니다. 엔진 대기 시간을 줄이는 것이 중요하지만, 예측을 사용하여 남아 있는 작은 지연을 마스킹하는 것이 효과적일 수 있으며, 지정하는 예측 양이 작을수록 품질이 향상됩니다.

목표는 HMD 및 컨트롤러 변환(광자로 렌더링됨)의 예측 시간을 최대한 짧게 유지하고(정확성이 총 시간보다 중요함) 낮은 지속성 글로벌 디스플레이를 유지하는 것입니다. 패널은 11.11ms 프레임 중 약 2ms 동안만 켜집니다.

위 이미지는 최고의 VR 렌더링은 아니지만 예측을 설명하는 데 도움이 됩니다.

파이프라인 아키텍처: 현재 프레임을 렌더링하는 동안 다음 프레임을 시뮬레이션합니다.

변환은 다시 예측되고 전역 c버퍼는 커밋 전에 업데이트됩니다. 이는 예측 제한으로 인해 실제로 VR에 필요하며 CPU에서 약 5도 정도 보수적으로 줄여야 합니다.

VSync 대기: 가장 간단한 VR 구현, VSync 직후 예측, 모드 #1: Present(), 백 버퍼 지우기, 픽셀 읽기; 모드 #2: Present(), 백 버퍼 지우기, 쿼리 실행. 초기 구현에는 적합하지만 이를 수행하지 마십시오. GPU는 이를 위해 설계되지 않았습니다.

"Run Start"를 사용한 VSync: VSync에서 얼마나 멀리 떨어져 있는지 어떻게 알 수 있습니까? 까다롭습니다. 그래픽 API는 이를 직접 제공하지 않습니다. Windows의 SteamVR/OpenVRAPI는 별도의 프로세스로 실행되어 IDXGIOutput::WaitForVBlank()가 호출될 때 시간을 기록하고 프레임 카운터를 증가시킵니다. 그런 다음 애플리케이션은 프레임 ID도 반환하는 getTimeSincelllastVsync()를 호출할 수 있습니다. GPU 공급업체, HMD 장치 및 렌더링 API가 이를 제공해야 합니다.

"실행 시작"에 대한 세부 정보: 잘못된 프레임을 처리하려면 GPU와 부분적으로 동기화하고 백 버퍼를 지운 후 쿼리를 삽입하고 전체 프레임을 제출하고 해당 쿼리에서 회전한 다음 Present()를 호출하여 현재 프레임에 대해 VSync의 올바른 측면에 있는지 확인하고 이제 실행 시작 시간까지 회전할 수 있습니다.

쿼리 질문이 중요한 이유는 무엇입니까? 프레임 지연이 있는 경우 쿼리는 다음 프레임에 대해 VSync 오른쪽에 있으므로 예측이 정확하게 유지됩니다(아래 주황색).

요약 실행 시작: 안정적인 1.5-2.0밀리초 GPU 성능 향상! 일반적인 상황에서는 NVIDIA Nsight 및 Microsoft GPUView에서 각각 다음 그림을 볼 수 있습니다.

15.2.3.2 타임워프(TW)

시간 왜곡에 대한 아이디어는 수십 년 동안 VR 연구에서 존재해 왔지만 John Carmack은 2014년 4월에 Oculus 소프트웨어에 특정 기능을 추가했습니다. Carmack은 Oculus DK1이 출시되기 전인 2013년 초에 이 아이디어에 대해 처음 글을 썼습니다. 표준 시간 왜곡 자체는 실제로 프레임 속도를 향상시키는 데 도움이 되지 않으며 VR에서 인지되는 지연 시간을 줄이기 위한 것도 아닙니다. VR 이전 Oculus DK1은 하드웨어보다는 소프트웨어로 인해 오늘날보다 대기 시간이 훨씬 길었습니다. Timewarp는 대기 시간을 눈에 띄지 않을 정도로 줄이기 위해 Oculus에서 사용하는 여러 소프트웨어 기술 중 하나입니다.

Timewarp는 렌더링된 프레임을 HMD로 보내기 전에 다시 투영하여 머리 회전의 변화를 표현합니다. 즉, 프레임이 렌더링을 시작하는 시간과 렌더링을 완료하는 시간 사이에 회전된 머리 방향으로 이미지를 기하학적으로 왜곡합니다. 다시 렌더링하는 데 걸리는 시간의 일부만 소요되고 프레임이 HMD로 즉시 전송되므로 결과가 사용자가 봐야 하는 것과 더 가깝기 때문에 인지된 지연 시간이 더 낮습니다.

오늘날 모든 주요 VR 플랫폼은 타임워프 개념을 사용합니다. 따라서 일반적인 믿음과는 달리 전체 프레임 속도에서도 여전히 프레임이 재투영되는 것을 볼 수 있습니다.

VR의 목표 프레임 속도가 90fps라고 가정하면 약 10밀리초 동안 GPU 정지로 인해 경험이 중단되고, 목표 프레임 속도에 도달하지 못하면 시간 왜곡이 발생하고 fps가 절반으로 줄어듭니다. 컨텍스트 우선순위화를 통해 VR 플랫폼 공급업체는 GPU 선점을 통해 비동기식 시간 왜곡을 구현할 수 있습니다. 컨텍스트 우선순위 지정은 VR 플랫폼 공급업체가 비동기식 시간 왜곡을 구현할 수 있도록 NVIDIA에서 제공하는 하위 수준 기능입니다. 이를 수행하는 방법은 우선 순위가 높은 그래픽 컨텍스트를 사용하여 GPU 선점을 활성화하는 것입니다.

Timewarp는 Oculus SDK에 구현된 기능으로, 이미지를 렌더링한 다음 렌더링된 이미지에 대해 사후 처리를 수행하여 렌더링 중 머리 움직임의 변화에 ​​따라 조정할 수 있습니다. 몇 가지 머리 자세를 사용하여 여기 보이는 것과 같은 이미지를 렌더링했다고 가정해 보겠습니다. 그러나 렌더링이 완료되면 플레이어의 머리가 이동하여 이제 약간 다른 방향을 바라보게 됩니다. 타임 워핑(Time Warping)은 이를 보상하기 위해 순수한 영상 공간 연산으로 영상을 이동시키는 방법이다. 머리가 오른쪽에 있으면 이미지가 왼쪽으로 이동하는 식입니다. 시간 왜곡은 이미지 공간 왜곡으로 인해 작은 왜곡이 발생하는 경우도 있지만 인지된 대기 시간을 줄이는 데는 매우 효과적입니다.

아래 그림은 시간 왜곡이 있는 것과 없는 것의 비교 그림입니다.

타임 워핑을 활성화하면(아래) vsync 전에 머리 포즈를 몇 밀리초 전에 다시 샘플링하고 방금 렌더링된 이미지를 새 머리 포즈로 워프할 수 있어 인지된 대기 시간을 크게 줄일 수 있습니다.

타임워프가 사용되고 게임이 일정한 프레임 속도로 렌더링되는 경우 이는 여러 프레임에 걸친 타이밍 효과입니다. 아래 그림의 녹색 막대는 메인 게임 렌더링을 나타냅니다. 플레이어가 이동함에 따라 시간이 걸리고 화면에 다른 개체가 표시됩니다. 게임이 각 프레임을 렌더링한 후 시간 왜곡을 시작하기 전에 vsync까지 기다립니다(작은 청록색 막대로 표시됨). 게임이 vsync 시간 제한 내에 계속 실행되는 한 이는 훌륭하게 작동합니다.

15.2.3.3 비동기 타임워프(ATW)

VR 렌더링에서는 콘텐츠의 복잡성이 다양합니다. 따라서 복잡한 컨텐츠의 렌더링은 한 프레임의 새로 고침 주기 내에 완료되지 않을 수 있습니다. 따라서 화면을 새로 고친 후에는 새 콘텐츠가 생성되지 않고 사용자에게 화면이 정지된 것으로 표시됩니다. 이러한 문제를 해결하기 위해 업계에서는 ATW 렌더링 기술을 제안했습니다. 이 기술은 포즈 예측을 통해 프레임 내 머리 자세를 결정하고, 이전 프레임 이미지 생성 시 포즈를 기반으로 포즈 차이를 계산하고, 포즈 차이를 기반으로 이전 프레임 이미지의 위치를 ​​변경하고, 새 프레임에서 중간 이미지를 생성함으로써 현재 프레임 부족으로 인한 프리징 문제를 해결합니다. ATW 렌더링 기술은 대부분의 경우 사용자에게 원활한 시각적 경험을 보장할 수 있습니다. 이론적으로 ATW는 한 프레임의 이미지를 기반으로 새로운 이미지를 지속적으로 생성할 수 있습니다. 그러나 지속적인 왜곡으로 인해 생성된 이미지와 실제 렌더링된 이미지 사이에는 오차가 누적됩니다. 결과적으로 이미지 품질이 저하됩니다.

하지만 vsync에서는 게임이 100% 안정적으로 실행되지 않습니다. PC 운영 체제는 이를 보장할 수 없습니다. 때때로 Windows는 백그라운드나 다른 곳에서 파일 인덱싱을 시작하기로 결정하고 게임이 지연되어 장애가 발생합니다. 말더듬은 항상 짜증나지만 VR에서는 정말 짜증납니다. 이전 프레임이 헤드셋에 붙어 있으면 즉시 현기증이 발생할 수 있습니다.

ATW(Async Timewarp)가 등장하는 곳입니다. Timewarp는 애플리케이션이 렌더링을 완료할 때까지 기다릴 필요가 없다는 아이디어입니다. Timewarp는 애플리케이션이 렌더링을 완료했는지 여부에 관계없이 vsync 전에 깨어나 작업을 완료하는 GPU에서 실행되는 별도의 프로세스처럼 작동해야 합니다. 이렇게 할 수 있다면 메인 렌더링 패스가 뒤쳐질 때마다 이전 프레임을 다시 왜곡할 수 있습니다. 결과적으로 우리는 HMD에서 이미지 끊김 현상을 견딜 필요가 없습니다. 앱이 중단되거나 프레임이 삭제되는 경우에도 지연 시간이 짧은 머리 추적이 계속됩니다.

지)

NVIDIA는 높은 우선순위 그래픽 컨텍스트, 다른 GPU 작업 선점, 메인 렌더링 - 일반 컨텍스트, 타임워프 렌더링 - 높은 우선순위 컨텍스트를 지원합니다. 현재 GPU는 드로우 콜 경계에서만 전환할 수 있는 드로잉 레벨 선점(preemption)을 지원합니다! 긴 지연 컨텍스트 스위치를 그립니다. 여전히 기본 프레임 속도(90Hz)로 렌더링을 시도 중입니다! 더 나은 경험은 비동기 시간 왜곡이 안전망이라는 것입니다. 긴 도면은 지연을 유발할 수 있고, 시간 분할이 약 1ms를 초과하는 그래픽, 무거운 후처리가 화면 공간에서 분할됩니다.

NV는 또한 데스크톱이 VR 헤드셋으로 확장되는 것을 방지하고 운영 체제에서 디스플레이를 숨기지만 더 나은 사용자 경험을 위해 VR 애플리케이션이 직접 렌더링되도록 하는 직접 모드를 지원합니다. 전면 버퍼 렌더링의 경우 일반적으로 D3D11에서는 액세스할 수 없지만 직접 모드를 사용하면 전면 버퍼에 액세스할 수 있어 낮은 수준의 대기 시간 최적화, vblank 중 렌더링 및 빔 레이싱이 가능합니다.

비동기식 시간 왜곡은 동일한 형상 왜곡 개념을 사용하여 손실된 프레임을 보상합니다. 현재 프레임이 제시간에 렌더링을 완료하지 못하는 경우 ATW는 최신 추적 데이터를 사용하여 이전 프레임을 다시 투영합니다. 렌더링 이후가 아니라 렌더링과 동시에 발생하기 때문에 "비동기"라고 합니다. 실제 프레임이 제시간에 렌더링을 완료할지 여부를 알기 전에 합성 프레임이 준비됩니다.

ATW는 2014년 말 Gear VR Innovator Edition에서 처음 출시되었습니다. 그러나 2016년 3월 Rift 소비자가 출시될 때까지 PC에서는 사용할 수 없었습니다. 이 기능이 최근 GPU에 추가된 하드웨어 기능에 의존한다는 점은 Rift가 GeForce 7 시리즈 카드나 R9 시리즈 이전의 AMD 카드를 지원하지 않는 이유 중 하나입니다. 2016년 10월 Valve는 비동기 재투영(Asynchronous Reprojection)이라는 유사한 기능을 SteamVR에 추가했습니다. 이 기능은 처음에는 NVIDIA GPU만 지원했지만 2017년 4월에 AMD GPU에 대한 지원이 추가되었습니다. 다음 세 그림은 다양한 모드의 게임 루프를 비교한 것입니다.

위: 기본 게임 루프.

중간: 프레임 속도가 일정하게 유지되면 경험이 현실감 있고 즐겁게 느껴집니다. 시간 내에 발생하지 않으면 이전 프레임이 표시되므로 현기증이 날 수 있습니다. 가운데 이미지는 기본 게임 루프의 지터 예를 보여줍니다.

하단: ATW는 렌더링된 이미지를 약간 움직여 머리 움직임의 변화에 맞게 조정하는 기술입니다. 이미지는 수정되었으나 머리가 많이 움직이지 않아 변화가 미미합니다. 또한 사용자의 컴퓨터, 게임 디자인 또는 운영 체제 문제를 해결하기 위해 ATW는 불규칙성 또는 예기치 않은 프레임 속도 저하 순간을 수정하는 데 도움을 줄 수 있습니다. 아래 이미지는 ATW 적용 시 프레임 저하의 예를 보여줍니다.

15.2.3.4 인터리브 재투영(IR)

비동기 재투영이 SteamVR에 추가되기 전에 Valve의 플랫폼에는 인터리브 재투영(IR)이 있었습니다. ATW와 마찬가지로 IR은 항상 켜져 있는 시스템이 아니라 신디사이저에 의해 자동으로 켜지고 꺼집니다. 응용 프로그램이 몇 초에 걸쳐 여러 프레임을 계속 삭제하는 경우 IR은 응용 프로그램을 강제로 절반 프레임 속도(45FPS)로 실행한 다음 매초마다 하나의 프레임을 합성하므로 "놀라운" 현상이 발생합니다. 인터리브 재투영은 실제로 두 이미지 아티팩트를 공간적으로 일관되게 만들기 때문에 비동기 재투영에 비해 몇 가지 인지적 이점이 있습니다. 2018년 SteamVR 모션 스무딩 기술이 출시되면서 인터레이스 재투영 기술은 더 이상 사용되지 않게 되었습니다.

15.2.3.5 비동기 Spacewarp(ASW) / 모션 스무딩

시간 왜곡(현재) 및 재투영은 회전 추적에만 사용됩니다. 머리의 위치 이동이나 장면에 있는 다른 개체의 움직임은 고려하지 않습니다. 2016년 12월 Oculus는 이 문제를 해결하기 위해 ASW(Asynchronous Spacewarp)를 출시했습니다. ASW는 본질적으로 이전 프레임 간의 차이(즉, 동작)를 사용하여 다음 프레임이 어떻게 보일지 추정하는 빠른 외삽 알고리즘입니다. 이름에도 불구하고 ASW가 항상 활성화되는 것은 아닙니다. 과거 SteamVR의 시차적 재투영과 마찬가지로 애플리케이션이 몇 초 내에 여러 프레임을 지속적으로 드롭하면 ASW가 자동으로 활성화됩니다. 그런 다음 애플리케이션이 절반 프레임 속도(45FPS)로 실행되도록 강제하고 초당 하나의 프레임을 종합적으로 생성합니다. 따라서 ASW는 ATW를 대체할 수 없으며 ATW는 항상 활성화되어 있으며 필요할 때 ASW가 시작됩니다.

ASW에는 프레임의 색상 정보만 있고 물체의 깊이는 없기 때문에 이미지에 명백한 아티팩트가 있는 경우가 많습니다. 2018년 11월 Valve는 Motion Smoothing이라는 유사한 기능을 SteamVR에 추가했습니다.

Asynchronous Spacewarp 2.0은 기술에 대한 깊은 이해를 통합하여 기술의 품질을 크게 향상시키는 ASW의 향후 업데이트입니다. 이 기술을 발표하면서 Oculus는 2.0 업데이트에서 제거할 시각적 결함의 예로 다음 장면을 선보였습니다.

그러나 현재까지의 다른 모든 기술과 달리 ASW 2.0은 어떤 응용 프로그램에서도 실행될 수 없습니다. 개발자는 프레임마다 깊이 버퍼를 커밋하거나 ASW 1.0으로 대체해야 합니다. 다행히도 Unity와 Unreal Engine이 함께 대다수의 VR 앱을 지원하지만 이제 Oculus 통합을 사용할 때 깊이가 기본적으로 적용됩니다.

15.2.3.6 위치 타임워프(PTW)

PTW는 ATW(Asynchronous Timewarp)에 대한 향후 업데이트이며, ATW는 ASW 2.0에서 고품질 위치 수정을 추가하는 데 사용한 것과 동일한 깊이 버퍼를 사용합니다. 현재의 ATW와 마찬가지로 PTW 업데이트는 항상 활성화되므로 프레임이 손실된 경우 복합 프레임이 제시간에 준비됩니다. Facebook은 사전에 위치 지터가 더 이상 없기 때문에 PTW가 ASW 활성화 또는 비활성화 전환을 보다 원활하게 만든다고 주장합니다. 그러나 ASW 2.0과 마찬가지로 PTW는 깊이 버퍼를 제출하는 애플리케이션에서만 작동합니다. ASW 2.0은 더 이상 HMD의 움직임을 고려하지 않고 모두 PTW에 의존하기 때문에 PTW는 ASW 2.0과 동일한 업데이트를 가질 것이라고 합니다.

간단히 말해서, 각 기술의 역할은 다음과 같습니다.

  • 시간 왜곡: 인지된 대기 시간을 줄입니다.

  • ATW(비동기 시간 왜곡/재투영): 회전은 손실된 프레임을 보상합니다.

  • ASW(Asynchronous Spacewarp)/모션 스무딩: 프레임 속도가 낮을 ​​때 애플리케이션 속도를 45FPS로 낮추고 과거 프레임에서 모션을 추정하여 2프레임마다 합성합니다.

각 기술을 비교하는 방법은 다음과 같습니다.

"자동 전환" 기술이 항상 활성화되는 것은 아닙니다. 대신, 컴포지터가 프레임 속도가 몇 초 이상 낮다는 것을 감지하면 모드 중 하나가 활성화됩니다. 활성화되면 컴포지터는 실행 중인 애플리케이션을 강제로 절반 프레임 속도(현재 HMD의 경우 45FPS)로 렌더링합니다. 합성기는 이전 프레임 분석을 기반으로 추가 프레임을 합성하고 이를 HMD 추적 데이터와 결합합니다. GPU 사용률이 다시 감소하면 컴포지터는 모드를 비활성화하고 애플리케이션을 90FPS로 되돌립니다.

15.2.3.7 대기 시간 및 지연 최적화

개발자가 시스템 지연 시간(예: 디스플레이 업데이트 속도, 하드웨어 지연 시간 등)의 여러 측면을 제어할 수는 없지만 VR 경험이 프레임을 지연시키거나 삭제하지 않도록 하는 것이 중요합니다. 많은 게임은 처리하고 화면에 렌더링하는 요소의 수나 복잡한 요소로 인해 속도가 느려집니다. 기존 비디오 게임에서는 사소한 불편함에 불과하지만 VR 사용자에게는 매우 불편할 수 있습니다. 지연 시간은 사용자 머리의 움직임과 화면에 표시되는 업데이트된 이미지 사이의 총 시간(광자로의 이동)으로 정의됩니다. 여기에는 센서 응답, 융합, 렌더링, 이미지 전송 및 디스플레이 응답 시간이 포함됩니다. 대기 시간의 영향에 대한 과거 연구에서는 다소 엇갈린 결과가 나왔습니다. 많은 전문가들은 머리 움직임과 모니터의 해당 업데이트 사이의 지연으로 인해 감각 충돌과 전정안구 반사 오류가 발생할 수 있으므로 지연 시간을 최소화하여 불편함을 줄일 것을 권장합니다. 따라서 가능한 한 지연을 최소화하는 것이 좋습니다.

특히 머리 장착형 디스플레이에 대한 일부 연구에 따르면 고정된 대기 시간은 48밀리초만큼 짧든 300밀리초만큼 길든 거의 동일한 수준의 불편함을 유발하는 것으로 나타났습니다. 그러나 조종석과 운전 시뮬레이터의 가변적이고 예측할 수 없는 지연은 평균 지속 시간이 길어질수록 더 큰 불편함을 야기합니다. 이는 사람들이 결국 일관되고 예측 가능한 지연에 익숙해질 수 있지만 평균적으로 변동하고 예측할 수 없는 지연은 지속 시간이 길어질수록 더 혼란스럽다는 것을 의미합니다.

Oculus는 공식적으로 VR을 강제하기 위한 임계값은 20밀리초 이하의 지연이어야 한다고 믿습니다. 이 범위를 벗어나면 사용자는 환경에 대한 몰입감과 편안함이 덜하다고 보고합니다. 지연 시간이 60밀리초를 초과하면 사람의 머리 움직임과 가상 세계의 움직임 사이의 해리가 동기화되지 않은 느낌을 받기 시작하여 불편함과 방향 감각 상실을 유발합니다. 긴 잠복기는 불편함의 주요 원인 중 하나로 간주됩니다. 편의성 문제와 관계없이 대기 시간은 사용자 상호 작용 및 현재 상태를 방해할 수 있습니다. 이상적인 세계에서는 0밀리초에 가까울수록 좋습니다. 지연시간이 불가피하다면 변동폭이 클수록 불편함은 커집니다 목표는 가변지연시간을 최대한 줄이고 줄이는 것입니다.

15.2.4 렌더링

20세기와 21세기의 컴퓨터 그래픽 모델을 비교하면 다음과 같다.

인간의 인식 한계는 최신 VR 장비의 100,000~100만 배를 초과합니다.

최적화된 방법은 분할 렌더링입니다.

사후 추적 업데이트 – 디스플레이 변조 중에 추적을 삽입하여 프레임 속도보다 빠르게 위치를 업데이트합니다.

XR 쌍안경 디스플레이의 경우 교차점과 디스플레이 평면의 차이로 인해 충돌이 있습니다.

NV의 GameWorks VR은 VR 장비 및 게임 개발을 위한 SDK입니다. 2015년 초부터 이 버전은 다음 기능을 지원합니다.

VR SLI가 듀얼 GPU 크로스파이어 렌더링인 경우:

크로스 프레임 SLI와 VR SLI 간의 지연 시간 비교는 다음과 같습니다.

VR SLI 구현 다이어그램은 다음과 같습니다.

VR의 두 가지 보기에 대한 렌더링 표현의 개선 사항은 다음과 같습니다.

cpp
// 최적화
for (each view) 
    find_objects();
    for (each object) 
        update_constants();
        render();

// 최적화
find_objects(); 
for (each object)
    for (each view) 
         update_constants();
    render();

15.2.4.1 다중 해상도 렌더링 및 포비티드 렌더링

주변에서 낮은 해상도를 렌더링하는 경우 앨리어싱/깜빡임에 주의해야 하며, 주변에서 부드러운 이미지 흐림을 렌더링하면 사용자는 "터널 비전" 효과를 경험하게 되며 연구에 따르면 주변에서 저주파 콘텐츠의 대비를 높여야 합니다. 사용자의 시선을 추적하고 시선 지점에서 멀어질수록 점점 더 낮은 해상도로 렌더링됩니다.

다음은 다양한 고정 지점에서 렌더링 이미지를 비교한 것입니다.

넓은 FOV를 지원하기 위해 헤드셋은 일반적으로 렌즈 원근법을 사용하지만 렌즈는 교란, 핀쿠션 왜곡 및 색수차(광 굴절의 다양한 파장)와 같은 문제를 야기합니다.

사진 렌즈 왜곡의 콜백 소프트웨어 수정:

VR 렌더링의 렌즈 왜곡에 대한 소프트웨어 보상, 1단계: 기존 그래픽 파이프라인을 사용하여 각 눈의 전체 해상도로 장면을 렌더링합니다. 2단계: 물리적 렌즈 왜곡 후 장면이 올바르게 보이도록 이미지를 왜곡합니다(색수차는 R, G, B에 대한 별도의 왜곡을 사용하여 대략적으로 보정할 수 있음).

래스터라이제이션된 그래픽은 평면에 대한 원근 투영을 기반으로 하며 넓은 시야에 걸쳐 VR 렌더링에 필요한 높은 FOV에서 이미지를 왜곡합니다. 잠재적인 솔루션 공간: 뒤틀린 디스플레이, 균일한 각도 해상도를 달성하기 위한 레이캐스팅, 조각별 선형 투영 평면(각 화면 프레임에 대해 서로 다른 평면)을 사용한 렌더링.

VR 왜곡으로 인해 렌더링 중에 이미지의 네 가장자리가 압축되어 애플리케이션 콘텐츠에서 렌더링된 많은 수의 픽셀을 리샘플링한 후에는 표시할 수 없습니다. 실제로 이러한 픽셀의 렌더링 오버헤드를 줄일 수 있습니다. 업계의 GPU 공급업체는 렌더링 오버헤드를 줄이기 위해 다중 해상도 셰이딩 기술을 제안했습니다. 이 기술에서는 이미지가 그리드로 분할됩니다. 중앙 영역은 원래 해상도를 유지하며 네 측면과 모서리의 해상도는 각각 1/2 및 1/4로 압축됩니다(필요에 따라 변경 가능). 애플리케이션 콘텐츠를 렌더링하는 동안 GPU는 즉시 이미지를 그립니다.

VR 장치에서 렌더링된 이미지는 렌즈의 광학 효과를 상쇄하기 위해 왜곡되어야 합니다. 아래 이미지에서는 모든 것이 휘어지고 왜곡되어 보이지만, 렌즈를 통해 보면 시청자는 왜곡되지 않은 이미지를 인지하게 됩니다.

문제는 GPU가 기본적으로 이러한 왜곡된 보기로 렌더링할 수 없기 때문에 삼각형 래스터라이제이션가 더 복잡해진다는 것입니다. 현재 VR 플랫폼은 모두 먼저 일반 이미지(왼쪽)를 렌더링한 다음 후처리를 통해 이미지를 왜곡된 뷰(오른쪽)로 리샘플링함으로써 이 문제를 해결합니다.

변형 과정에서 어떤 일이 일어나는지 살펴보면 이미지의 중심은 동일하게 유지되지만 가장자리가 상당히 눌려지는 것을 알 수 있습니다. 이는 이미지의 가장자리를 과도하게 음영 처리한다는 의미입니다. 우리는 화면에 전혀 표시되지 않는 수많은 픽셀을 생성하고 있습니다. 이는 워핑 과정에서 버려질 뿐이므로 작업이 낭비되고 성능이 저하됩니다.

다중 해상도 셰이딩의 개념은 이미지를 여러 뷰포트로 분할하는 것입니다(아래 이미지는 3x3 그리드입니다). "중앙" 뷰포트는 동일한 크기로 유지하지만 가장자리 주변의 모든 뷰포트는 축소됩니다. 우리가 원하는 왜곡된 이미지에 대한 더 나은 근사치이지만 너무 많은 픽셀을 낭비하지 않습니다. 그리고 음영 처리할 픽셀 수가 적기 때문에 렌더링 속도가 더 빠릅니다. 가장자리를 얼마나 세게 축소하느냐에 따라 픽셀의 25%~50%를 저장할 수 있으며 이는 픽셀 셰이딩 속도를 1.3배~2배 향상시킵니다.

다음은 여러 렌더링 모드에 대한 음영 처리된 픽셀을 비교한 것입니다.

15.2.4.2 입체 및 다중 뷰 렌더링

시차는 두 개의 입체 이미지에 투사된 3D 점의 상대적 거리입니다. 입체영상을 구현하기 위한 공통기술이기도 하다. 다음 그림은 세 가지 시차 사례를 보여줍니다.

비전 시스템은 수직 시차가 아닌 수평 시차만 사용합니다! 거친 토인 방식은 수직 시차를 유발하여 시각적 불편함을 유발합니다(아래 왼쪽 그림).

OpenGL/WebGL을 사용한 입체 렌더링: 뷰 매트릭스. 뷰 행렬과 투영 행렬을 수정해야 하며 렌더링 파이프라인은 변경되지 않은 채 유지됩니다. 이 두 행렬만 변경되지 않고 두 이미지는 순서대로 렌더링되어야 합니다. 먼저 뷰 행렬을 살펴보고 회전 및 이동 행렬을 사용하여 눈, 중심, 위쪽 매개변수에서 뷰 행렬을 생성하는 자신만의 LookAt 함수를 작성합니다. THREE.Matrix4().lookAt() 함수를 사용하지 마십시오. 제대로 작동하지 않습니다! OpenGL을 사용하여 입체 뷰 행렬을 구성하는 과정은 다음과 같습니다.

위에서 논의된 원근 투영은 축=대칭입니다. 또한 THREE.Matrix4().makePerspective(left,right,top,bottom,znear,zfar)를 사용하여 수행할 수 있는 비대칭 축외 프러스텀를 설정하는 다른 방법이 필요합니다.

축상 및 축외 뷰 프러스텀 구성의 개략도는 다음과 같습니다.

OpenGL을 사용하여 입체 이미지를 그리는 가장 효율적인 방법:

  1. 색상 및 깊이 버퍼를 지웁니다.

  2. 장면을 빨간색 채널로만 렌더링하도록 왼쪽 모델 뷰와 투영 매트릭스를 설정합니다.

  3. 깊이 버퍼를 지웁니다.

  4. 장면을 녹색 및 파란색 채널로만 렌더링하도록 오른쪽의 모델 보기 및 투영 매트릭스를 설정합니다.

우리는 약간 더 복잡한 방식으로 이를 수행할 것입니다(어차피 다른 작업이 필요함): 다중 렌더링 패스, 오프스크린(프레임) 버퍼로 렌더링.

OpenGL 프레임 버퍼 일반적으로 (프레임) 버퍼는 창 관리자(예: 브라우저)에 의해 제공됩니다. 대부분의 단일 채널 응용 프로그램에는 두 개의 (이중) 버퍼가 있습니다. 백 버퍼와 백 버퍼로 렌더링되는 프론트 버퍼입니다. 완료되면 버퍼를 교체합니다(WebGL이 이 작업을 수행합니다!). 장점은 렌더링에 시간이 걸리고 삼각형이 화면에 어떻게 그려지는지 사용자가 보는 것을 원하지 않는다는 것입니다. 많은 스테레오 애플리케이션에서는 최종 이미지만 표시합니다. 4개 버퍼: 전면/후면 왼쪽 및 오른쪽 버퍼, 왼쪽 및 오른쪽 이미지를 후면 버퍼로 렌더링한 다음 두 개를 함께 바꿉니다.

보다 일반적인 접근 방식은 오프스크린 버퍼를 사용하는 것입니다. OpenGL에서 가장 일반적인 형태의 오프스크린 버퍼: "텍스처로 렌더링" 개념을 사용하지만 색상, 깊이 및 기타 중요한 조각별 정보의 여러 "첨부"가 있는 프레임 버퍼 개체, 가능한 많은 프레임 버퍼 개체, 모두 GPU에서 "라이브"(메모리 전송 없음), 색상당 비트 심도: 색상 첨부의 경우 8비트, 16비트, 32비트; 깊이는 24비트입니다.

FBO는 다중 렌더 패스에 필수적입니다! 패스 1: FBO의 색상과 깊이를 렌더링하고, 패스 2: 텍스처 직사각형을 렌더링합니다. 프래그먼트 셰이더에서 FBO에 액세스합니다.

인간 망막의 흐림(초점 이탈) 효과를 시뮬레이션하려면 DOF(뎁스 오브 필드(DoF)) 후처리가 필요합니다. 여기서는 무시되는 많은 방법이 있습니다.

일반적인 애플리케이션 렌더링과 달리 각 프레임의 VR 렌더링에는 왼쪽 눈 이미지와 오른쪽 눈 이미지를 동시에 렌더링해야 합니다. 각 프레임에서 왼쪽 눈의 이미지에 대한 렌더링 작업과 오른쪽 눈의 이미지에 대한 렌더링 작업이 제출됩니다. 따라서 VR 렌더링은 일반 응용 프로그램 렌더링보다 CPU/GPU 리소스를 두 배나 차지합니다. 이러한 문제를 해결하기 위해 업계에서는 하나의 태스크만 제출한 후 좌안 영상과 우안 영상을 동시에 렌더링할 수 있는 멀티뷰 렌더링 기술을 제안해왔다. 왼쪽 눈과 오른쪽 눈에 대한 이미지의 정보는 대부분 동일하며 이미지의 차이는 약간만 다릅니다. 따라서 멀티뷰 렌더링 기술에서는 CPU가 렌더링 작업과 시차 정보를 GPU에 제출하기만 하면 GPU가 왼쪽 눈과 오른쪽 눈에 대한 이미지를 렌더링할 수 있어 CPU 리소스 사용량을 크게 줄이고 프레임 속도를 높일 수 있습니다.

VR과 같은 렌더링을 최적화하는 데 사용되는 MultiView 비교 차트입니다. 위: MultiView 모드 없이 렌더링, 두 눈 각각 그리기 지침을 제출합니다. 중간: 기본 MultiView 모드, 제출 지침을 재사용하고 GPU 레이어에 추가 명령 목록을 복사합니다. 하단: DC, 명령 목록 및 형상 정보를 재사용할 수 있는 고급 MultiView 모드.

Unity는 다음과 같은 VR 렌더링 모드를 지원합니다.

  • 멀티 카메라. 각 눈에 대한 뷰를 렌더링하기 위한 가장 간단한 방법은 렌더링 루프를 두 번 실행하는 것입니다. 각 눈은 렌더링 루프의 자체 반복을 구성하고 실행합니다. 마지막으로 디스플레이 장치에 제출할 수 있는 두 개의 이미지가 준비됩니다. 기본 구현에서는 각 눈에 하나씩 두 개의 Unity 카메라를 사용하며, 이는 입체 이미지 생성 프로세스 전반에 걸쳐 사용됩니다. 이는 Unity에서 XR을 지원하는 원래 방법이었으며 여전히 타사 HMD 플러그인에서 제공됩니다. 이 접근 방식은 작동하지만 다중 카메라는 무차별 대입에 의존하며 CPU 및 GPU 측면에서 효율성이 가장 낮습니다. CPU는 렌더 루프에서 정확히 두 번 반복해야 하며, GPU는 눈이 두 번 그리는 개체의 캐싱을 활용하지 못할 가능성이 높습니다.

  • 멀티 패스. 멀티패스는 XR 렌더링 루프를 최적화하려는 Unity의 초기 시도입니다. 핵심 아이디어는 렌더링 루프의 뷰 독립적인 부분을 추출하는 것입니다. 즉, XR 눈 시점에 명시적으로 의존하지 않는 모든 작업은 각 눈에 대해 수행될 필요가 없습니다. 이 최적화의 가장 확실한 후보는 그림자 렌더링으로, 그림자는 카메라를 보는 사람의 위치에 명시적으로 의존하지 않습니다. Unity는 실제로 계단식 그림자 맵을 생성한 다음 그림자를 화면 공간에 매핑하는 두 단계로 그림자를 구현합니다. 멀티 패스의 경우 계단식 그림자 맵 세트를 생성한 다음 두 개의 화면 공간 그림자 맵을 생성할 수 있습니다. 화면 공간 그림자 맵은 뷰어의 위치에 따라 달라지기 때문입니다. 화면 공간 그림자 맵은 그림자 맵 생성 루프가 상대적으로 밀접하게 결합되어 있기 때문에 그림자 생성 아키텍처로 인해 지역성의 이점을 얻습니다. 이는 유사한 단계로 돌아가기 전에 렌더 루프에서 전체 반복이 필요한 나머지 렌더링 작업 부하와 비교할 수 있습니다(예: 나머지 렌더 루프 단계로 구분된 눈별 불투명 패스). 두 눈 사이에 공유될 수 있는 또 다른 단계는 처음에는 명확하지 않을 수 있습니다. 두 눈 사이에 단일 컬링이 수행될 수 있습니다. 원래 구현에서는 프러스텀 컬링을 사용하여 각 눈에 대해 하나씩 두 개의 객체 목록을 생성했습니다. 그러나 두 눈이 공유하는 균일한 컬링 프러스텀를 만드는 것은 가능합니다. 즉, 각 눈은 단일 눈 컬링 프러스텀를 사용할 때보다 약간 더 많이 렌더링되지만 단일 컬링의 이점은 일부 추가 버텍스 셰이더, 클리핑 및 래스터라이제이션 비용보다 큽니다.

  • 단일 패스. 단일 패스 스테레오 렌더링은 전체 렌더 루프가 특정 부분에 대해 두 번 또는 두 번이 아니라 한 번 전달된다는 것을 의미합니다.

이 두 가지 그리기를 수행하려면 모든 상수 데이터가 바인딩되고 인덱스가 있는지 확인해야 합니다. 줄거리는 어떻게 되나요? 각각의 드로우 콜을 수행하는 방법은 무엇입니까? 멀티 패스에서는 양쪽 눈에 자체 렌더 타겟이 있지만 연속적인 그리기 호출에서 렌더 타겟을 전환하는 데 비용이 너무 많이 들기 때문에 단일 패스에서는 이를 수행할 수 없습니다. 비슷한 옵션은 렌더 대상 배열을 사용하는 것이지만 대부분의 플랫폼의 기하 셰이더에서 슬라이스 인덱스를 파생해야 합니다. 이는 GPU 비용이 많이 들고 기존 셰이더에 방해가 될 수도 있습니다.

확실한 해결책은 Double Wide 렌더 타겟을 사용하고 그리기 호출 간에 뷰포트를 전환하여 각 눈이 Double Wide 렌더 타겟의 절반으로 렌더링되도록 하는 것이었습니다. 뷰포트를 전환하면 비용이 발생하지만 렌더 대상을 전환하는 것보다 적고 지오메트리 셰이더를 사용하는 것보다 적습니다(Double Wide에는 특히 후처리와 관련하여 자체적인 문제가 있지만). 뷰포트 배열을 사용하는 관련 옵션도 있지만 인덱스는 지오메트리 셰이더에서만 파생될 수 있기 때문에 렌더 타겟 배열과 동일한 문제가 있습니다.

이제 두 눈을 렌더링하기 위해 두 번의 연속 그리기를 시작하는 솔루션이 있으며, 지원 인프라 구성이 필요합니다. 멀티패스에서는 단일 뷰 렌더링과 유사하기 때문에 기존 뷰 및 프로젝션 매트릭스 인프라를 사용할 수 있으며 뷰 및 프로젝션 매트릭스를 현재 눈의 매트릭스로 바꾸면 됩니다. 그러나 단일 채널의 경우 상수 버퍼 바인딩을 불필요하게 전환하고 싶지는 않습니다. 따라서 두 눈의 뷰 및 프로젝션 행렬을 함께 묶고 unity_StereoEyeIndex를 사용하여 인덱싱할 수 있으며 도면 간에 전환할 수 있습니다. 이를 통해 셰이더 인프라는 셰이더 패스 중에 렌더링할 뷰 및 투영 행렬 세트를 선택할 수 있습니다.

또 다른 세부 사항: 뷰포트 및 unity_StereoEyeIndex 상태의 변경을 최소화하기 위해 왼쪽, 오른쪽, 왼쪽, 왼쪽 등을 그리는 대신 왼쪽, 오른쪽, 오른쪽, 왼쪽, 오른쪽 등의 리듬을 사용할 수 있도록 눈 그리기 모드를 수정할 수 있습니다. 이를 통해 교대로 케이던스를 바꾸는 대신 상태 업데이트 수를 절반으로 줄일 수 있습니다. 컬링과 섀도잉에 이미 최적화되어 있기 때문에 멀티패스보다 두 배 빠르지는 않지만 여전히 각각의 시선을 예약하고 뷰포트를 전환하는 동시에 약간의 CPU 및 GPU 비용이 발생합니다.

  • 스테레오 인스턴싱(단일 패스 인스턴스). 렌더 타겟 배열은 입체 렌더링을 위한 자연스러운 솔루션입니다. 눈 텍스처는 렌더 대상 배열에 사용할 수 있는 형식과 크기를 공유하지만 배열 슬라이스를 내보내기 위해 기하 셰이더를 사용하는 것은 큰 단점입니다. 우리가 정말로 원하는 것은 버텍스 셰이더에서 렌더 대상 배열 인덱스를 내보낼 수 있어 통합이 더 쉽고 성능이 향상되는 것입니다. 버텍스 셰이더에서 렌더 대상 배열 인덱스를 내보내는 기능은 실제로 일부 GPU 및 API에 존재하며 점점 더 보편화되고 있습니다. DX11에서 이 기능은 함수 옵션 VPAndRTArrayIndexFromAnyShaderFeedingRasterizer로 표시됩니다.

이제 렌더링 대상 배열의 어느 조각을 지정할 수 있으므로 해당 조각을 어떻게 선택합니까? 우리는 단일 채널 더블 와이드의 기존 인프라를 활용합니다. unity_StereoEyeIndex를 사용하여 셰이더에서 SV_RenderTargetArrayIndex 의미 체계를 채울 수 있습니다. API 측면에서는 동일한 뷰포트를 사용하여 대상 배열의 두 조각을 모두 렌더링할 수 있으므로 더 이상 뷰포트를 전환할 필요가 없습니다. 버텍스 셰이더에서 인덱싱되도록 행렬을 구성했습니다.

두 개의 그리기를 실행하고 각 그리기 전에 상수 버퍼에서 unity_StereoEyeIndex 값을 전환하는 기존 기술을 계속 사용할 수 있지만 더 효율적인 기술이 있습니다. GPU 인스턴스화를 사용하여 단일 그리기 호출을 발행하고 GPU가 양쪽 눈에 그리기를 다중화하도록 할 수 있습니다. 그려지는 기존 인스턴스 수를 두 배로 늘릴 수 있습니다(인스턴스가 사용되지 않으면 인스턴스 수를 2로 설정하기만 하면 됩니다). 그런 다음 버텍스 셰이더에서 인스턴스 ID를 디코딩하여 렌더링할 눈을 결정할 수 있습니다.

이 기술을 사용함으로써 얻을 수 있는 가장 큰 효과는 API 측에서 생성되는 그리기 호출 수가 실제로 절반으로 줄어들어 CPU 시간이 많이 절약된다는 것입니다. 또한 GPU 자체는 두 개의 별도 그리기 호출을 처리할 필요가 없기 때문에 생성된 작업량이 동일하더라도 그리기를 더 효율적으로 처리할 수 있습니다. 또한 기존 단일 패스에서 했던 것처럼 그리기 사이에 뷰포트를 변경할 필요가 없어 상태 업데이트를 최소화할 수도 있습니다.

참고: 이 방법은 Windows 10 또는 HoloLens에서 데스크톱 VR 환경을 실행하는 사용자에게만 사용할 수 있습니다.

  • 단일 패스 멀티 뷰. 멀티뷰는 일부 OpenGL/OpenGL ES 구현에서 사용할 수 있는 확장으로, 드라이버 자체가 두 눈 사이의 개별 그리기 호출 멀티플렉싱을 처리합니다. 드라이버는 그리기 호출을 명시적으로 인스턴스화하고 셰이더의 눈 인덱스에 대한 인스턴스를 디코딩하는 대신 그리기를 복사하고 셰이더에서 배열 인덱스(gl_ViewID를 통해)를 생성하는 일을 담당합니다. 입체 인스턴스화와는 다른 낮은 수준의 구현 세부 사항이 있습니다. 버텍스 셰이더가 래스터라이제이션할 렌더 대상 배열 슬라이스를 명시적으로 선택하는 것이 아니라 드라이버 자체가 렌더 대상을 결정합니다. gl_ViewID는 뷰 관련 상태를 계산하는 데 사용되지만 렌더 대상을 선택하는 데는 사용되지 않습니다. 사용 시 이는 개발자에게 중요하지 않지만 흥미로운 세부 사항입니다. 다중 보기 확장을 사용하는 방식으로 인해 단일 프로세스 인스턴스용으로 구축된 동일한 인프라를 사용할 수 있으며 개발자는 동일한 기능(스캐폴딩)을 사용하여 두 단일 패스 기술을 모두 지원할 수 있습니다.

다음은 Unity의 다양한 VR 렌더링 기술의 성능 비교입니다.

위 이미지에서 볼 수 있듯이 단일 패스 및 단일 패스 인스턴스화는 다중 프로세스에 비해 상당한 CPU 이점을 나타냅니다. 그러나 단일 채널로 전환하면 이미 많은 CPU 오버헤드가 절약되므로 단일 채널과 단일 채널 인스턴스화 간의 델타는 상대적으로 작습니다. 단일 패스 인스턴스화는 그리기 호출 수를 줄이지만 이 비용은 장면 그래프 처리에 비해 매우 낮습니다. 대부분의 최신 그래픽 드라이버가 다중 스레드라는 점을 고려하면 예약된 CPU 스레드에서 그리기 호출을 실행하는 것이 매우 빠를 수 있습니다.

15.2.4.3 라이트 필드 렌더링

기존 VR 영상 방식은 기본적으로 양안 시차를 이용한 2D 영상 방식이다. 눈의 초점과 집중점은 오랫동안 같은 위치에 머물지 않습니다. 전자는 스크린 평면에 위치하고, 후자는 양안 시차에 의해 생성된 가상 평면에 위치합니다. 결과적으로 변연계 조절 충돌이 발생하여 현기증 및 몰입감 상실과 같은 신체적 불편을 초래할 수 있습니다.

현실 세계에서 육안으로 보이는 것을 복원하면 완벽한 몰입도가 복원됩니다. 초점 거리를 변경함으로써 눈은 물체 표면에서 서로 다른 거리, 위치, 방향으로 반사된 빛을 수집할 수 있습니다. 이 모든 것의 완전한 세트가 빛의 장입니다. 업계 공급업체는 라이트 필드 정보를 수집하는 라이트 필드 카메라를 개발했습니다. 라이트 필드 렌더링 기술은 수집된 라이트 필드 정보를 복원하여 사용자의 더 높은 몰입형 경험 요구 사항을 충족합니다. 라이트 필드 정보의 수집, 저장 및 전송은 여전히 ​​대용량 데이터 등 많은 기본 문제에 직면해 있으며 라이트 필드 렌더링 기술은 아직 초기 단계입니다. 그러나 VR 경험에 대한 사용자의 요구가 점점 더 높아지면서 향후 핵심 렌더링 기술이 될 수도 있습니다.

라이트 필드 디스플레이의 그림입니다.

라이트 필드 디스플레이 헤드셋.

Foveated 라이트 필드 렌더링.

15.2.4.4 빛과 그림자

탄젠트 공간 축에 정렬된 이방성 조명의 경우 대각선을 따라 표시되는 표준 등방성 조명은 탄젠트 공간 축에 이방성으로 정렬되며 2D 탄젠트 법선과 쌍을 이루는 추가 값은 2개만 필요합니다 = RGBA 텍스처에 적합합니다(DXT5 >95% 시간).

거칠기를 인덱스로 변환: 확산 조명은 Lambert를 지수(NLk)로 높입니다. 여기서 k는 0.6-1.4 범위에 있습니다. 이방성 확산 조명을 시도했지만 그만한 가치가 없습니다. 스페큘러 반사 지수 범위는 1-16384이며 이방성을 갖는 수정된 Blinn-Phong입니다.

cpp
void RoughnessEllipseToScaleAndExp(float2 vRoughness, out float o_flDiffuseExponentOut,out float2 o_vSpecularExponentOut,out float2 o_vSpecularScaleOut)
{
    o_flDiffuseExponentOut=((1.0-(vRoughness.x+ vRoughness.y) * 0.5) *0.8)+0.6;// Outputs 0.6-1.4
    o_vSpecularExponentOut.xy=exp2(pow(1.0-vRoughness.xy,1.5)*14.0);// Outputs 1-16384
    o_vSpecularScaleOut.xy=1.0-saturate(vRoughness.xy*0.5);//This is a pseudo energy conserving scalar for the roughness exponent
}

이방성 조명 계산 프로세스:

기하학적 반사 앨리어싱: 노멀 맵이 없는 조밀한 메시도 앨리어싱을 생성할 수 있으며 러프니스 밉은 도움이 되지 않습니다! 보간된 버텍스 노멀의 편도함수를 사용하여 곡률에 근접한 기하학적 러프니스 항을 생성할 수 있습니다.

cpp
float3 vNormalWsDdx = ddx(vGeometricNormalWs.xyz);
float3 vNormalWsDdy = ddy(vGeometricNormalWs.xyz);
float flGeometricRoughnessFactor = pow(saturate(max(dot(vNormalWsDdx.xyz, vNormalWsDdx.xyz), dot(vNormalWsDdy.xyz, vNormalWsDdy.xyz))), 0.333);
vRoughness.xy=max(vRoughness.xy, flGeometricRoughnessFactor.xx); // Ensure we don’t double-count roughness if normal map encodes geometric roughness

flGeometricRoughnessFactor의 시각화.

MSAA 중심 대 중심 보간은 버텍스 노멀을 과도하게 보간하고 노멀 보간으로 인해 윤곽선에서 반사광 깜박임이 발생할 수 있으므로 완벽하지 않습니다. 기사에서 사용된 기술은 다음과 같습니다.

cpp
// 보간(Interpolation)노멀(Normal)회 :회 ,회
float3 vNormalWs:TEXCOORD0;
centroid float3 vCentroidNormalWs:TEXCOORD1;

// 픽셀(Pixel)셰이딩 내에서 ,만약 노멀(Normal)1.01,노멀(Normal)
if(dot(i.vNormalWs.xyz, i.vNormalWs.xyz) >= 1.01)
{
    i.vNormalWs.xyz = i.vCentroidNormalWs.xyz;
}

노멀 맵 인코딩: 탄젠트 법선을 Z 평면에 투영하는 것은 2D 텍스처 범위의 약 78.5%만 사용하는 반면, 반팔면체 인코딩은 2D 텍스처의 전체 범위를 사용합니다.

1.4x는 단지 HTC Vive에 대한 권장 사항일 뿐이라는 것이 밝혀졌습니다(각 HMD 디자인에는 광학 장치와 패널을 기반으로 하는 권장 스칼라가 다릅니다). 느린 GPU에서는 권장되는 렌더 타겟 스칼라를 축소하고, 더 빠른 GPU에서는 권장되는 렌더 타겟 스칼라를 확장하여 GPU 주기를 최대한 활용하세요.

모니터의 해상도를 높이고(VR은 도당 픽셀 수가 더 적다는 점을 잊지 마세요), 기본적으로 8x를 사용하여 색상 및 일반 맵에 대해 이 옵션을 활성화했습니다. 다른 모든 기능을 비활성화합니다(삼선형만 제외). 성능을 측정하는 데 필요합니다. 다른 곳에서 병목 현상이 발생하면 이방성 필터링이 "무료"일 수 있습니다.

소음은 좋은 친구입니다. VR에서는 전환이 끔찍하고 LCD TV보다 밴딩이 더 분명합니다. 픽셀 셰이더에 부동 소수점 정밀도가 있으면 프레임 버퍼에 노이즈가 추가될 수 있습니다.

cpp
float3 ScreenSpaceDither(float2vScreenPos)
{
    // Iestyn's RGB dither(7 asm instructions) from Portal 2X360, slightly modified for VR
    float3 vDither = dot(float2(171.0, 231.0), vScreenPos.xy + g_flTime).xxx;
    vDither.rgb = frac(vDither.rgb / float3(103.0, 71.0, 97.0)) - float3(0.5, 0.5, 0.5);
    return (vDither.rgb / 255.0) * 0.375;
}

환경 맵의 경우 무한대 = 하늘에만 대한 표준 구현에는 환경 맵에 대한 일종의 거리 재매핑이 필요합니다. 구는 저렴하고 큐브는 더 비싸며 둘 다 서로 다른 상황에서 유용합니다.

성능 쿼리가 필요합니다! 항상 vsync를 유지하고, 프레임 속도를 보기 위해 VSync를 비활성화하면 플레이어가 어지러워지며, GPU 작업량을 보고하기 위해 성능 쿼리를 사용해야 합니다. 가장 간단한 구현은 첫 번째 드로우 콜부터 마지막 ​​드로우 콜까지 측정하는 것입니다. 이상적으로는 Present()에서 첫 번째 그리기 호출까지의 유휴 시간, 첫 번째 그리기 호출에서 마지막 그리기 호출까지의 유휴 시간, 마지막 그리기 호출에서 현재 Present()까지의 유휴 시간을 측정합니다.

15.2.4.5 광선 감지

레이캐스팅은 VR/AR 래스터라이제이션의 실행 가능한 대안입니다. VR에서 새로운 요구 사항은 넓은 시야, 렌즈 왜곡, 하위 픽셀 렌더링, 낮은 대기 시간, 스크롤링 디스플레이 수정, 심도, 고해상도 및 프레임 속도, 포비티드 렌더링, 효율적인 앤티앨리어싱 등입니다.

위 기능은 다음과 같이 다양한 렌더링 방법으로 지원됩니다.

VR을 위한 계층적 가시성: 상용 하드웨어의 음영 처리, 완전 동적 장면 지원, 비점 원점을 포함한 임의의 일관된 광선 분포를 포함한 초당 대규모 광선! 광 샘플링 레벨의 구성 프로세스에 대한 개략도는 다음과 같습니다.

계층적 샘플링은 캐시 적중률, 즉 성능을 향상시킬 수 있습니다.

15.2.4.6 앤티앨리어싱

일반적인 앤티앨리어싱 방법은 다음과 같습니다.

  • 엣지 지오메트리 AA, 일반적으로 하드웨어 가속;

  • 이미지 공간 AA, FXAA, MLAA, SMAA 등과 같은 대부분의 렌더링 파이프라인에 매우 적합합니다.

  • 시간적 슈퍼샘플링을 위해 재투영을 사용하는 시간적 AA.

MSAA는 빈도가 높은 기하학, 거의 수직선, 대각선에서 더 잘 보이지만 내부 텍스처/음영은 여전히 앨리어싱됩니다.

FXAA는 가장자리 형상, 질감/음영 세부 정보에서 더 잘 보이지만 고주파수 데이터에서는 세부 정보가 손실되는 경우가 있습니다.

오버샘플링 앤티앨리어싱은 더 큰 버퍼로 렌더링하는 것이 잘 작동합니다. 여유가 있는 경우 좋은 다운샘플링 필터와 함께 사용하면 됩니다.

Specular AA는 이미지를 크게 향상시킬 수도 있습니다. 좋은 출발점은 LEAN, Cheap LEAN(CLEAN) 및 Toksvig AA를 살펴보는 것입니다. 왜곡 셰이더는 가장자리 앨리어싱을 줄입니다. 일부 게임에서는 LOD에 더 많은 주의를 기울여야 할 수도 있습니다. 여러 AA 방법을 조합하면 엔진에 가장 적합한 방법을 사용하여 앨리어싱의 다양한 측면을 처리하는 각기 다른 AA 솔루션을 통해 더 나은 결과를 얻을 수 있습니다.

앨리어싱은 VR의 가장 큰 적입니다. 카메라(플레이어의 머리)가 계속 움직이므로 앨리어싱이 증폭됩니다. 렌더링할 픽셀이 더 많지만 각 픽셀은 이전보다 더 큰 각도를 채웁니다. 평균은 다음과 같습니다. 2560x1600 30" 모니터: ~50픽셀/도(50도 수평 시야), 720p 30" 모니터: ~25픽셀/도(50도 수평 시야), VR: ~15.3픽셀/도(110도 시야, 비VR의 1.4배) 픽셀의 품질을 개선해야 합니다.

4xMSAA 최저 품질: MSAA가 효과적이기 때문에 포워드 렌더러가 앤티앨리어싱에서 승리합니다. 성능이 허용되면 8xMSAA를 사용하십시오. 렌더러가 업계의 다른 알고리즘과 어떻게 비교되는지 확인하려면 이미지 공간 앤티앨리어싱 알고리즘을 4xMSAA 및 8xMSAA와 나란히 비교해야 합니다. 디더링된 SSAA는 HLSL의 "샘플" 수정자를 사용할 때 확실히 최고이지만 성능을 절약할 수 있는 경우에만 가능합니다.

일반 맵은 계속 사용할 수 있으며 대부분의 일반 맵은 VR에서 잘 작동합니다. 유효하지 않은 경우: 추적된 볼륨 내에서 몇 센티미터보다 큰 지형지물은 세부 묘사가 좋지 않으며, 추적된 볼륨 내의 표면 모양은 일반 맵에 포함될 수 없습니다. 효과적인 경우: 가까이서 볼 수 없는 추적된 볼륨 외부의 멀리 있는 물체, 표면 "질감" 및 미세한 세부 사항. 노멀 맵 매핑 오류:

평균 법선만 생성하는 밉 필터는 중요한 러프니스 정보를 잃게 됩니다.

Mips로 인코딩된 러프니스: 해당 텍스처에 기여하는 가장 높은 밉에 대한 모든 2D 탄젠트 법선의 표준 편차인 등방성 값(원의 반경으로 시각화됨)을 저장할 수 있으며, 각각 X 및 Y 방향의 표준 편차에 대한 2D 이방성 값(타원의 치수로 시각화됨)을 저장할 수도 있습니다. 이는 탄젠트 공간 축 정렬 이방성 조명을 계산하는 데 사용할 수 있습니다!

추가된 아티스트는 거칠기를 생성하고 2D 광택 = 1.0 – 거칠기를 생성하고 간단한 상자 필터를 사용하여 이를 각 밉 수준에서 노멀 맵 거칠기와 추가/합산하고 결과 노멀 맵 거칠기를 저장하는 것은 이방성 광택 맵 때문에 무료였습니다.

왼쪽: 등방성 광택; 오른쪽: 이방성 광택.

15.2.5 XR 최적화

단일 GPU의 경우 단일 GPU가 모든 작업을 수행하고 스테레오 렌더링은 다양한 방식으로 수행될 수 있으며(이 예에서는 순차 렌더링 사용) 그림자 버퍼가 두 눈 간에 공유됩니다. 다중 GPU 선호도 API, AMD 및 NVIDIA에는 선호도 마스크를 사용하여 GPU 전체에 그리기 호출을 브로드캐스트하고, 각 GPU에 대해 서로 다른 셰이더 상수 버퍼를 설정하고, GPU 전체에 렌더 대상의 하위 사각형을 전송하고, 대상 GPU가 렌더링되는 동안 전송 펜스를 사용하여 비동기적으로 전송하는 여러 GPU 선호도 API가 있습니다. 2개의 GPU를 사용하면 각 GPU가 한쪽 눈을 렌더링하고 두 GPU 모두 섀도우 버퍼를 렌더링하며 전송 버블에서 "왼쪽 커밋" 및 "응용 프로그램 창"이 수행되어 성능이 30~35% 향상됩니다. 4개의 GPU, 각 GPU가 눈의 절반을 렌더링하고 모든 GPU가 섀도우 버퍼를 렌더링하는 경우 PS 비용은 선형적으로 증가하지만 VS 비용은 그렇지 않으며 드라이버의 CPU 비용이 높을 수 있습니다.

위에서 아래로: 1, 2, 4 GPU 렌더링 다이어그램.

프로젝션 매트릭스 대 VR 광학: 프로젝션 매트릭스는 우리가 원하는 것과 반대되는 픽셀 밀도 분포를 가지며, 프로젝션 매트릭스는 가장자리에서 각도당 픽셀 밀도를 증가시키고, VR 광학은 중앙에서 픽셀 밀도를 증가시키며 결국 가장자리의 픽셀을 과도하게 렌더링하게 됩니다. NVIDIA의 "다중 해상도 셰이딩"을 사용하면 더 적은 CPU 오버헤드로 약 5~10% 추가 GPU 성능을 얻을 수 있습니다.

방사형 밀도 마스킹: 현재 GPU 아키텍처와 일치하도록 2x2 픽셀 정사각형의 체커보드 패턴 렌더링을 건너뜁니다.

필터를 재구성하는 과정은 다음과 같습니다.

방사형 밀도 마스킹 단계:

  • 렌더링 시 2x2 픽셀 정사각형을 잘라내거나 2x2 체커보드 패턴을 사용하여 템플릿이나 깊이를 채운 다음 렌더링합니다.

  • 재구성 필터.

Aperture Robot Repair에서 5~15%의 성능 절감, 다양한 콘텐츠와 다양한 셰이더를 사용하여 더 높은 이득을 얻을 수 있습니다. 픽셀을 재구성하고 건너뛰는 오버헤드가 픽셀 쿼드를 건너뛰는 데 따른 픽셀 셰이더 절약을 초과하지 않는 경우에는 세척입니다. 저사양 GPU에서는 거의 항상 많은 작업이 절약됩니다.

누락된 프레임을 처리하기 위해 엔진이 프레임 속도에 도달하지 않은 경우 VR 시스템은 마지막 프레임의 렌더링된 이미지를 재사용하고 재투영할 수 있습니다. 누락된 프레임을 채우기 위해 재투영을 사용하는 회전 재투영, 위치 및 회전 재투영만 최후의 수단 안전망으로 간주되어야 합니다. 대상 사용자가 애플리케이션의 최소 사양보다 낮은 GPU를 사용하지 않는 한 프레임 속도를 유지하기 위해 재투영에 의존하지 마십시오.

회전 전용 재투영: 지터는 카메라 변환, 애니메이션 및 추적된 컨트롤러에 의해 이동되는 개체로 인해 발생합니다. 지터는 평균을 낸 두 개의 서로 다른 이미지로 나타납니다.

회전 재투영은 머리 중심이 아닌 눈 중심이므로 잘못된 위치에서 재투영되며, 재투영 시 회전량에 따라 ICD(카메라 간 거리)가 인위적으로 감소됩니다.

긍정적인 측면: 이 알고리즘은 수십 년 동안 잘 이해되어 왔으며 현대 연구를 통해 개선될 가능성이 높습니다. 알려진 부작용이 있더라도 단일 드롭 프레임을 매우 잘 처리합니다. 따라서... 누락된 프레임에 대한 최후의 수단 안전망이 될 만큼 충분히 좋은 매우 중요한 절충안이 있습니다. 이는 프레임을 잃는 것보다 낫습니다.

위치 재투영: 여전히 큰 관심을 끄는 해결되지 않은 문제입니다. 기존 렌더러에서는 하나의 깊이만 사용할 수 있으므로 반투명도를 표현하는 것이 어렵습니다(입자 시스템). 깊이는 해결된 색상의 MSAA 깊이 버퍼에 저장되어 잠재적으로 색상 오버플로가 발생할 수 있습니다. 표현되지 않은 픽셀의 경우 구멍 채우기 알고리즘은 유효한 입체 쌍의 프레임이 많더라도 망막 경합을 유발할 수 있습니다. 사용자가 웅크리거나 일어서서 수직으로 이동하면 간격을 채워야 합니다.

비동기식 재투영: GPU에 따라 드로우 콜 경계에서 선점할 수 있는 현재 세대 GPU와 동일하거나 더 나은 선점 세분성을 요구하는 이상적인 안전망

, 현재 vsync가 제때에 다시 출시될 수 있다는 보장은 없습니다. 애플리케이션은 선점 세분성을 이해해야 합니다.

인터리브 재투영 힌트: 이전 GPU는 비동기 재투영을 지원할 수 없으므로 대안이 필요합니다. OpenVR API에는 인터리브 재투영 힌트가 있습니다. 기본 시스템이 상시 비동기 재투영을 지원하지 않는 경우 애플리케이션은 매번 프레임 회전 전용 재투영을 요청할 수 있습니다. 애플리케이션은 ~18ms/프레임 렌더링을 가져옵니다. 기본 VR 시스템은 애플리케이션이 목표 프레임 속도 아래로 떨어지면 자동으로 활성화되는 안전망으로 인터리브 재투영을 사용할 수도 있습니다. 매 프레임마다 다시 투영하는 것은 좋은 절충안입니다.

프레임 속도를 유지하는 것은 어렵습니다. 가상 현실은 사용자가 카메라를 너무 많이 제어할 수 있고 많은 상호 작용 모델을 통해 사용자가 세계를 재구성할 수 있기 때문에 기존 게임보다 더 어렵습니다. 사용자가 콘텐츠를 쉽게 재구성할 수 있으므로 렌더링 및 콘텐츠를 90fps로 조정하지 않아도 됩니다. Aperture Robot Repair는 경험의 최악의 20%를 조정하여 달성한 프레임 속도를 달성했습니다.

적응형 품질: 렌더링 설정을 동적으로 변경하여 GPU 활용도를 최대화하는 동시에 프레임 속도를 유지합니다. 목표는 유휴 GPU 주기가 있을 때 프레임 삭제 및 재투영 가능성을 줄이고 품질을 향상시키는 것입니다. 예를 들어, Aperture Robot Repair VR 데모는 두 가지 다른 방법을 사용하여 NVIDIA 680에서 목표 프레임 속도로 실행됩니다. 이점은 응용 프로그램에 사용할 수 있는 최소 GPU 사양, 아트 자산 제한 증가입니다. 이제 아티스트는 프레임 속도를 유지하기 위해 재투영에 의존하지 않고도 약간 낮은 충실도의 렌더링과 높은 다각형 자산 또는 더 복잡한 재질을 교환할 수 있으며 예상치 못한 이점도 있습니다. 응용 프로그램이 모든 하드웨어에서 더 좋아 보입니다.

VR에서 조정할 수 없는 것: 스페큘러 반사 같은 시각적 기능은 전환할 수 없으며 그림자도 전환할 수 없습니다. 조정할 수 있는 항목: 렌더 해상도/뷰포트(동적 해상도라고도 함), MSAA 수준 또는 앤티앨리어싱 알고리즘, 포비티드 렌더링, 방사형 밀도 마스킹 등 적응형 품질 예(굵은 글씨는 기본 구성):

GPU 작업 부하 측정: GPU 작업 부하가 항상 안정적인 것은 아니며 거품이 있을 수 있습니다. VR 시스템 GPU 작업 부하가 가변적입니다: 렌즈 왜곡, 색수차, 결합 경계, 적용 범위 등. 애플리케이션이 아닌 VR 시스템에서 타이밍을 가져옵니다. 예를 들어 OpenVR은 모든 GPU 작업을 계산하는 총 GPU 타이머를 제공합니다.

GPU 타이머 - 지연, GPU 쿼리에 이미 1개의 프레임이 있고 대기열에 수정할 수 없는 프레임이 1~2개 있습니다.

구현 세부 사항 - 70%-90% GPU 활용도 유지를 목표로 하는 3가지 규칙.

  • 높은 GPU 사용률 = 프레임의 90%(10.0ms), 큰 감소: GPU 프레임의 90% 임계값 이후 마지막 프레임의 렌더링이 완료되면 2레벨 감소하고 2프레임을 기다립니다.

  • 낮은 GPU 사용률 = 프레임의 70%(7.8ms), 보수적으로 증가: 마지막 3프레임이 GPU 프레임의 70% 임계값 미만으로 완료되면 1레벨 증가하고 2프레임을 기다립니다.

  • 예측 = 프레임의 85%(9.4ms), 빠른 성장을 예측하기 위해 마지막 두 프레임의 선형 외삽을 사용합니다. 마지막 프레임이 85% 임계값을 초과하고 선형 외삽의 다음 프레임이 높은 임계값(90%)을 초과하는 경우 2레벨을 떨어뜨리고 2프레임을 기다립니다.

10% 유휴 규칙: 90%라는 높은 임계값은 거의 모든 프레임에서 다른 처리를 위해 GPU의 10%를 유휴 상태로 남겨 두는 것입니다. 이는 좋은 것입니다. Windows 데스크톱에서 몇 프레임마다 GPU가 필요한 경우에도 GPU는 다른 처리와 공유되어야 합니다. GPU 예산에 대한 정신 모델은 작년에 프레임당 11.11ms에서 현재 프레임당 10.0ms로 변경되었으므로 다른 처리를 위해 GPU 주기가 거의 중단되지 않습니다.

CPU와 GPU 성능을 분리하여 렌더링 스레드를 자율적으로 만들고 CPU가 새 프레임을 준비하지 않은 경우 재투영하지 마세요! 대신, 렌더링 스레드는 업데이트된 HMD 포즈와 동적 해상도에 대한 최저 적응형 품질 지원을 사용하여 GPU 워크로드의 마지막 프레임을 다시 제출합니다. 애니메이션 지터 문제를 해결하려면 렌더링 스레드에 애니메이션을 업데이트된 상태로 유지하기 위해 두 애니메이션 프레임 사이에 보간될 수 있는 두 개의 애니메이션 프레임을 제공합니다. 그러나 사소하지 않은 애니메이션 예측은 어려운 문제입니다. 그런 다음 보다 복잡한 시뮬레이션을 위해 GPU 프레임 속도의 1/2 또는 1/3로 CPU를 실행하거나 저가형 CPU에서 실행하도록 계획할 수 있습니다.

요약하자면, 모든 VR 엔진은 다중 GPU(최소 2개의 GPU)를 지원해야 하며, 포비티드 렌더링 및 방사형 밀도 마스킹은 광학 대 프로젝션 매트릭스 전투를 상쇄하는 데 도움이 되는 솔루션입니다. 적응형 품질은 다른 프로세스에 GPU의 10%를 사용하면서 충실도를 높이거나 낮추며, 최소 사양 프레임 속도를 달성하기 위해 재투영에 의존하지 않습니다! 엔진이 렌더링 스레드에서 다시 제출하여 CPU와 GPU 성능을 분리하는 방법을 고려하세요.

기술 및 디자인에 대한 최적화 아이디어는 개발 시 최적화하고, 확장성을 위해 메시를 코딩 및 구축하고, 계획된 모든 VR 지원 하드웨어에 대해 테스트하여 가능한 한 빨리 문제를 식별하는 것입니다. 성능 측면에서 mocap을 사용할 수 있습니다. 액션 지향, VR 라이브 공연의 공연 스타일 지원, VR에서 VR 연출, 차단을 통해 공간을 최대한 활용할 수 있습니다.

지난 2년 동안 모바일 VR의 새로운 과제는 긴 시선, 더 많은 캐릭터, 더 길고 더 인터랙티브한 영화, 다양한 무기, 인벤토리 및 수집품 등을 갖춘 탐험 가능한 세계였습니다. 게임 스레드 과제는 복잡한 구성 요소 계층 이동, 생성/파괴 지연, 전투 중 비전투 업데이트 및 CPU 정지입니다.

구성 요소 계층에 대한 일반적인 사항: 장면 구성 요소 대신 액터 구성 요소, 범위 내 이동, 복잡한 계층은 프레임당 최대 한 번 이동하고 필요할 때 분리/다시 연결합니다. 구성 요소 계층 구조를 위한 스켈레탈 메시: 분리 최적화: 플레이어 조각, 모든 적, 전투에 등장하는 모든 영화 캐릭터에 대해 애니메이션 그래픽을 사용하여 루트 뼈대를 있어야 할 위치로 이동하여 스켈레탈 메시 구성 요소를 분리합니다. 단점은 일부 애니메이션 노드를 수정해야 하고 부품 위치가 더 이상 정확하지 않다는 것입니다. 구성 요소 계층의 중복: 기본적으로 불필요한 중복이 많이 있으며, UE 물리/충돌 옵션 교육, 가능한 경우 대상 목록을 유지하도록 전환합니다.

대부분의 장애는 액터 및 구성 요소 생성/파괴, 풀 시스템을 통한 객체 재사용, 모든 것을 미리 생성 및 최고 한도 사용에서 발생합니다. 비전투 로직의 경우 플레이어 거리 시스템을 추가하여 전투 시 영향을 줄이고 플레이어가 너무 멀리 있으면 배치된 아이템을 취소합니다. 게임 스레드가 중단되는 이유는 Quest 장치에 코어 수가 적고 렌더링, 오디오 및 게임 요구 사항이 동시에 필요하며 일부는 여전히 해결 가능하기 때문일 수 있습니다. Unreal Insights는 통계 캡처보다 성능에 미치는 영향이 적기 때문에 지연의 원인을 정확히 찾아내는 데 도움이 될 수 있습니다. 단점은 작업 설정이 까다롭고 4.24부터 해석을 까다롭게 만드는 개체 이름이 없으며 캡처를 시작/중지할 방법이 없다는 것입니다. 게임 스레드 지연에 대한 팁: 작업 그래프 시스템을 이해하고 전제 조건 확인에 주의하세요. 병렬 작업이 강제로 조기 완료되어 중단될 수 있습니다.

기타 게임 스레드 팁: 블루프린트 틱 없음, 틱에서 블루프린트 구현 가능/블루프린트 네이티브 이벤트 없음, 비동적 위임 지원, 블루프린트 타이머/일정 주의.

렌더링 문제에는 메모리, GPU, 그리기 호출, 복잡한 셰이더, 복잡한 애니메이션 및 복잡한 환경과 같은 요소가 포함됩니다. 미리 계산된 가시성(PCV), 시각적 점프 방지, 높은 샘플링 설정, 기회 최대화, 작은 셀. 효율성: 설정/유지보수, 계산 시간. PCV 단계에는 선택적 그리드 배치, 탐색 그리드 단위 배치 및 월드 설정의 공개 구성(스택의 셀 수, 샘플링 설정, 그리드 수 임계값)이 포함됩니다.

테스트 시나리오의 각 단계에서 병목 현상에 대한 개요:

또한 HLOD(레벨 LOD) 및 사용자 정의 HLOD를 활성화할 수 있습니다.

항상 HLOD를 켜고, 전환 POP를 제거하고, 라이트맵 LOD POP를 제거하고, 소스 메시를 삭제하고, 라이트맵 해상도를 높이고, PCV 계산을 줄이고, 인스턴스화된 충돌을 유지하세요. 동적 해상도는 Oculus Rift 동적 해상도 오버레이를 활용하여 추가 비용 없이 뷰포트의 크기를 동적으로 조정합니다.

시각적 개선 사항: 메뉴용 스테레오 레이어(포켓), 모바일 시차 반사, 포워드 렌더링 데칼. 버텍스 애니메이션: 영화 같은 강성 솔버 도구, 4000개 이상의 애니메이션 개체, 보간, 절차적 오버레이. 버텍스 변형 및 인스턴스화: 여러 인스턴스, 모프, 베이크된 라이트 프로브 샘플링을 통한 자체 조명. 버텍스 애니메이션 및 인스턴싱: 시네마틱 파이프라인을 위한 군중 도구, 200개의 애니메이션 캐릭터, 아틀라제이제이션을 통한 추가 변형.

UI 및 장면 요소에 읽기 쉬운 텍스트를 사용합니다. VR에서 텍스트 가독성을 보장하는 방법에는 여러 가지가 있습니다. 렌더링 목적으로 응용 프로그램에서 부호 있는 거리 필드 글꼴을 사용하는 것이 좋습니다. 이렇게 하면 크기가 조정되거나 축소된 경우에도 글꼴이 원활하게 렌더링됩니다. 애플리케이션에서 지원하는 언어도 고려해야 합니다. 모노그램의 복잡성은 가독성에 영향을 미칠 수 있습니다. 예를 들어 응용 프로그램은 동아시아 언어를 잘 지원하는 글꼴을 사용하기를 원할 수 있습니다. 일부 언어는 동일한 사본에서 다른 언어보다 더 많은 문자를 사용하므로 현지화는 텍스트 레이아웃에도 영향을 미칠 수 있습니다. 장면 내 글꼴 크기와 위치도 중요합니다. Gear VR의 경우 30pt보다 큰 글꼴을 선택하면 일반적으로 고정된 z 깊이 4.5m(단위)에서 최소한의 가독성을 제공하며, 48pt보다 큰 글꼴을 선택하면 일반적으로 편안한 읽기 환경이 보장됩니다. Rift의 경우 25pt보다 큰 글꼴 크기는 고정된 z 깊이 4.5m(일치)에서 최소한의 가독성을 제공하며, 42pt보다 큰 글꼴 크기는 일반적으로 편안한 읽기 환경을 보장합니다.

깜박임은 시뮬레이터 질환의 안구운동 구성요소에서 중요한 역할을 하며 종종 화면의 일부 또는 전부에서 밝기와 어둠의 빠른 "펄스"로 표시됩니다. 사용자가 깜박임을 인지하는 정도는 디스플레이가 "켜짐" 모드와 "꺼짐" 모드 사이를 순환하는 속도, "켜짐" 단계에서 방출되는 빛의 양, 망막의 어느 부분이 자극되는지, 심지어 하루 중 시간과 개인의 피로도 등 여러 요소의 함수입니다. 깜박임은 시간이 지남에 따라 눈에 띄게 줄어들지만 여전히 두통과 눈의 피로를 유발할 수 있습니다. 어떤 사람들은 깜박임에 매우 민감하여 그 결과 눈의 피로, 피로 또는 두통을 경험합니다. 다른 사람들은 그것을 알아차리지도 못하거나 어떤 부작용도 겪지 않을 것입니다. 그럼에도 불구하고 특정 사람이 디스플레이 깜박임을 인지할 가능성을 높이거나 낮출 수 있는 특정 요인이 있습니다.

첫째, 사람들은 시야 중앙의 깜박임보다 주변 깜박임에 더 민감합니다. 둘째, 화면 이미지가 밝을수록 깜박임이 더 커집니다. 밝은 이미지, 특히 주변부(예: 밝은 흰색 방에 서 있는 경우)에서 눈에 띄는 디스플레이 깜박임이 발생할 수 있습니다. 가능하면 더 어두운 색상을 사용하십시오. 특히 플레이어의 관점 중심 밖의 영역에서는 더욱 그렇습니다. 일반적으로 새로 고침 빈도가 높을수록 깜박임이 눈에 띄게 줄어듭니다.

의도적으로 화려한 콘텐츠를 만들지 마세요. 고대비의 깜박이는(또는 빠르게 교대하는) 자극은 일부 사람들에게 광과민성 발작을 유발할 수 있습니다. 관련하여 미세한 흑백 줄무늬와 같은 높은 공간 주파수 텍스처도 광과민성 간질 발작을 유발할 수 있습니다. 국제표준화기구(International Standards Organization)는 감광성 간질 발작의 위험을 줄이기 위해 이미지 콘텐츠 표준으로 ISO 9241-391:2016을 발표했습니다. 이는 잠재적으로 유해한 섬광 및 패턴을 해결합니다. 콘텐츠는 이미지 보안에 대한 표준 및 모범 사례를 준수하도록 보장되어야 합니다.

**노멀맵 대신 시차맵을 사용하세요.**노멀 맵은 주어진 3D 모델에 버텍스 세부 정보를 추가하지 않고도 깊이와 텍스처를 전달하기 위해 사실적인 조명 신호를 제공합니다. 현대 게임에서 널리 사용되지만 입체적인 3D로 보면 매력이 훨씬 떨어집니다. 노멀 맵은 양안 시차나 모션 시차를 고려하지 않기 때문에 개체 모델에 칠해진 평면 텍스처와 유사한 이미지를 생성합니다. 시차 매핑은 노멀 매핑을 기반으로 하지만 노멀 매핑은 뎁스 오브 필드(DoF)를 설명할 수 없습니다. 시차 매핑은 콘텐츠 제작자가 제공하는 추가 높이 맵을 사용하여 샘플링된 표면 텍스처의 텍스처 좌표를 이동하는 방식으로 작동합니다. 셰이더 수준에서 계산된 픽셀별 또는 정점별 뷰 방향을 사용하여 텍스처 좌표 오프셋을 적용합니다. 시차 매핑은 벽돌 벽이나 조약돌 경로와 같이 충돌 표면에 영향을 주지 않는 미세한 세부 사항이 있는 표면에 가장 적합합니다.

개발된 플랫폼에 적합한 왜곡 보정을 적용합니다. VR 장치의 렌즈는 렌더링된 이미지를 왜곡하며, 이 왜곡은 SDK의 포스트 프로세스 단계를 통해 수정됩니다. SDK 지침에 따라 이러한 왜곡을 올바르게 수행하는 것이 중요합니다. 잘못된 왜곡은 상당히 정확해 보이지만 여전히 혼란스럽고 불편함을 느낄 수 있으므로 세부 사항에 주의하는 것이 중요합니다. 모든 왜곡 보정 값은 실제 장치와 일치해야 하며, 그 중 어느 것도 사용자가 조정할 수 없습니다.

간단히 말해서, Quest 2는 상세한 최적화 방법, 시각적 개선 기술, 향상된 상호 작용 및 게임 메커니즘을 통해 성능이 크게 향상되고 사용자 경험이 향상되었습니다.

모바일 XR 최적화에 대한 자세한 내용은 언리얼 렌더링 시스템 분석(12) - 모바일 특별 주제 3부(렌더링 최적화) 및 12.6.5장 XR 최적화를 참조하세요.

15.2.6 기타

초기 XR SDK는 SLAM 현지화 및 깊이 매핑, 동시 현지화 및 매핑(지도 내 위치를 추적하면서 지도 생성)에 대한 지원이 제한적인 경우가 많았습니다. 원래 최초의 화성 탐사선을 포함하여 로봇 공학용으로 개발된 최신 장치에는 처리를 돕는 소위 "MVU"를 포함한 추가 처리 능력이 있습니다. SLAM 제한 사항은 지저분한 방이 깨끗한 방보다 낫다는 것입니다. 모션 블러 및 조명은 이미지 객체 인식 및 추적에 유사한 문제를 일으킬 수 있습니다. 깊이 카메라 제한은 컬러 카메라보다 낮은 해상도, 깊이 포인트를 보간해야 함, 낮은 프레임 속도, 메싱 시 카메라를 매우 느리게 움직여야 함, IR은 반사 표면(창, 거울 등)에서는 잘 작동하지 않습니다.

VR용 오디오의 경우 영화 사운드와 게임 사운드 사이에 유사점이 있을 수 있지만 두 사운드 이론과 개념을 영화 VR로 번역할 때 주목할 만한 중요한 차이점도 있습니다.

몰입감과 현장감은 서로 다른 것이며, 몰입형 오디오는 단지 위치 오디오를 사용하는 것이 아니라 영화 촬영, 차단, 연기 등은 물론 현장감을 구현하는 핵심 요소입니다. VR에는 4가지 유형의 FOA가 있습니다. 하나는 영화 캐릭터가 인식하고 이해하며 현재 시야에서 시각화됩니다(아래 상단 이미지). 영화 캐릭터가 인식하고 이해하는 음성도 있지만 현재 fov의 범위 내에 있지 않습니다(아래 하단 이미지). 비/음성 해설도 있습니다. 캐릭터가 듣지는 못하지만 청중이 화면의 동작을 동반하는 것으로 인식하여 동작을 설명할 수도 있습니다. 및 METADIEGETIC 사운드 - VE의 일부이지만 다른 캐릭터에게는 들리지 않는 청중 캐릭터의 상상이나 환각입니다.

VR에서의 Conemarching: 90FPS에서 프랙탈 경험 개발에서는 VR에서의 프랙탈 알고리즘과 RayMarch 최적화 기술에 대해 설명합니다.

왼쪽: 레이 행진 최적화, 아이디어는 동일한 장면을 서로 다른 해상도에서 단계별로 렌더링하는 것입니다. 각 패스를 사용하여 교차점이 없음을 보장할 수 없을 때까지 구를 추적하여 해상도를 두 배로 늘리고 일반적으로 약간의 편차를 두고 거리를 재사용합니다. 오른쪽: 모든 것을 두 번 렌더링하는 문제를 해결하려면 깊이를 재투영하고, 콘 페인터를 사용하여 중앙 눈을 렌더링하고, 왼쪽 및 오른쪽 눈으로 재투영하고, 수평으로 이동하는 스크린 공간 광선을 오프셋하고, 더 나은 수집 거리를 얻으려면 더 낮은 해상도에서 콘 패스를 사용할 수 있습니다.

conemarcher를 사용하면 깊이를 두 번 계산해야 하므로 이제 대부분의 파이프라인을 직접 절단할 수 있습니다. 재투영 패스는 일반적으로 빠르며 제어 프로세스에 비례하여 성능이 향상됩니다. 재투영 추정 중 일부가 완벽하지 않기 때문에 셰이딩 프로세스에 약간의 조정이 필요합니다.

렌더링 셰이딩은 여전히 비용이 많이 들고, 주변에 많은 세부 정보가 필요하지 않으며, 동적인 것이 필요하고, 프랙탈을 기반으로 크기가 조정되며, 하드웨어에 따라 주변에서는 절반 해상도, 중앙에서는 전체 해상도로 렌더링하고 가장자리를 혼합합니다(사진 왼쪽 및 중앙). 최종 합성 결과는 강한 비네팅이 계산 시간을 일부 절약한다는 것을 보여줍니다(그림 오른쪽).

게다가 VR에서는 섀도우, 노멀, 오클루전, SSS가 모두 매우 비쌉니다! 이 기사에서는 너무 멀리 렌더링하지 않기, 균등 산란을 사용하여 숨기기, 깊이 추정의 시간 재투영, 저주파 효과 재투영, 화면 공간 법선을 사용하여 음영 복잡성 감소, 주변에서 낮은 품질 음영 사용, 입체 재투영 시차에서 제공하는 윤곽선을 사용하여 TAA+FXAA 구현 등을 포함한 많은 최적화 방법이 제안되었습니다.

CocoVR - Spherical Multiprojection은 구형 투영 소개, 실제 구형 투영, 아트 파이프라인, 알고리즘 세부 정보, 개발 도구 등을 포함한 구형 다중 투영 기술을 소개합니다. 구형 투영은 많은 VR 경험이 보이는 하늘을 시뮬레이션하기 위해 배경에 적용하는 것이 아니라 360도 이미지를 촬영하여 스카이돔에 적용하는 것과 유사하며 장면의 대부분의 형상에 적용합니다.

매핑 코드는 다음과 같습니다.

cpp
float2((1 + atan2(InVector.x, - InVector.y) / 3.14159265) / 2, acos(InVector.z) / 3.14159265);

구형 다중 투영 셰이더 알고리즘은 다음과 같습니다.

  • 각 픽셀에 대해:

프로브의 깊이 큐브 플롯을 기반으로 가시성을 테스트합니다. 프로브가 보이면 채점 시스템을 통과하세요. 가시성 테스트 프로세스:

각 감지기에는 해당 위치에서 렌더링된 깊이 큐브맵이 포함되어 있습니다. 오프라인 렌더링.

  • Unity는 깊이를 저장하는 데 사용되는 고정밀 큐브맵 형식을 지원하지 않습니다.

  • 다양한 32비트 부동 소수점 인코딩 기능을 사용하세요. 대부분의 경우 시도는 값에 너무 많은 불안정성을 가져오고 검출기에서 멀어질수록 오류가 더 커지고 거리에 따라 약간 증가하는 작은 편향이 추가됩니다.

  • 깊이 값을 저장하는 Latlong 텍스처는 테스트되지 않았습니다. 큐브맵의 문제 중 일부 또는 전부를 해결할 수 있습니다.

프로브 가시성 보기 모드는 프로브 배치에 도움이 됩니다.

  • 최적의 프로브 투영 색상. 스펙과 반사도 추가할 수 있습니다.

  • 감지기가 발견되지 않으면 감지기 색상의 전역 사양과 버텍스 색상을 사용하여 사용할 감지기를 선택하여 단일 색상으로 대체할 수 있습니다.

위의 기술은 보기에도 좋고 오버헤드도 낮습니다. 렌더링하는 데 눈당 약 0.8밀리초가 가장 많이 소요됩니다. 즉, 이 기술은 다른 멋진 기능으로 확장될 수 있고, 많은 메모리를 차지할 수 있으며, 깊이 큐브맵의 해상도가 충분히 높지 않기 때문에(보통 512 정도) 작은 형상에서는 문제가 될 수 있습니다.

고품질 모바일 가상 현실(VR)은 다가오는 그래픽 기술 시대의 요구 사항입니다. 전 세계 사용자는 하드웨어 및 네트워크 상태에 관계없이 몰입형 가상 경험을 즐길 수 있습니다. 그러나 VR 실행 중 사용자 행동의 대화형 특성과 복잡한 환경 제약으로 인해 최첨단 소프트웨어를 기반으로 한 모바일 VR 디자인은 실시간 성능 요구 사항을 완전히 충족할 수 없습니다. 독특한 인간 시각 시스템 효과와 VR 모션 특성과 실시간 하드웨어 수준 정보 간의 강력한 상관 관계에서 영감을 받은 Q-VR: 미래 모바일 협업 가상 현실을 위한 시스템 수준 설계는 소프트웨어와 하드웨어 공동 설계를 통해 미래의 저지연 고품질 모바일 VR을 구현하는 새로운 동적 협업 렌더링 솔루션인 Q-VR을 제안합니다. 소프트웨어 수준에서 Q-VR은 유연한 고급 조정 인터페이스를 제공하여 사용자 인식을 유지하면서 네트워크 대기 시간을 줄입니다. 하드웨어 수준에서 Q-VR은 점점 더 강력해지는 VR 하드웨어의 컴퓨팅 성능을 효과적으로 활용하여 다양한 사용자의 하드웨어 및 네트워크 조건에 적응합니다. 실제 게임에 대한 광범위한 평가를 통해 Q-VR은 상용 VR 장치의 기존 로컬 렌더링 디자인에 비해 평균 3.4배(최대 6.7배)의 엔드투엔드 성능 향상을 달성할 수 있으며, 최첨단 정적 공동 렌더링에 비해 프레임 속도는 4.1배 향상되는 것으로 나타났습니다.

Q-VR 소프트웨어 및 하드웨어 코드 설계 처리 다이어그램.

최신 VR 그래픽 파이프라인의 예.

현재 두 가지 모바일 VR 시스템 설계에서 고급 VR 애플리케이션을 실행할 때 시스템 지연 시간 및 FPS.

정적 공동 렌더링 실행 파이프라인과 Q-VR, Q-VR의 소프트웨어 및 하드웨어 최적화가 파이프라인에 반영됩니다. 렌더링 작업은 개념적으로 다양한 하드웨어 구성 요소에 매핑되며, 그 중 LIWC와 UCA가 본 논문에서 새로 설계되었습니다. 다중 가속기 병렬 처리로 인해 프레임 내 작업(예: RR, 네트워크 및 VD)이 실시간으로 겹칠 수 있습니다. CL: 소프트웨어 제어 논리; LS: 로컬 설정; LR: 로컬 렌더링; C: 구성; RR: 원격 렌더링; VD: 비디오 디코딩; LIWC: 경량 상호작용 인식 워크로드 컨트롤러; UCA: 통합 구성 및 ATW.

시각적 인식은 Q-VR의 소프트웨어 수준 설정 및 구성 예, 프로그래밍 모델 및 하드웨어와 인터페이스하는 방법에 영감을 주었습니다.

이 글에서 제안하는 LIWC 아키텍처 다이어그램입니다.

기준 순차 실행과 ATW(UCA)를 사용한 통합 합성 간의 비교입니다.

UCA 아키텍처 다이어그램.

VR 질병에 대한 더 나은 이해를 향하여 VR 질병의 신체적 증상 수준을 평가하여 VRSA(VR 질병 평가)의 블랙박스 문제를 해결합니다. 유사한 수준의 VR 질병을 유발하는 VR 콘텐츠의 경우 콘텐츠의 특성에 따라 신체적 증상이 달라질 수 있습니다. 기존 VRSA 방법의 대부분은 VR 질병의 전체 점수를 평가하는 데 중점을 둡니다. VR 멀미를 더 잘 이해하기 위해서는 VR 멀미의 전체적인 정도보다는 VR 멀미의 주요 증상 수준을 예측하고 제공할 필요가 있습니다. 이 논문은 VR 질병의 전반적인 정도에 영향을 미치는 주요 신체 증상, 즉 방향 감각 상실, 메스꺼움, 안구 운동 증상의 정도를 예측합니다. 또한 다양한 프레임 속도, 생리학적 신호 및 주관적 등급이 포함된 360도 비디오를 포함하여 VRSA를 위한 새로운 대규모 데이터 세트가 도입되었습니다. VRSA 벤치마크와 새로 수집된 데이터 세트에서 우리의 접근 방식은 주관적 점수와 가장 높은 상관 관계를 달성할 수 있을 뿐만 아니라 어떤 증상이 VR 질병의 주요 원인인지 더 잘 이해할 수 있는 잠재력도 있습니다.

신체 증상 예측의 직관은 VR 질병을 더 잘 이해하는 데 도움이 됩니다. 일반적으로 VR 콘텐츠는 시공간적 특성에 따라 다양한 정도의 신체적 증상을 유발한다.

신경 불일치 메커니즘을 고려한 신체 증상 예측에 대한 그림입니다.

본 논문에서는 신체 증상을 고려하지 않는 기존 작업의 한계를 해결하고 VR 질병을 더 잘 이해하기 위한 새로운 객관적인 신체 증상 예측 방법을 제안합니다. 또한, 4가지 프레임 레이트의 360도 영상 80편을 구축하고, 생리적 신호(HR, GSR)와 신체 증상 점수에 대한 주관적 설문지(SSQ 점수)를 얻기 위해 광범위한 주관적 실험을 실시했다. 광범위한 실험을 통해 모델이 VR 질병의 전체 점수뿐만 아니라 VR 질병의 신체적 증상도 제공할 수 있다는 것이 입증되었습니다. 이는 VR 콘텐츠의 보안을 살펴보기 위한 실용적인 응용 프로그램 역할을 할 수 있습니다.

다중 사용자 VR 환경의 네트워킹 성능에 관한 연구는 VR에서 다중 사용자 상호 작용의 구현 및 최적화 기술을 탐구합니다.

멀티플레이어 VR용 CS 아키텍처. 이 아키텍처에서 서버는 연결된 클라이언트에 데이터를 전송하는 등 애플리케이션의 모든 측면을 제어합니다. 이러한 맥락에서 서버는 클라이언트가 연결할 수 있는 게임 인스턴스이며, 클라이언트도 게임 인스턴스입니다. 주요 차이점은 클라이언트가 장면의 네트워크 개체에 대한 권한이 없다는 것입니다. 즉, 클라이언트는 장면의 다른 개체에 대한 변경 사항을 업데이트할 수 없습니다. 서버는 모든 네트워크 개체에 대한 권한을 갖고 있으므로 연결된 클라이언트를 장면의 모든 변경 사항으로 업데이트하고 들어오는 요청을 처리하는 일을 담당합니다. 클라이언트는 다른 사람에게 알리지 않고도 로컬에서 장면을 변경할 수 있습니다. 컨트롤러 입력 처리, 플레이어 카메라 설정 등 다양한 상황에서 유용합니다.

VR의 가상 손: 모션 캡처, 합성 및 인식 정보는 VR의 모션 캡처, 합성 및 인식과 관련된 기술에 대한 심층적 설명을 제공합니다. 관심 있는 어린이들은 놓치지 마세요. [QuickTime VR – An Image-Based Approach to Virtual Environment Navigation](QuickTime VR – An Image-Based Approach to Virtual Environment Navigation.pdf)에서는 이미지 기반 가상 환경 탐색 방법에 대해 설명합니다.

——QuickTimeVR. 가상 현실을 위한 시각적 현실 세계의 캡처, 재구성 및 표현은 VR의 모션 캡처, 재구성 및 표현 기술을 분석합니다. VR HMD용 고충실도 얼굴 및 음성 애니메이션에서는 VR 헤드셋의 고충실도 얼굴 및 음성 애니메이션 캡처 및 재구성에 대해 설명합니다. [실시간 렌더링 및 가상 현실을 위한 임시 적응형 셰이딩 재사용](실시간 렌더링 및 가상 현실을 위한 임시 적응형 셰이딩 재사용.pdf)에서는 실시간 렌더링 및 가상 현실을 위한 시간 적응형 셰이딩 재사용 기술을 공유합니다. 또한 일부 회사(예: Huawei)에서는 클라우드 렌더링을 기반으로 한 VR 아키텍처를 연구하고 있습니다.

15.3 UE XR

15.3.1 UE XR 개요

UE3 시대부터 VR 렌더링은 노드 그래프를 통해 지원되었습니다. 노드 그래프에서는 렌더링 파이프라인의 다양한 구성 요소를 여러 구성으로 재배치, 수정 및 다시 연결할 수 있습니다. 지원되는 노드 유형에 따라 VR 기술에 특정한 효과를 생성하는 방식으로 노드를 구성할 수도 있습니다. 아래 이미지는 포스트 프로세스 파이프라인로 빨간색-청록색 입체 이미지를 렌더링하도록 구성된 언리얼 엔진의 머티리얼 편집 인터페이스를 보여주는 예입니다.

UE3의 머티리얼 에디터를 사용하여 Unreal Engine이 적청색 입체성을 지원하도록 구성하세요. 이러한 방식으로 편광 입체 디스플레이를 위한 이미지 인터레이스 등 다른 입체 인코딩을 지원할 수 있습니다.

다음은 2013년 전후 VR에 대한 게임 엔진 지원 표입니다.

현재 UE4.27 및 이후 버전은 이미 AR, VR, MR 및 기타 기술을 지원하고 Google, Apple, Microsoft, Maigic Leap, Oculus, SteamVR, Samsung 및 기타 회사와 이들의 다양한 XR 플랫폼을 지원하며 물론 OpenXR과 같은 표준 인터페이스도 포함합니다.

15.3.2 UE XR 소스 코드 분석

언리얼 렌더링 시스템 분석(12) - 모바일 특집 1부(UE 모바일 렌더링 분석)에서는 UE의 모바일 소스코드를 자세히 분석하고, XR의 렌더링 기술도 일부 분석했습니다. 다음은 XR 렌더링의 몇 가지 핵심 사항을 분석한 것입니다. 이 섹션에서는 분석을 위한 청사진으로 UE 4.27.2를 사용합니다.

15.3.2.1 멀티뷰

UE의 Multi-View는 다음 인터페이스 설정을 통해 켜거나 끌 수 있습니다:

코드에서는 콘솔 변수 vr.MobileMultiView에 그 값이 저장되며, 이 콘솔 변수와 관련된 주요 코드는 다음과 같습니다.

cpp
// MobileShadingRenderer.cpp

FRHITexture* FMobileSceneRenderer::RenderForward(FRHICommandListImmediate& RHICmdList, const TArrayView<const FViewInfo*> ViewList)
{
    (...)

    // 페치/가져오기(Fetch)변수
    static const auto CVarMobileMultiView = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("vr.MobileMultiView"));
    const bool bIsMultiViewApplication = (CVarMobileMultiView && CVarMobileMultiView->GetValueOnAnyThread() != 0);

    (...)

    // 만약 scenecolor는 뷰(View),그러나 적용 는 뷰(View),이면 에 의해 셰이딩 렌더링(Render)로 뷰(View)뷰(View)。
    SceneColorRenderPassInfo.MultiViewCount = View.bIsMobileMultiViewEnabled ? 2 : (bIsMultiViewApplication ? 1 : 0);
    
    (...)
}

// VulkanRenderTarget.cpp

FVulkanRenderTargetLayout::FVulkanRenderTargetLayout(const FGraphicsPipelineStateInitializer& Initializer)
{
    (...)
    
    FRenderPassCompatibleHashableStruct CompatibleHashInfo;
    
    (...)
    
    MultiViewCount = Initializer.MultiViewCount;
    
    (...)
    
    CompatibleHashInfo.MultiViewCount = MultiViewCount;
    
    (...)
}

// VulkanRHI.cpp

static VkRenderPass CreateRenderPass(FVulkanDevice& InDevice, const FVulkanRenderTargetLayout& RTLayout)
{
    (...)
    
    // 0b11 for 2, 0b1111 for 4, and so on
    uint32 MultiviewMask = ( 0b1 << RTLayout.GetMultiViewCount() ) - 1;
    
    (...)
    
    const uint32_t ViewMask[2] = { MultiviewMask, MultiviewMask };
    const uint32_t CorrelationMask = MultiviewMask;
    
    VkRenderPassMultiviewCreateInfo MultiviewInfo;
    if (RTLayout.GetIsMultiView())
    {
        FMemory::Memzero(MultiviewInfo);
        MultiviewInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_MULTIVIEW_CREATE_INFO;
        MultiviewInfo.pNext = nullptr;
        MultiviewInfo.subpassCount = NumSubpasses;
        MultiviewInfo.pViewMasks = ViewMask;
        MultiviewInfo.dependencyCount = 0;
        MultiviewInfo.pViewOffsets = nullptr;
        MultiviewInfo.correlationMaskCount = 1;
        MultiviewInfo.pCorrelationMasks = &CorrelationMask;

        CreateInfo.pNext = &MultiviewInfo;
    }
    
    (...)
}

위는 Vulkan 그래픽 API 처리를 위한 것입니다. OpenGL ES의 경우 특정 튜토리얼은 다중 뷰 렌더링 사용을 참조하세요. UE에는 또 다른 처리 코드도 있습니다:

cpp
// OpenGLES.cpp

void FOpenGLES::ProcessExtensions(const FString& ExtensionsString)
{
    (...)
    
    // 검사/감지(Detect)는 지원 Multi-View확장
    const bool bMultiViewSupport = ExtensionsString.Contains(TEXT("GL_OVR_multiview"));
    const bool bMultiView2Support = ExtensionsString.Contains(TEXT("GL_OVR_multiview2"));
    const bool bMultiViewMultiSampleSupport = ExtensionsString.Contains(TEXT("GL_OVR_multiview_multisampled_render_to_texture"));
    if (bMultiViewSupport && bMultiView2Support && bMultiViewMultiSampleSupport)
    {
        glFramebufferTextureMultiviewOVR = (PFNGLFRAMEBUFFERTEXTUREMULTIVIEWOVRPROC)((void*)eglGetProcAddress("glFramebufferTextureMultiviewOVR"));
        glFramebufferTextureMultisampleMultiviewOVR = (PFNGLFRAMEBUFFERTEXTUREMULTISAMPLEMULTIVIEWOVRPROC)((void*)eglGetProcAddress("glFramebufferTextureMultisampleMultiviewOVR"));

        bSupportsMobileMultiView = (glFramebufferTextureMultiviewOVR != NULL) && (glFramebufferTextureMultisampleMultiviewOVR != NULL);
    }
    
    (...)
}

// OpenGLES.h

struct FOpenGLES : public FOpenGLBase
{
    (...)
    
    static FORCEINLINE bool SupportsMobileMultiView() { return bSupportsMobileMultiView; }
    
    (...)
}

// OpenGLDevice.cpp

static void InitRHICapabilitiesForGL()
{
    (...)
    
    GSupportsMobileMultiView = FOpenGL::SupportsMobileMultiView();
    
    (...)
}

// OpenGLRenderTarget.cpp

GLuint FOpenGLDynamicRHI::GetOpenGLFramebuffer(uint32 NumSimultaneousRenderTargets, FOpenGLTextureBase** RenderTargets, const uint32* ArrayIndices, const uint32* MipmapLevels, FOpenGLTextureBase* DepthStencilTarget)
{
    (...)
    
if PLATFORM_ANDROID && !PLATFORM_LUMINGL4
    static const auto CVarMobileMultiView = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("vr.MobileMultiView"));

    // 만약 활성화(Enable)지원 ,할당(Allocate)뷰(View)프레임버퍼 。
    // 뷰(View)지원 로드/읽기(Read)버퍼 ,비활성화(Disable)바인딩(Bind)GL_DRAW_FRAMEBUFFER.
    const bool bRenderTargetsDefined = (RenderTargets != nullptr) && RenderTargets[0];
    const bool bValidMultiViewDepthTarget = !DepthStencilTarget || DepthStencilTarget->Target == GL_TEXTURE_2D_ARRAY;
    const bool bUsingArrayTextures = (bRenderTargetsDefined) ? (RenderTargets[0]->Target == GL_TEXTURE_2D_ARRAY && bValidMultiViewDepthTarget) : false;
    const bool bMultiViewCVar = CVarMobileMultiView && CVarMobileMultiView->GetValueOnAnyThread() != 0;

    if (bUsingArrayTextures && FOpenGL::SupportsMobileMultiView() && bMultiViewCVar)
    {
        FOpenGLTextureBase* const RenderTarget = RenderTargets[0];
        glBindFramebuffer(GL_FRAMEBUFFER, 0);
        glBindFramebuffer(GL_DRAW_FRAMEBUFFER, Framebuffer);

        FOpenGLTexture2D* RenderTarget2D = (FOpenGLTexture2D*)RenderTarget;
        const uint32 NumSamplesTileMem = RenderTarget2D->GetNumSamplesTileMem();
        if (NumSamplesTileMem > 1)
        {
            glFramebufferTextureMultisampleMultiviewOVR(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, RenderTarget->GetResource(), 0, NumSamplesTileMem, 0, 2);
            VERIFY_GL(glFramebufferTextureMultisampleMultiviewOVR);

            if (DepthStencilTarget)
            {
                glFramebufferTextureMultisampleMultiviewOVR(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, DepthStencilTarget->GetResource(), 0, NumSamplesTileMem, 0, 2);
                VERIFY_GL(glFramebufferTextureMultisampleMultiviewOVR);
            }
        }
        else
        {
            glFramebufferTextureMultiviewOVR(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, RenderTarget->GetResource(), 0, 0, 2);
            VERIFY_GL(glFramebufferTextureMultiviewOVR);

            if (DepthStencilTarget)
            {
                glFramebufferTextureMultiviewOVR(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, DepthStencilTarget->GetResource(), 0, 0, 2);
                VERIFY_GL(glFramebufferTextureMultiviewOVR);
            }
        }

        FOpenGL::CheckFrameBuffer();

        FOpenGL::ReadBuffer(GL_NONE);
        FOpenGL::DrawBuffer(GL_COLOR_ATTACHMENT0);

        GetOpenGLFramebufferCache().Add(FOpenGLFramebufferKey(NumSimultaneousRenderTargets, RenderTargets, ArrayIndices, MipmapLevels, DepthStencilTarget, PlatformOpenGLCurrentContext(PlatformDevice)), Framebuffer + 1);
        
        return Framebuffer;
    }
#endif
    
    (...)
}

해당 셰이더 코드는 해당 키워드나 명령문을 추가해야 합니다.

cpp
// OpenGLShaders.cpp

void OPENGLDRV_API GLSLToDeviceCompatibleGLSL(...)
{
    // Whether we need to emit mobile multi-view code or not.
    const bool bEmitMobileMultiView = (FCStringAnsi::Strstr(GlslCodeOriginal.GetData(), "gl_ViewID_OVR") != nullptr);
    
    (...)
    
    if (bEmitMobileMultiView)
    {
        MoveHashLines(GlslCode, GlslCodeOriginal);

        if (GSupportsMobileMultiView)
        {
            AppendCString(GlslCode, "\n\n");
            AppendCString(GlslCode, "#extension GL_OVR_multiview2 : enable\n");
            AppendCString(GlslCode, "\n\n");
        }
        else
        {
            // Strip out multi-view for devices that don't support it.
            AppendCString(GlslCode, "#define gl_ViewID_OVR 0\n");
        }
    }
    
    (...)
}

void OPENGLDRV_API GLSLToDeviceCompatibleGLSL(...)
{
    (...)
    
    if (bEmitMobileMultiView && GSupportsMobileMultiView && TypeEnum == GL_VERTEX_SHADER)
    {
        AppendCString(GlslCode, "\n\n");
        AppendCString(GlslCode, "layout(num_views = 2) in;\n");
        AppendCString(GlslCode, "\n\n");
    }
    
    (...)
}

셰이더 코드에서 MOBILE_MULTI_VIEW를 사용하여 모바일 멀티뷰 활성화 여부를 지정합니다.

cpp
// MobileBasePassVertexShader.usf

void Main(
    FVertexFactoryInput Input
    , out FMobileShadingBasePassVSOutput Output
#if INSTANCED_STEREO
    , uint InstanceId : SV_InstanceID
    , out uint LayerIndex : SV_RenderTargetArrayIndex
#elif MOBILE_MULTI_VIEW
    // 테이블 모바일 플랫폼(Mobile)뷰(View)의 뷰(View)。
    , in uint ViewId : SV_ViewID
#endif
    )
{
    (...)
#elif MOBILE_MULTI_VIEW
    // 에 따라 ViewId뷰(View),의 결과 。
    #if COMPILER_GLSL_ES3_1
        const int MultiViewId = int(ViewId);
        ResolvedView = ResolveView(uint(MultiViewId));
        Output.BasePassInterpolants.MultiViewId = float(MultiViewId);
    #else
        ResolvedView = ResolveView(ViewId);
        Output.BasePassInterpolants.MultiViewId = float(ViewId);
    #endif
#else
    (...)
}

// InstancedStereo.ush

ViewState ResolveView(uint ViewIndex)
{
    if (ViewIndex == 0)
    {
        return GetPrimaryView();
    }
    else
    {
        return GetInstancedView();
    }
}

// ShaderCompiler.cpp

ENGINE_API void GenerateInstancedStereoCode(FString& Result, EShaderPlatform ShaderPlatform)
{
    (...)

    // 정의 ViewState
    Result =  "struct ViewState\r\n";
    Result += "{\r\n";
    for (int32 MemberIndex = 0; MemberIndex < StructMembers.Num(); ++MemberIndex)
    {
        const FShaderParametersMetadata::FMember& Member = StructMembers[MemberIndex];
        FString MemberDecl;
        GenerateUniformBufferStructMember(MemberDecl, StructMembers[MemberIndex], ShaderPlatform);
        Result += FString::Printf(TEXT("\t%s;\r\n"), *MemberDecl);
    }
    Result += "};\r\n";

    // 정의 GetPrimaryView
    Result += "ViewState GetPrimaryView()\r\n";
    Result += "{\r\n";
    Result += "\tViewState Result;\r\n";
    for (int32 MemberIndex = 0; MemberIndex < StructMembers.Num(); ++MemberIndex)
    {
        const FShaderParametersMetadata::FMember& Member = StructMembers[MemberIndex];
        Result += FString::Printf(TEXT("\tResult.%s = View.%s;\r\n"), Member.GetName(), Member.GetName());
    }
    Result += "\treturn Result;\r\n";
    Result += "}\r\n";

    // 정의 GetInstancedView
    Result += "ViewState GetInstancedView()\r\n";
    Result += "{\r\n";
    Result += "\tViewState Result;\r\n";
    for (int32 MemberIndex = 0; MemberIndex < StructMembers.Num(); ++MemberIndex)
    {
        const FShaderParametersMetadata::FMember& Member = StructMembers[MemberIndex];
        Result += FString::Printf(TEXT("\tResult.%s = InstancedView.%s;\r\n"), Member.GetName(), Member.GetName());
    }
    Result += "\treturn Result;\r\n";
    Result += "}\r\n";
    
    (...)
}

15.3.2.2 고정 포브레이션

고정 포비티드 렌더링은 UE 프로젝트 설정의 VR 페이지에서도 켤 수 있으며, 해당 콘솔 변수는 vr.VRS.HMDFixedFoveationLevel입니다. UE 관련 처리 코드는 다음과 같습니다.

cpp
// VariableRateShadingImageManager.cpp

FRDGTextureRef FVariableRateShadingImageManager::GetVariableRateShadingImage(FRDGBuilder& GraphBuilder, const FSceneViewFamily& ViewFamily, const TArray<TRefCountPtr<IPooledRenderTarget>>* ExternalVRSSources, EVRSType VRSTypesToExclude)
{
    // 만약 RHI지원 VRS,즉, 반환(Return)。
    if (!GRHISupportsAttachmentVariableRateShading || !GRHIVariableRateShadingEnabled || !GRHIAttachmentVariableRateShadingEnabled)
    {
        return nullptr;
    }

    // 갱신(Update),즉, VRS。
    Tick();

    if (EnumHasAllFlags(VRSTypesToExclude, EVRSType::All))
    {
        return nullptr;
    }

    FVRSImageGenerationParameters VRSImageParams;

    const bool bIsStereo = IStereoRendering::IsStereoEyeView(*ViewFamily.Views[0]) && GEngine->XRSystem.IsValid();
    
    VRSImageParams.bInstancedStereo |= ViewFamily.Views[0]->IsInstancedStereoPass();
    VRSImageParams.Size = FIntPoint(ViewFamily.RenderTarget->GetSizeXY());

    UpdateFixedFoveationParameters(VRSImageParams);
    UpdateEyeTrackedFoveationParameters(VRSImageParams, ViewFamily);

    EVRSGenerationFlags GenFlags = EVRSGenerationFlags::None;

    // 설정(Set)XR foveation VRS의 。
    if (bIsStereo && !EnumHasAnyFlags(VRSTypesToExclude, EVRSType::XRFoveation) && !EnumHasAnyFlags(VRSTypesToExclude, EVRSType::EyeTrackedFoveation))
    {
        EnumAddFlags(GenFlags, EVRSGenerationFlags::StereoRendering);

        if (!EnumHasAnyFlags(VRSTypesToExclude, EVRSType::FixedFoveation) && VRSImageParams.bGenerateFixedFoveation)
        {
            EnumAddFlags(GenFlags, EVRSGenerationFlags::HMDFixedFoveation);
        }

        if (!EnumHasAllFlags(VRSTypesToExclude, EVRSType::EyeTrackedFoveation) && VRSImageParams.bGenerateEyeTrackedFoveation)
        {
            EnumAddFlags(GenFlags, EVRSGenerationFlags::HMDEyeTrackedFoveation);
        }

        if (VRSImageParams.bInstancedStereo)
        {
            EnumAddFlags(GenFlags, EVRSGenerationFlags::SideBySideStereo);
        }
    }

    if (GenFlags == EVRSGenerationFlags::None)
    {
        if (ExternalVRSSources == nullptr || ExternalVRSSources->Num() == 0)
        {
            // Nothing to generate.
            return nullptr;
        }
        else
        {
            // If there's one external VRS image, just return that since we're not building anything here.
            if (ExternalVRSSources->Num() == 1)
            {
                const FIntVector& ExtSize = (*ExternalVRSSources)[0]->GetDesc().GetSize();
                check(ExtSize.X == VRSImageParams.Size.X / GRHIVariableRateShadingImageTileMinWidth && ExtSize.Y == VRSImageParams.Size.Y / GRHIVariableRateShadingImageTileMinHeight);
                return GraphBuilder.RegisterExternalTexture((*ExternalVRSSources)[0]);
            }

            // If there is more than one external image, we'll generate a final one by combining, so fall through.
        }
    }

    // 페치/가져오기(Fetch)FOV
    IHeadMountedDisplay* HMDDevice = (GEngine->XRSystem == nullptr) ? nullptr : GEngine->XRSystem->GetHMDDevice();
    if (HMDDevice != nullptr)
    {
        HMDDevice->GetFieldOfView(VRSImageParams.HMDFieldOfView.X, VRSImageParams.HMDFieldOfView.Y);
    }

    const uint64 Key = CalculateVRSImageHash(VRSImageParams, GenFlags);
    FActiveTarget* ActiveTarget = ActiveVRSImages.Find(Key);
    if (ActiveTarget == nullptr)
    {
        // 렌더링(Render)VRS
        return GraphBuilder.RegisterExternalTexture(RenderShadingRateImage(GraphBuilder, Key, VRSImageParams, GenFlags));
    }

    ActiveTarget->LastUsedFrame = GFrameNumber;

    return GraphBuilder.RegisterExternalTexture(ActiveTarget->Target);
}

// 렌더링(Render)PC의 VRS
TRefCountPtr<IPooledRenderTarget> FVariableRateShadingImageManager::RenderShadingRateImage(...)
{
    (...)
}

// 렌더링(Render)모바일 플랫폼(Mobile)의 VRS
TRefCountPtr<IPooledRenderTarget> FVariableRateShadingImageManager::GetMobileVariableRateShadingImage(const FSceneViewFamily& ViewFamily)
{
    if (!(IStereoRendering::IsStereoEyeView(*ViewFamily.Views[0]) && GEngine->XRSystem.IsValid()))
    {
        return TRefCountPtr<IPooledRenderTarget>();
    }

    FIntPoint Size(ViewFamily.RenderTarget->GetSizeXY());

    const bool bStereo = GEngine->StereoRenderingDevice.IsValid() && GEngine->StereoRenderingDevice->IsStereoEnabled();
    IStereoRenderTargetManager* const StereoRenderTargetManager = bStereo ? GEngine->StereoRenderingDevice->GetRenderTargetManager() : nullptr;

    FTexture2DRHIRef Texture;
    FIntPoint TextureSize(0, 0);

    // 만약 지원 ,로 VR포인트 할당(Allocate)해상도 텍스처(Texture)。
    if (StereoRenderTargetManager && StereoRenderTargetManager->NeedReAllocateShadingRateTexture(MobileHMDFixedFoveationOverrideImage))
    {
        bool bAllocatedShadingRateTexture = StereoRenderTargetManager->AllocateShadingRateTexture(0, Size.X, Size.Y, GRHIVariableRateShadingImageFormat, 0, TexCreate_None, TexCreate_None, Texture, TextureSize);
        if (bAllocatedShadingRateTexture)
        {
            MobileHMDFixedFoveationOverrideImage = CreateRenderTarget(Texture, TEXT("ShadingRate"));
        }
    }

    return MobileHMDFixedFoveationOverrideImage;
}

셰이더 코드는 다음과 같습니다.

cpp
// VariableRateShading.usf

(...)

uint GetFoveationShadingRate(float FractionalOffset, float FullCutoffSquared, float HalfCutoffSquared)
{
    if (FractionalOffset > HalfCutoffSquared)
    {
        return SHADING_RATE_4x4;
    }

    if (FractionalOffset > FullCutoffSquared)
    {
        return SHADING_RATE_2x2;
    }

    return SHADING_RATE_1x1;
}

uint GetFixedFoveationRate(uint2 PixelPositionIn)
{
    const float2 PixelPosition = float2((float)PixelPositionIn.x, (float)PixelPositionIn.y);
    const float FractionalOffset = GetFractionalOffsetFromEyeOrigin(PixelPosition);
    return GetFoveationShadingRate(FractionalOffset, FixedFoveationFullRateCutoffSquared, FixedFoveationHalfRateCutoffSquared);
}

uint GetEyetrackedFoveationRate(uint2 PixelPositionIn)
{
    return SHADING_RATE_1x1;
}

////////////////////////////////////////////////////////////////////////////////////////////////////
// Return the ideal combination of two specified shading rate values.
////////////////////////////////////////////////////////////////////////////////////////////////////

// 개 셰이딩 ,는 의 개 。
uint CombineShadingRates(uint Rate1, uint Rate2)
{
    return max(Rate1, Rate2);
}

// 셰이딩 텍스처(Texture)
[numthreads(THREADGROUP_SIZEX, THREADGROUP_SIZEY, 1)]
void GenerateShadingRateTexture(uint3 DispatchThreadId : SV_DispatchThreadID)
{
    const uint2 TexelCoord = DispatchThreadId.xy;
    uint ShadingRateOut = 0;

    if ((ShadingRateAttachmentGenerationFlags & HMD_FIXED_FOVEATION) != 0)
    {
        ShadingRateOut = CombineShadingRates(ShadingRateOut, GetFixedFoveationRate(TexelCoord));
    }

    if ((ShadingRateAttachmentGenerationFlags & HMD_EYETRACKED_FOVEATION) != 0)
    {
        ShadingRateOut = CombineShadingRates(ShadingRateOut, GetEyetrackedFoveationRate(TexelCoord));
    }

    // Conservative combination, just return the max of the two.
    RWOutputTexture[TexelCoord] = ShadingRateOut;
}

고정된 시선점을 달성하기 위해서는 VRS의 특성이 필요함을 알 수 있다.

15.3.2.3 OpenXR

OpenXR은 UE에 내장된 플러그인으로, 플러그인 인터페이스에서 검색하고 열 수 있습니다.

OpenXR용 플러그인 코드는 Engine\Plugins\Runtime\OpenXR에 있습니다. OpenXR의 표준 인터페이스는 다음과 같습니다.

cpp
// OpenXRCore.h

/** List all OpenXR global entry points used by Unreal. */
#define ENUM_XR_ENTRYPOINTS_GLOBAL(EnumMacro) \
    EnumMacro(PFN_xrEnumerateApiLayerProperties,xrEnumerateApiLayerProperties) \
    EnumMacro(PFN_xrEnumerateInstanceExtensionProperties,xrEnumerateInstanceExtensionProperties) \
    EnumMacro(PFN_xrCreateInstance,xrCreateInstance)

/** List all OpenXR instance entry points used by Unreal. */
#define ENUM_XR_ENTRYPOINTS(EnumMacro) \
    EnumMacro(PFN_xrDestroyInstance,xrDestroyInstance) \
    EnumMacro(PFN_xrGetInstanceProperties,xrGetInstanceProperties) \
    EnumMacro(PFN_xrPollEvent,xrPollEvent) \
    EnumMacro(PFN_xrResultToString,xrResultToString) \
    EnumMacro(PFN_xrStructureTypeToString,xrStructureTypeToString) \
    EnumMacro(PFN_xrGetSystem,xrGetSystem) \
    EnumMacro(PFN_xrGetSystemProperties,xrGetSystemProperties) \
    EnumMacro(PFN_xrEnumerateEnvironmentBlendModes,xrEnumerateEnvironmentBlendModes) \
    EnumMacro(PFN_xrCreateSession,xrCreateSession) \
    EnumMacro(PFN_xrDestroySession,xrDestroySession) \
    EnumMacro(PFN_xrEnumerateReferenceSpaces,xrEnumerateReferenceSpaces) \
    EnumMacro(PFN_xrCreateReferenceSpace,xrCreateReferenceSpace) \
    EnumMacro(PFN_xrGetReferenceSpaceBoundsRect,xrGetReferenceSpaceBoundsRect) \
    EnumMacro(PFN_xrCreateActionSpace,xrCreateActionSpace) \
    EnumMacro(PFN_xrLocateSpace,xrLocateSpace) \
    EnumMacro(PFN_xrDestroySpace,xrDestroySpace) \
    EnumMacro(PFN_xrEnumerateViewConfigurations,xrEnumerateViewConfigurations) \
    EnumMacro(PFN_xrGetViewConfigurationProperties,xrGetViewConfigurationProperties) \
    EnumMacro(PFN_xrEnumerateViewConfigurationViews,xrEnumerateViewConfigurationViews) \
    EnumMacro(PFN_xrEnumerateSwapchainFormats,xrEnumerateSwapchainFormats) \
    EnumMacro(PFN_xrCreateSwapchain,xrCreateSwapchain) \
    EnumMacro(PFN_xrDestroySwapchain,xrDestroySwapchain) \
    EnumMacro(PFN_xrEnumerateSwapchainImages,xrEnumerateSwapchainImages) \
    EnumMacro(PFN_xrAcquireSwapchainImage,xrAcquireSwapchainImage) \
    EnumMacro(PFN_xrWaitSwapchainImage,xrWaitSwapchainImage) \
    EnumMacro(PFN_xrReleaseSwapchainImage,xrReleaseSwapchainImage) \
    EnumMacro(PFN_xrBeginSession,xrBeginSession) \
    EnumMacro(PFN_xrEndSession,xrEndSession) \
    EnumMacro(PFN_xrRequestExitSession,xrRequestExitSession) \
    EnumMacro(PFN_xrWaitFrame,xrWaitFrame) \
    EnumMacro(PFN_xrBeginFrame,xrBeginFrame) \
    EnumMacro(PFN_xrEndFrame,xrEndFrame) \
    EnumMacro(PFN_xrLocateViews,xrLocateViews) \
    EnumMacro(PFN_xrStringToPath,xrStringToPath) \
    EnumMacro(PFN_xrPathToString,xrPathToString) \
    EnumMacro(PFN_xrCreateActionSet,xrCreateActionSet) \
    EnumMacro(PFN_xrDestroyActionSet,xrDestroyActionSet) \
    EnumMacro(PFN_xrCreateAction,xrCreateAction) \
    EnumMacro(PFN_xrDestroyAction,xrDestroyAction) \
    EnumMacro(PFN_xrSuggestInteractionProfileBindings,xrSuggestInteractionProfileBindings) \
    EnumMacro(PFN_xrAttachSessionActionSets,xrAttachSessionActionSets) \
    EnumMacro(PFN_xrGetCurrentInteractionProfile,xrGetCurrentInteractionProfile) \
    EnumMacro(PFN_xrGetActionStateBoolean,xrGetActionStateBoolean) \
    EnumMacro(PFN_xrGetActionStateFloat,xrGetActionStateFloat) \
    EnumMacro(PFN_xrGetActionStateVector2f,xrGetActionStateVector2f) \
    EnumMacro(PFN_xrGetActionStatePose,xrGetActionStatePose) \
    EnumMacro(PFN_xrSyncActions,xrSyncActions) \
    EnumMacro(PFN_xrEnumerateBoundSourcesForAction,xrEnumerateBoundSourcesForAction) \
    EnumMacro(PFN_xrGetInputSourceLocalizedName,xrGetInputSourceLocalizedName) \
    EnumMacro(PFN_xrApplyHapticFeedback,xrApplyHapticFeedback) \
    EnumMacro(PFN_xrStopHapticFeedback,xrStopHapticFeedback)

완성된 OpenXR 인터페이스는 XR Spec에서 찾을 수 있습니다. UE와 관련된 중요한 유형과 인터페이스는 다음과 같습니다.

cpp
// OpenXRAR.h

// OpenXR
class FOpenXRARSystem :
    public FARSystemSupportBase,
    public IOpenXRARTrackedMeshHolder,
    public IOpenXRARTrackedGeometryHolder,
    public FGCObject,
    public TSharedFromThis<FOpenXRARSystem, ESPMode::ThreadSafe>
{
public:
    FOpenXRARSystem();
    virtual ~FOpenXRARSystem();

    void SetTrackingSystem(TSharedPtr<FXRTrackingSystemBase, ESPMode::ThreadSafe> InTrackingSystem);

    virtual void OnARSystemInitialized();
    virtual bool OnStartARGameFrame(FWorldContext& WorldContext);

    virtual void OnStartARSession(UARSessionConfig* SessionConfig);
    virtual void OnPauseARSession();
    virtual void OnStopARSession();
    virtual FARSessionStatus OnGetARSessionStatus() const;
    virtual bool OnIsSessionTrackingFeatureSupported(EARSessionType SessionType, EARSessionTrackingFeature SessionTrackingFeature) const;

    (...)

private:
    // FOpenXRHMD인스턴스
    FOpenXRHMD* TrackingSystem;
        
    class IOpenXRCustomAnchorSupport* CustomAnchorSupport = nullptr;
    FARSessionStatus SessionStatus;

    class IOpenXRCustomCaptureSupport* QRCapture = nullptr;
    class IOpenXRCustomCaptureSupport* CamCapture = nullptr;
    class IOpenXRCustomCaptureSupport* SpatialMappingCapture = nullptr;
    class IOpenXRCustomCaptureSupport* SceneUnderstandingCapture = nullptr;
    class IOpenXRCustomCaptureSupport* HandMeshCapture = nullptr;

    TArray<IOpenXRCustomCaptureSupport*> CustomCaptureSupports;
        
    (...)
};

// IHeadMountedDisplayModule.h

// 모듈 의 인터페이스 .
class IHeadMountedDisplayModule : public IModuleInterface, public IModularFeature
{
public:
    static FName GetModularFeatureName();
    virtual FString GetModuleKeyName() const = 0;
    virtual void GetModuleAliases(TArray<FString>& AliasesOut) const;
    float GetModulePriority() const;
    
    static inline IHeadMountedDisplayModule& Get();
    static inline bool IsAvailable();

    virtual void StartupModule() override;
    virtual bool PreInit();
    virtual bool IsHMDConnected();

    virtual uint64 GetGraphicsAdapterLuid();

    virtual FString GetAudioInputDevice();
    virtual FString GetAudioOutputDevice();

    virtual TSharedPtr< class IXRTrackingSystem, ESPMode::ThreadSafe > CreateTrackingSystem() = 0;
    virtual TSharedPtr< IHeadMountedDisplayVulkanExtensions, ESPMode::ThreadSafe > GetVulkanExtensions();
    virtual bool IsStandaloneStereoOnlyDevice();
};

// IOpenXRHMDPlugin.h

// 모듈 의 인터페이스 。,인터페이스 내에서 의 단계 모듈 。
class OPENXRHMD_API IOpenXRHMDPlugin : public IHeadMountedDisplayModule
{
public:
    static inline IOpenXRHMDPlugin& Get()
    {
        return FModuleManager::LoadModuleChecked< IOpenXRHMDPlugin >( "OpenXRHMD" );
    }
    
    static inline bool IsAvailable();

    virtual bool IsExtensionAvailable(const FString& Name) const = 0;
    virtual bool IsExtensionEnabled(const FString& Name) const = 0;

    virtual bool IsLayerAvailable(const FString& Name) const = 0;
    virtual bool IsLayerEnabled(const FString& Name) const = 0;
};

// OpenXRHMD.cpp

class FOpenXRHMDPlugin : public IOpenXRHMDPlugin
{
public:
    FOpenXRHMDPlugin();
    ~FOpenXRHMDPlugin();
    
    // 생성(Create)(FOpenXRHMD인스턴스 )
    virtual TSharedPtr< class IXRTrackingSystem, ESPMode::ThreadSafe > CreateTrackingSystem() override
    {
        if (!RenderBridge)
        {
            if (!InitRenderBridge())
            {
                return nullptr;
            }
        }
        // 로드 IOpenXRARModule。
        auto ARModule = FModuleManager::LoadModulePtr<IOpenXRARModule>("OpenXRAR");
        // 생성(Create)AR。
        auto ARSystem = ARModule->CreateARSystem();

        // 생성(Create)FOpenXRHMD인스턴스 .
        auto OpenXRHMD = FSceneViewExtensions::NewExtension<FOpenXRHMD>(Instance, System, RenderBridge, EnabledExtensions, ExtensionPlugins, ARSystem);
        if (OpenXRHMD->IsInitialized())
        {
            // 초기화(Initialize)ARSystem.
            ARModule->SetTrackingSystem(OpenXRHMD);
            OpenXRHMD->GetARCompositionComponent()->InitializeARSystem();
            return OpenXRHMD;
        }

        return nullptr;
    }
    
    (...)
    
private:
    void *LoaderHandle;
    // XR
    XrInstance Instance;
    XrSystemId System;
    TSet<FString> AvailableExtensions;
    TSet<FString> AvailableLayers;
    TArray<const char*> EnabledExtensions;
    TArray<const char*> EnabledLayers;
    // IOpenXRHMDPlugin
    TArray<IOpenXRExtensionPlugin*> ExtensionPlugins;
    TRefCountPtr<FOpenXRRenderBridge> RenderBridge;
    TSharedPtr< IHeadMountedDisplayVulkanExtensions, ESPMode::ThreadSafe > VulkanExtensions;

    // 초기화(Initialize)의 인터페이스
    bool InitRenderBridge();
    bool InitInstanceAndSystem();
    bool InitInstance();
    bool InitSystem();
    
    (...)
};

// XRTrackingSystemBase.h

class HEADMOUNTEDDISPLAY_API FXRTrackingSystemBase : public IXRTrackingSystem
{
public:
    FXRTrackingSystemBase(IARSystemSupport* InARImplementation);
    virtual ~FXRTrackingSystemBase();

    virtual bool DoesSupportPositionalTracking() const override { return false; }
    virtual bool HasValidTrackingPosition() override { return DoesSupportPositionalTracking(); }
    virtual uint32 CountTrackedDevices(EXRTrackedDeviceType Type = EXRTrackedDeviceType::Any) override;
    virtual bool IsTracking(int32 DeviceId) override;
    virtual bool GetTrackingSensorProperties(int32 DeviceId, FQuat& OutOrientation, FVector& OutPosition, FXRSensorProperties& OutSensorProperties) override;
    virtual EXRTrackedDeviceType GetTrackedDeviceType(int32 DeviceId) const override;
    
    virtual TSharedPtr< class IXRCamera, ESPMode::ThreadSafe > GetXRCamera(int32 DeviceId = HMDDeviceId) override;

    virtual bool GetRelativeEyePose(int32 DeviceId, EStereoscopicPass Eye, FQuat& OutOrientation, FVector& OutPosition) override;

    virtual void SetTrackingOrigin(EHMDTrackingOrigin::Type NewOrigin) override;
    virtual EHMDTrackingOrigin::Type GetTrackingOrigin() const override;
    virtual FTransform GetTrackingToWorldTransform() const override;
    virtual bool GetFloorToEyeTrackingTransform(FTransform& OutFloorToEye) const override;
    virtual void UpdateTrackingToWorldTransform(const FTransform& TrackingToWorldOverride) override;

    virtual void CalibrateExternalTrackingSource(const FTransform& ExternalTrackingTransform) override;
    virtual void UpdateExternalTrackingPosition(const FTransform& ExternalTrackingTransform) override;
    virtual class IXRLoadingScreen* GetLoadingScreen() override final;

    virtual void GetMotionControllerData(UObject* WorldContext, const EControllerHand Hand, FXRMotionControllerData& MotionControllerData) override;

    (...)

protected:
    TSharedPtr< class FDefaultXRCamera, ESPMode::ThreadSafe > XRCamera;
    FTransform CachedTrackingToWorld;
    FTransform CalibratedOffset;
    mutable class IXRLoadingScreen* LoadingScreen;

    (...)
};

// HeadMountedDisplayBase.h

class HEADMOUNTEDDISPLAY_API FHeadMountedDisplayBase : public FXRTrackingSystemBase, public IHeadMountedDisplay, public IStereoRendering
{
public:
    FHeadMountedDisplayBase(IARSystemSupport* InARImplementation);
    virtual ~FHeadMountedDisplayBase();

    virtual IStereoLayers* GetStereoLayers() override;

    virtual bool GetHMDDistortionEnabled(EShadingPath ShadingPath) const override;
    virtual void OnLateUpdateApplied_RenderThread(FRHICommandListImmediate& RHICmdList, const FTransform& NewRelativeTransform) override;

    virtual void CalculateStereoViewOffset(const enum EStereoscopicPass StereoPassType, FRotator& ViewRotation, const float WorldToMeters, FVector& ViewLocation) override;
    virtual void InitCanvasFromView(FSceneView* InView, UCanvas* Canvas) override;

    virtual bool IsSpectatorScreenActive() const override;

    virtual class ISpectatorScreenController* GetSpectatorScreenController() override;
    virtual class ISpectatorScreenController const* GetSpectatorScreenController() const override;

    virtual FVector2D GetEyeCenterPoint_RenderThread(EStereoscopicPass Eye) const;
    virtual FIntRect GetFullFlatEyeRect_RenderThread(FTexture2DRHIRef EyeTexture) const { return FIntRect(0, 0, 1, 1); }
    virtual void CopyTexture_RenderThread(FRHICommandListImmediate& RHICmdList, FRHITexture2D* SrcTexture, FIntRect SrcRect, FRHITexture2D* DstTexture, FIntRect DstRect, bool bClearBlack, bool bNoAlpha) const {}

    (...)
    
protected:
    mutable TSharedPtr<class FDefaultStereoLayers, ESPMode::ThreadSafe> DefaultStereoLayers;
    TUniquePtr<FDefaultSpectatorScreenController> SpectatorScreenController;

    (...)
};

// OpenXRHMD.h

// OpenXR인터페이스 。
class FOpenXRHMD
    : public FHeadMountedDisplayBase
    , public FXRRenderTargetManager
    , public FSceneViewExtensionBase
    , public FOpenXRAssetManager
    , public TStereoLayerManager<FOpenXRLayer>
{
public:
    virtual bool EnumerateTrackedDevices(TArray<int32>& OutDevices, EXRTrackedDeviceType Type = EXRTrackedDeviceType::Any) override;
        
    virtual bool GetRelativeEyePose(int32 InDeviceId, EStereoscopicPass InEye, FQuat& OutOrientation, FVector& OutPosition) override;
    virtual bool GetIsTracked(int32 DeviceId);
        
    // 페치/가져오기(Fetch)HMD의 현재(Current)。
    virtual bool GetCurrentPose(int32 DeviceId, FQuat& CurrentOrientation, FVector& CurrentPosition) override;
    virtual bool GetPoseForTime(int32 DeviceId, FTimespan Timespan, FQuat& CurrentOrientation, FVector& CurrentPosition, bool& bProvidedLinearVelocity, FVector& LinearVelocity, bool& bProvidedAngularVelocity, FVector& AngularVelocityRadPerSec);
    virtual void SetBaseRotation(const FRotator& BaseRot) override;
    virtual FRotator GetBaseRotation() const override;

    virtual void SetBaseOrientation(const FQuat& BaseOrient) override;
    virtual FQuat GetBaseOrientation() const override;

    virtual void SetTrackingOrigin(EHMDTrackingOrigin::Type NewOrigin) override;
    virtual EHMDTrackingOrigin::Type GetTrackingOrigin() const override;
        
    (...)

public:
    FOpenXRHMD(const FAutoRegister&, XrInstance InInstance, XrSystemId InSystem, TRefCountPtr<FOpenXRRenderBridge>& InRenderBridge, TArray<const char*> InEnabledExtensions, TArray<class IOpenXRExtensionPlugin*> InExtensionPlugins, IARSystemSupport* ARSystemSupport);
    virtual ~FOpenXRHMD();

    // RHI스레드(Thread)의 렌더링(Render)。
    void OnBeginRendering_RHIThread(const FPipelinedFrameState& InFrameState, FXRSwapChainPtr ColorSwapchain, FXRSwapChainPtr DepthSwapchain);
    // RHI스레드(Thread)의 렌더링(Render)。
    void OnFinishRendering_RHIThread();
        
    (...)

private:
    TArray<const char*>        EnabledExtensions;
    TArray<class IOpenXRExtensionPlugin*> ExtensionPlugins;
    XrInstance                Instance;
    XrSystemId                System;

    // 렌더링(Render)
    TRefCountPtr<FOpenXRRenderBridge> RenderBridge;
    // 렌더링(Render)모듈
    IRendererModule*        RendererModule;

    TArray<FHMDViewMesh>    HiddenAreaMeshes;
    TArray<FHMDViewMesh>    VisibleAreaMeshes;
        
    (...)
};

// OpenXRHMD_RenderBridge.h

// OpenXR렌더링(Render)
class FOpenXRRenderBridge : public FXRRenderBridge
{
public:
    virtual void* GetGraphicsBinding() = 0;
    
     // 생성(Create)스왑체인 。
    virtual FXRSwapChainPtr CreateSwapchain(...) = 0;
    FXRSwapChainPtr CreateSwapchain(...);

    // 프레젠트 렌더링(Render)의 。 
    virtual bool Present(int32& InOutSyncInterval) override
    {
        bool bNeedsNativePresent = true;

        if (OpenXRHMD)
        {
            OpenXRHMD->OnFinishRendering_RHIThread();
            bNeedsNativePresent = !OpenXRHMD->IsStandaloneStereoOnlyDevice();
        }

        InOutSyncInterval = 0; // VSync off

        return bNeedsNativePresent;
    }
    
    (...)

private:
    FOpenXRHMD* OpenXRHMD;
};

#ifdef XR_USE_GRAPHICS_API_D3D11
FOpenXRRenderBridge* CreateRenderBridge_D3D11(XrInstance InInstance, XrSystemId InSystem);
#endif
#ifdef XR_USE_GRAPHICS_API_D3D12
FOpenXRRenderBridge* CreateRenderBridge_D3D12(XrInstance InInstance, XrSystemId InSystem);
#endif
#ifdef XR_USE_GRAPHICS_API_OPENGL
FOpenXRRenderBridge* CreateRenderBridge_OpenGL(XrInstance InInstance, XrSystemId InSystem);
#endif
#ifdef XR_USE_GRAPHICS_API_VULKAN
FOpenXRRenderBridge* CreateRenderBridge_Vulkan(XrInstance InInstance, XrSystemId InSystem);

// OpenXRHMD_RenderBridge.cpp

// D3D11의 렌더링(Render)
class FD3D11RenderBridge : public FOpenXRRenderBridge
{
public:
    FD3D11RenderBridge(XrInstance InInstance, XrSystemId InSystem);
    virtual FXRSwapChainPtr CreateSwapchain(...) override final;

    (...)
};

// D3D12의 렌더링(Render)
class FD3D12RenderBridge : public FOpenXRRenderBridge
{
public:
    FD3D12RenderBridge(XrInstance InInstance, XrSystemId InSystem);
    virtual FXRSwapChainPtr CreateSwapchain(...) override final

    (...)
};

// OpenGL의 렌더링(Render)
class FOpenGLRenderBridge : public FOpenXRRenderBridge
{
public:
    FOpenGLRenderBridge(XrInstance InInstance, XrSystemId InSystem);
    virtual FXRSwapChainPtr CreateSwapchain(...) override final

    (...)
};

// Vulkan의 렌더링(Render)
class FVulkanRenderBridge : public FOpenXRRenderBridge
{
public:
    FVulkanRenderBridge(XrInstance InInstance, XrSystemId InSystem);
    virtual FXRSwapChainPtr CreateSwapchain(...) override final

    (...)
};

위에서 볼 수 있듯이 OpenXR에는 주로 FOpenXRARSystem, FOpenXRHMDPlugin, FOpenXRHMD 및 FOpenXRRenderBridge와 같은 상속 트리 유형을 포함하여 다양한 유형이 포함됩니다. 각각의 상속 관계는 다음 UML 다이어그램으로 표현될 수 있습니다.

클래스 다이어그램-v2 IARSystemSupport <|-- FARSystemSupportBase FARSystemSupportBase <|-- FOpenXRARSystem

클래스 FOpenXRARSystem{ FOpenXRHMD* 추적 시스템; }

IHeadMountedDisplayModule &lt;|-- IOpenXRHMDPlugin
IOpenXRHMDPlugin &lt;|-- FOpenXRHMDPlugin
클래스 FOpenXRHMDPlugin{
  XrInstance 인스턴스;
  XrSystemId 시스템;
  IOpenXRExtensionPlugin* ExtensionPlugins;
  FOpenXRRenderBridge* RenderBridge;
}

IXRTrackingSystem <|-- FXRTrackingSystemBase FXRTrackingSystemBase <|-- FHeadMountedDisplayBase IHeadMountedDisplay <|-- FHeadMountedDisplayBase IStereoRendering <|-- FHeadMountedDisplayBase

FHeadMountedDisplayBase <|-- FOpenXRHMD FXRRenderTargetManager <|-- FOpenXRHMD FSceneViewExtensionBase <|-- FOpenXRHMD

클래스 FOpenXRHMD{ XrInstance 인스턴스; XrSystemId 시스템; FOpenXRRenderBridge* RenderBridge; IRendererModule* 렌더러모듈; }

FRHIResource <|-- FRHICustomPresent FRHICustomPresent <|-- FXRRenderBridge FXRRenderBridge <|-- FOpenXRRenderBridge FOpenXRRenderBridge <|-- FD3D11RenderBridge FOpenXRRenderBridge <|-- FD3D12RenderBridge FOpenXRRenderBridge <|-- FOpenGLRenderBridge FOpenXRRenderBridge <|-- FVulkanRenderBridge

연결하세요:

클래스 다이어그램-v2 IARSystemSupport <|-- FARSystemSupportBase FARSystemSupportBase <|-- FOpenXRARSystem

FOpenXRARSystem *-- FOpenXRHMD

IHeadMountedDisplayModule &lt;|-- IOpenXRHMDPlugin
IOpenXRHMDPlugin &lt;|-- FOpenXRHMDPlugin

FOpenXRHMD플러그인 ..> FOpenXRARSystem
FOpenXRHMDPlugin --> FOpenXRRenderBridge
FOpenXRHMD --> FOpenXRRenderBridge

IXRTrackingSystem <|-- FXRTrackingSystemBase FXRTrackingSystemBase <|-- FHeadMountedDisplayBase IHeadMountedDisplay <|-- FHeadMountedDisplayBase IStereoRendering <|-- FHeadMountedDisplayBase

FHeadMountedDisplayBase <|-- FOpenXRHMD

FRHIResource <|-- FRHICustomPresent FRHICustomPresent <|-- FXRRenderBridge FXRRenderBridge <|-- FOpenXRRenderBridge

그렇다면 위의 중요한 유형들은 UE의 메인 루프와 어떤 관련이 있습니까? 대답은 아래와 같습니다.

cpp
// UnrealEngine.cpp

bool UEngine::InitializeHMDDevice()
{
    (...)

    // 페치/가져오기(Fetch)HMD의 모듈 리스트 .
    FName Type = IHeadMountedDisplayModule::GetModularFeatureName();
    IModularFeatures& ModularFeatures = IModularFeatures::Get();
    TArray<IHeadMountedDisplayModule*> HMDModules = ModularFeatures.GetModularFeatureImplementations<IHeadMountedDisplayModule>(Type);

    (...)

    for (auto HMDModuleIt = HMDModules.CreateIterator(); HMDModuleIt; ++HMDModuleIt)
    {
        IHeadMountedDisplayModule* HMDModule = *HMDModuleIt;

        (...)
        
        if(HMDModule->IsHMDConnected())
        {
            // XR모듈 생성(Create)인스턴스 (즉, IXRTrackingSystem인스턴스 ,만약 는 OpenXR,이면 는 FOpenXRHMD), 인스턴스 저장(Save)까지 UEngine의 XRSystem변수 내에서 。
            XRSystem = HMDModule->CreateTrackingSystem();

            if (XRSystem.IsValid())
            {
                HMDModuleSelected = HMDModule;
                break;
            }
        }

        (...)
}

위의 생성 및 초기화 코드는 OpenXR뿐만 아니라 다른 유형의 XR(예: FAppleARKitModule, FGoogleARCoreBaseModule, FGoogleVRHMDPlugin, FOculusHMDModule, FSteamVRPlugin 등)에도 유효합니다.

15.3.2.4 오큘러스 VR

Oculus의 XR 플러그인 소스 코드는 https://github.com/Oculus-VR/UnrealEngine/tree/4.27입니다. 물론 UE 4.27의 공식 버전에는 Oculus 플러그인 코드가 내장되어 있으며 디렉터리는 Engine\Plugins\Runtime\Oculus\입니다. UE의 일부 중요한 XR 유형은 플러그인에서 상속되거나 구현됩니다.

cpp
// IOculusHMDModule.h

// 모듈 의 인터페이스 。,인터페이스 내에서 의 단계 모듈 。
class IOculusHMDModule : public IHeadMountedDisplayModule
{
public:
    static inline IOculusHMDModule& Get();
    static inline bool IsAvailable();

    // 페치/가져오기(Fetch)HMD의 현재(Current) 및 위치 。만약 위치 을(를) 활용하여 ,DevicePosition로 벡터 .
    virtual void GetPose(FRotator& DeviceRotation, FVector& DevicePosition, FVector& NeckPosition, bool bUseOrienationForPlayerCamera = false, bool bUsePositionForPlayerCamera = false, const FVector PositionScale = FVector::ZeroVector) = 0;
    // 데이터 。만약 HMD지원 파라미터 ,이면 설정(Set)로 。
    virtual void GetRawSensorData(FVector& AngularAcceleration, FVector& LinearAcceleration, FVector& AngularVelocity, FVector& LinearVelocity, float& TimeInSeconds) = 0;

    // 반환(Return)을(를) 활용하여 구성(Configuration)。
    virtual bool GetUserProfile(struct FHmdUserProfile& Profile)=0;
    virtual void SetBaseRotationAndBaseOffsetInMeters(FRotator Rotation, FVector BaseOffsetInMeters, EOrientPositionSelector::Type Options) = 0;
    virtual void GetBaseRotationAndBaseOffsetInMeters(FRotator& OutRotation, FVector& OutBaseOffsetInMeters) = 0;
    virtual void SetBaseRotationAndPositionOffset(FRotator BaseRot, FVector PosOffset, EOrientPositionSelector::Type Options) = 0;
    virtual void GetBaseRotationAndPositionOffset(FRotator& OutRot, FVector& OutPosOffset) = 0;
    virtual class IStereoLayers* GetStereoLayers() = 0;
};

일반적으로 구조는 OpenXR과 유사하므로 이 기사에서는 이에 대해 자세히 설명하지 않습니다. 관심 있는 학생은 플러그인 디렉토리로 이동하여 소스 코드를 학습할 수 있습니다. 자세한 내용은 다음을 참조하세요.

  • Quest용 언리얼 엔진 앱 개발 시작하기

  • 언리얼 엔진 기본을 위한 Oculus 통합

  • Oculus 개발 중

15.3.3 UE VR 최적화

15.3.3.1 프레임 속도 최적화

대부분의 VR 애플리케이션은 자체 프로세스를 실행하여 VR 프레임 속도를 제어합니다. 따라서 VR 애플리케이션에 영향을 미치는 여러 일반 프로젝트 설정은 Unreal Engine 4에서 비활성화되어야 합니다. Unreal Engine의 일반 프레임 속도 설정을 비활성화하려면 다음 단계를 설정하십시오:

  • 편집기 메인 메뉴에서 편집->프로젝트 설정을 선택하여 프로젝트 설정 창을 엽니다.

  • 프로젝트 설정 창의 엔진 섹션에서 일반 설정을 선택하세요.

  • 프레임 속도 섹션에서:

부드러운 프레임 속도를 비활성화합니다.

  • 고정 프레임 속도 사용을 비활성화합니다.

  • 사용자 정의 시간 단계를 없음으로 설정합니다.

15.3.3.2 경험 최적화

시뮬레이션 멀미는 몰입형 경험 중에 사용자에게 영향을 미치는 멀미의 한 형태입니다. 다음 표에서는 VR에서 사용자가 경험하는 불편함을 제한할 수 있는 모범 사례를 설명합니다.

  • 프레임 속도 유지: 프레임 속도가 낮으면 시뮬레이션 멀미가 발생할 수 있습니다. 프로젝트를 최대한 최적화하면 사용자 경험이 향상됩니다. Oculus Quest 1 및 2, HTC Vive, Valve Index, PSVR 및 HoloLens 2의 목표 프레임 속도는 90이고 ARKit 및 ARCore의 목표 프레임 속도는 60입니다.

  • 사용자 테스트: 시뮬레이션 증후군을 피하기 위해 VR 애플리케이션에서 경험하는 불편함을 모니터링하기 위해 다양한 사용자를 대상으로 테스트합니다.

  • 사용자가 카메라를 제어하도록 하세요: 플레이어가 카메라 움직임을 제어할 수 없도록 하는 시네마틱 카메라와 기타 디자인은 몰입형 경험을 불편하게 만드는 주요 원인입니다. 머리 흔들림, 카메라 흔들림 등의 카메라 효과는 사용자가 제어할 수 없을 경우 불편함을 유발할 수 있으므로 최대한 피해야 합니다.

  • FOV는 장치와 일치해야 합니다. FOV 값은 장치의 SDK 및 내부 구성을 통해 설정되며 헤드셋 및 렌즈의 물리적 형상과 일치합니다. 따라서 언리얼 엔진에서는 FOV를 변경할 수 없으며, 사용자가 수정할 수도 없습니다. FOV 값이 변경되면 고개를 돌릴 때 세계 장면이 왜곡되어 불편함을 느끼게 됩니다.

  • 더 어두운 조명과 색상을 사용하고 번짐을 방지하세요. VR 요소를 디자인할 때 사용하는 조명과 색상은 평소보다 더 어두워야 합니다. VR에서는 강렬하고 생생한 조명으로 인해 사용자가 시뮬레이션 증후군을 더 빨리 경험할 수 있습니다. 사용자의 불편함을 방지하고 화면의 밝은 부분과 어두운 부분 사이에 번짐을 방지하려면 차가운 톤과 어두운 조명을 사용하십시오.

  • 이동 속도는 변하지 않아야 합니다. 사용자는 점진적으로 최고 속도로 증가하는 것이 아니라 최고 속도로 이동을 시작해야 합니다.

  • 사용자가 보는 것에 큰 영향을 미치는 포스트 프로세스 파이프라인를 사용하지 마세요. 사용자에게 불편함을 주지 않으려면 뎁스 오브 필드(DoF), 모션 블러와 같은 포스트 프로세스 파이프라인를 사용하지 마세요.

15.3.3.3 기타 최적화

VR에서 다음과 같은 문제가 있는 렌더링 기술을 사용하지 마십시오.

  • 스크린 공간 반사(SSR): SSR은 VR에서 작동할 수 있지만 생성되는 반사는 실제 반사와 일치하지 않을 수 있습니다. SSR 외에도 가격이 저렴하고 반사 매칭 문제가 덜 발생하는 반사 프로브를 사용할 수도 있습니다.

  • 화면 공간 전역 조명: HMD에서는 화면 공간 트릭으로 인해 두 눈 사이에 표시되는 내용에 차이가 발생할 수 있습니다. 이러한 차이는 사용자에게 불편함을 줄 수 있습니다.

  • 레이 트레이싱: 현재 VR 애플리케이션에 사용되는 레이 트레이싱은 편안한 VR 경험을 제공하는 데 필요한 해상도와 프레임 속도를 유지할 수 없습니다.

  • 2D 사용자 인터페이스 또는 빌보드 스프라이트: 2D 사용자 인터페이스 또는 빌보드 스프라이트는 입체 환경에서 제대로 작동하지 않고 대신 3D 세계 장면에서 제어 구성 요소를 사용할 수 있기 때문에 입체 렌더링을 지원하지 않습니다.

  • 노멀 맵: 노멀 맵이나 객체를 VR에서 볼 때 노멀 맵은 양안 디스플레이나 동적 시차를 고려하지 않기 때문에 이전과 동일한 효과를 내지 못한다는 것을 알 수 있습니다. 따라서 VR에서 볼 때 노멀 맵은 평평한 경우가 많습니다. 그러나 이것이 노멀 맵을 사용해서는 안 된다거나 사용할 필요가 없다는 의미는 아니며, 노멀 맵에 전달된 데이터가 지오메트리로 표현될 수 있는지 여부를 좀 더 주의 깊게 평가하면 됩니다. 대신 시차 맵을 사용할 수 있습니다. 시차 맵은 노멀 맵이 고려되지 않는 깊이 단서를 고려하는 노멀 맵의 업그레이드 버전입니다. 시차 지도 셰이더는 깊이 정보를 더 잘 표시하여 객체를 더 자세히 보이게 할 수 있습니다. 어떤 각도에서 보든 시차 맵은 항상 자체적으로 수정되어 사용자의 관점에서 올바른 깊이 정보를 표시합니다. 시차 매핑은 조약돌 포장 도로와 미세한 세부 묘사가 있는 표면에 가장 적합합니다.

UE를 위한 기타 VR 최적화:

  • 동적 조명과 그림자는 사용되지 않습니다.

  • 반투명도를 광범위하게 사용하지 마십시오.

  • 표시되는 배치의 인스턴스입니다. 인스턴스화된 그룹의 요소가 표시되면 전체 그룹이 그려집니다.

  • 모든 콘텐츠에 대해 LOD를 설정합니다.

  • 재료 복잡성을 단순화하고 각 개체의 재료 수를 줄입니다.

  • 베이킹 내용은 중요도가 낮습니다.

  • 플레이어를 담을 수 있는 큰 형상을 사용하지 않습니다.

  • 가능할 때마다 미리 계산된 가시 볼륨을 사용하십시오.

  • VR 인스턴스화된 스테레오/모바일 VR 멀티뷰를 활성화합니다.

  • **후처리를 비활성화합니다.**VR의 높은 렌더링 요구 사항으로 인해 기본적으로 켜져 있는 많은 고급 포스트 프로세스 기능을 비활성화해야 합니다. 그렇지 않으면 프로젝트에 심각한 성능 문제가 발생할 수 있습니다. 프로젝트 설정을 완료하려면 다음 단계를 수행하세요.

레벨에 포스트 프로세싱(PP) 볼륨을 추가합니다.

  • PP 볼륨을 선택하고 포스트 프로세스 볼륨 섹션에서 언바운드 옵션을 활성화하면 PP 볼륨의 설정이 전체 레벨에 적용됩니다.

  • 포스트 프로세스 볼륨설정을 열고 각 섹션으로 이동하여 활성화된 PP 설정을 비활성화합니다. 먼저 속성을 클릭한 다음 기본값(일반적으로 1.0)을 0으로 변경하여 기능을 비활성화합니다.

이렇게 하면 각 섹션을 클릭하고 모든 속성을 0으로 설정할 필요가 없습니다. Lens Flares, Screen Space 반사, Temporal AA, Screen Space Ambient Occlusion(SSAO), Bloom 및 성능에 영향을 미칠 수 있는 기타 기능과 같은 비용이 많이 드는 기능을 먼저 비활성화할 수 있습니다.

  • **플랫폼에 적합한 메모리 버킷을 설정하세요.**사용자는 다양한 메모리 용량을 갖춘 다양한 플랫폼에서 UE4 프로젝트를 실행하는 방법을 지정하고 메모리 버킷을 추가하여 사용할 옵션을 지정할 수 있습니다. 이 기능을 추가하려면 먼저 프로젝트의 Engine.ini 파일을 텍스트 편집기에서 열어야 합니다(Android/AndroidEngine.ini, IOS/IOSEngine.ini 또는 PlatformNameEngine.ini 파일을 사용하여 플랫폼 기반으로 설정). 사용의 편의를 위해 몇 가지 기본 설정이 있습니다. 다음은 AndroidEngine.ini에 대한 샘플 매개변수 설정입니다.
cpp
[PlatformMemoryBuckets]
LargestMemoryBucket_MinGB=8
LargerMemoryBucket_MinGB=6
DefaultMemoryBucket_MinGB=4
SmallerMemoryBucket_MinGB=3
 ; for now, we require 3gb
SmallestMemoryBucket_MinGB=3

어떤 메모리 버킷이 어떤 장치 설정과 연관되어 있는지는 DeviceProfiles.ini에서 지정할 수 있습니다. 예를 들어 텍스처 스트리밍 풀에서 사용하는 메모리 양을 조정하려면 DeviceProfiles.ini 파일에 다음 정보를 추가합니다.

cpp
[Mobile DeviceProfile]
+CVars_Default=r.Streaming.PoolSize=180
+CVars_Smaller=r.Streaming.PoolSize=150
+CVars_Smallest=r.Streaming.PoolSize=70
+CVars_Tiniest=r.Streaming.PoolSize=16

"Mobile"은 장치 설명이 추가될 플랫폼 이름으로 대체될 수 있습니다. 메모리 버킷을 사용하면 사용할 렌더링 설정을 지정할 수도 있습니다. 다음 예에서는 Scene Settings를 사용하여 텍스처의 TextureLODGroup이 설정되었습니다. UE4가 가장 작은 메모리 버킷을 사용하는 장치를 감지하면 MaxLODSize를 1024에서 256으로 조정하여 LOD 그룹이 "Scene"으로 설정된 텍스처에 필요한 메모리를 줄입니다.

cpp
[Mobile DeviceProfile]
+TextureLODGroups=(Group=TEXTUREGROUP_World, MaxLODSize=1024, OptionalMaxLODSize=1024, OptionalLODBias=1, MaxLODSize_Smaller=1024, MaxLODSize_Smallest=1024, MaxLODSize_Tiniest=256, LODBias=0, LODBias_Smaller=0, LODBias_Smallest=1, MinMagFilter=aniso, MipFilter=point)
  • **적절한 스레드 동기화 방법을 선택하십시오.**UE는 다음 스레드 동기화 방법을 지원합니다.

r.GTSyncType 0: 게임 스레드가 렌더링 스레드와 동기화됩니다(이전 동작, 기본값).

  • r.GTSyncType 1: 게임 스레드가 RHI 스레드와 동기화됩니다(병렬 렌더링 전 UE4와 동일).

  • r.GTSyncType 2: 게임 스레드가 스왑 체인과 동기화되어 +/- 오프셋을 밀리초 단위로 표시합니다. 이 모드에서 동기화를 달성하기 위해 엔진은 Present()를 호출할 때 드라이버에 전달된 인덱스로 표시된 프레임을 추적합니다. 이 인덱스는 플랫폼 프레임 전환 통계에서 검색되며 각 프레임 전환의 정확한 시간을 나타냅니다. 엔진 사용자는 이 값을 사용하여 다음 프레임이 언제 반전되어야 하는지 예측하고, 그 시간을 기준으로 다음 게임 스레드 프레임을 시작합니다.

또한 rhi.SyncSlackMS는 예측된 다음 수직 동기화 시간에 적용할 오프셋을 결정합니다. 이 값을 낮추면 입력 지연이 줄어들지만 엔진 파이프라인이 단축되고 끊김 현상으로 인해 프레임이 저하될 가능성이 높아집니다. 마찬가지로, 이 값을 늘리면 엔진 파이프라인이 길어져 게임이 끊김에 대한 탄력성을 갖게 되지만 입력 지연이 늘어납니다. 일반적으로 이 새로운 프레임 동기화 시스템을 사용하는 게임은 허용 가능한 프레임 속도를 유지하면서 rhi.SyncSlackMS를 최대한 줄여야 합니다. 예를 들어 업데이트 속도가 30Hz인 게임의 CVar 설정은 다음과 같습니다.

-rhi.SyncInterval 2

  • r.GTSyncType 2

  • r.OneFrameThreadLag 1

  • r.Vsync 1

  • rhi.SyncSlackMS 0

가장 좋은 입력 지연은 약 66ms(30Hz 프레임 2개)입니다. rhi.SyncSlackMS를 10으로 늘리면 최적의 입력 대기 시간은 약 76ms입니다. r.GTSyncType 2는 업데이트 속도가 60Hz인 게임에서도 작동하지만(예: rhi.SyncInterval이 1로 설정됨) 30hz에 비해 프레임 속도가 두 배로 늘어나 입력 지연이 절반으로 줄어들기 때문에 이 설정을 사용하는 이점은 눈에 띄지 않습니다.

  • 지연 시간을 줄이기 위해 렌더링 스레드에서 HMD 자세를 다시 획득합니다.

위: 시뮬레이션 시작 시 기본적으로 포즈를 쿼리하고 해당 포즈를 렌더링에 사용하면 헤드셋이 "느리게" 또는 느릴 수 있습니다. 이제 장치 위치 쿼리와 결과 프레임 표시 사이에 두 개의 프레임이 있을 수 있기 때문입니다. 하단: 렌더링하기 전에 포즈를 다시 쿼리하고 업데이트된 포즈를 사용하여 렌더링된 변환을 계산하면 이 문제가 해결됩니다.

  • 기타: VSync 켜기, DynRes 켜기, 컴포지터 올바르게 사용하기 등

UE의 XR 최적화에 대한 자세한 내용은 다음을 참조하세요.

  • 언리얼 엔진 XR(4.27)

  • VR 모범 사례

  • VR 성능 및 프로파일링

  • VR 성능 특징

  • VR 성능 도구

-장치 프로필

  • 낮은 대기 시간 프레임 동기화

  • 스테레오 렌더링 프로파일링

  • 12.6.5 XR 최적화

  • Quest용 언리얼 엔진 앱 개발 시작하기

  • 언리얼 고급 렌더링

15.3.4 UE VR 성능 감지

UE4에서는 게임 내 전체 데이터를 다음과 같은 방법으로 얻을 수 있습니다.

상태 단위: 전체 게임 스레드, 그리기 스레드, GPU 시간은 물론 전체 프레임 시간도 표시합니다. 이는 전체 총 프레임 시간이 이상적인 범위인 게임 스레드 시간 내에 있는지에 대한 정보를 수집하는 데 가장 적합하지만 드로 스레드 및 GPU 시간을 수집하는 데는 적합하지 않습니다.

startfpschart / stopfpschart: 90Hz 이상에서 소요된 시간의 백분율을 알아야 하는 경우 이 명령을 실행하십시오. 시작과 끝 사이의 기간 동안 데이터를 캡처 및 집계하고 버킷 프레임 속도 정보가 포함된 파일을 덤프합니다. 게임에서는 실제로 90Hz인데도 90Hz보다 약간 낮은 것으로 보고되는 경우가 있습니다. 프레임 속도에 소요되는 실제 시간을 결정하려면 80+ 버킷을 확인하는 것이 좋습니다.

stat GPU: GPU 분석 도구에서 제공하는 데이터와 유사하게 플레이어는 게임 내에서 이러한 데이터를 관찰하고 모니터링할 수 있으므로 GPU 작업의 오버헤드를 빠르게 확인하는 데 적합합니다.

라이브 데이터는 게임 중에 데이터를 수집해야 하는 경우(예: 차트에 사용하기 위해) 특히 유용합니다. 실시간 디스플레이를 사용하여 콘솔 변수 또는 정밀도 설정에서 활성화된 기능을 분석하거나 편집기에서 최적화 결과를 즉시 확인할 수 있습니다. 데이터는 다음과 같이 코드에서 부동 소수점 카운터로 선언됩니다. DECLARE_FLOAT_COUNTER_STAT(TEXT("Postprocessing"), Stat_GPU_Postprocessing, STATGROUP_GPU); 렌더링 스레드 코드 블록은 SCOPED_GPU_STAT 매크로와 함께 계측될 수 있으며 작동 원리는 SCOPED_DRAW_EVENT와 유사합니다. 예: SCOPED_GPU_STAT(RHICmdList, Stat_GPU_Postprocessing); 그리기 이벤트와 달리 GPU 데이터는 누적됩니다. 동일한 데이터에 대해 여러 항목을 추가할 수 있으며 집계됩니다. 표시용으로 표시된 콘텐츠는 포함된 [미계정] 데이터에 포함되어야 합니다. 이 숫자가 높으면 명시적 데이터에 포함되지 않은 콘텐츠가 있으며 이를 추적하려면 더 많은 매크로를 추가해야 함을 의미합니다.

또한 Oculus와 SteamVR에는 성능을 이해하기 위한 타사 도구가 있습니다. 실제 프레임 시간과 합성기 오버헤드를 보거나 RenderDoc과 같은 타사 디버깅 소프트웨어를 사용하려면 이러한 도구를 사용하는 것이 좋습니다.

Oculus HMD에는 성능 분석 도구가 내장되어 있습니다.

15.4 이 기사 요약

이 글에서는 주로 XR의 다양한 렌더링 기술과 UE의 XR 통합의 렌더링 프로세스 및 주요 알고리즘을 설명하므로 독자가 이 모듈에 대한 전반적인 이해를 가질 수 있습니다. 보다 기술적인 세부 사항과 원리에 관해서는 독자들이 UE 소스 코드를 직접 연구해야 합니다. 저는 비교적 완벽하고 포괄적이며 심층적인 XR 강좌와 책을 추천합니다.

  • 스탠포드 과정: EE 267

  • 버클리 과정: CS 184

  • 가상 현실(스티븐 M. 라발레)

특별 지침

  • 모든 참고문헌의 저자에게 감사드립니다. 일부 사진은 참고 자료와 인터넷에서 가져온 것이므로 삭제되었습니다.

  • 이 시리즈의 기사는 저자가 직접 작성한 것이며 블로그에만 게시됩니다. 이 글의 링크를 공유하셔도 좋지만 무단 전재는 허용되지 않습니다!

  • 계속되는 일련의 기사. 전체 목록을 보려면 콘텐츠 개요를 클릭하세요.

  • 계속되는 일련의 기사. 전체 목록을 보려면 콘텐츠 개요를 클릭하세요.

  • 계속되는 일련의 기사. 전체 목록을 보려면 콘텐츠 개요를 클릭하세요.








📚 참고 자료 및 공식 링크 (References)


🔗 연관 지식 베이스 (Wiki & Tools)

Based on the legendary technical series by Timlly (0向往0 / 毛星云)