OpenVR
싱글 패스 인스턴스화 렌더링

싱글 패스 스테레오 렌더링(더블 와이드 렌더링)

싱글 패스 스테레오 렌더링 은 PC 및 Playstation 4 기반 VR 애플리케이션의 기능입니다. 이 기능은 왼쪽 및 오른쪽 눈 이미지를 너비가 한쪽 눈 텍스처의 두 배인 패킹된 단일 렌더 텍스처로 동시에 렌더링합니다. Unity는 Renderer 컴포넌트가 포함된 각 게임 오브젝트에 대해 2개의 드로우 콜을 사용하여 씬을 두 번 렌더링합니다. 하지만 왼쪽 및 오른쪽 눈 모두에 대해 렌더링할 때는 씬 그래프를 통해서만 반복합니다. 싱글 패스 스테레오 렌더링을 진행하는 동안 두 눈은 컬링 및 그림자 계산에 필요한 작업을 공유합니다. 또한 GPU가 각 게임 오브젝트를 교차하는 방식으로 렌더링(두 눈 간에 오브젝트 렌더링을 교차함)하기 때문에 그래픽스 커맨드 상태 변경 스위치가 더 적습니다.

싱글 패스 스테레오 렌더링을 사용하면 GPU가 양쪽 눈을 위한 컬링을 공유합니다. GPU는 컬링을 위한 목적으로 한 번만 씬의 모든 게임 오브젝트를 통해 반복하며, 그런 다음 컬링 프로세스에서 살아남은 게임 오브젝트를 렌더링합니다.

아래의 비교 이미지에서 일반적인 VR 렌더링과 싱글 패스 스테레오 렌더링의 차이를 볼 수 있습니다.

일반적인 VR 렌더링

왼쪽은 왼쪽 눈 이미지, 오른쪽은 오른쪽 눈 이미지입니다.
왼쪽은 왼쪽 눈 이미지, 오른쪽은 오른쪽 눈 이미지입니다.

싱글 패스 스테레오 VR 렌더링

왼쪽 눈과 오른쪽 눈 이미지를 함께 패킹합니다.
왼쪽 눈과 오른쪽 눈 이미지를 함께 패킹합니다.

이 기능을 활성화하려면 플레이어 설정(메뉴: Edit > Project Settings 로 이동한 다음 Player 카테고리 선택)을 엽니다. 그런 다음 XR Settings 패널로 이동하여 Virtual Reality Supported 체크박스가 선택되어 있는지 확인하고, Stereo Rendering Method 드롭다운 메뉴에서 Single Pass 옵션을 선택합니다.

플레이어의 XR 설정 패널에서 싱글 패스 렌더링 선택
플레이어의 XR 설정 패널에서 싱글 패스 렌더링 선택

Unity 빌트인 렌더링 기능과 스탠다드 에셋은 모두 이 기능을 지원합니다. 그러나 커스텀 빌드한 셰이더와 에셋 스토어에서 다운로드한 셰이더는 싱글 패스 스테레오 렌더링 지원을 추가하려면 수정이 필요할 수 있습니다(예를 들어, 패킹된 렌더 텍스처의 절반에 액세스하려면 스크린 공간을 조정하고 상쇄해야 할 수 있음).

셰이더에 싱글 패스 스테레오 렌더링 지원 추가

UnityCG.cginc의 기존 헬퍼 메서드는 싱글 패스 스테레오 렌더링을 지원합니다. 사용 중인 애플리케이션이 XR이든 아니든, 버텍스에 대한 변환 작업은 여전히 수행해야 합니다. 예를 들어, 어떤 타입의 애플리케이션을 만들 때 버텍스는 모델 공간에서 버텍스 셰이더 단계에 들어가고 클립 공간에서 나옵니다. 버텍스 셰이더는 클립 공간 버텍스 좌표를 출력해야 합니다. 셰이더의 영향을 받는 버텍스들은 대개 버텍스 셰이더가 클립 공간으로 변환하기 전에 모델 공간에서 시작합니다. 하지만 이러한 버텍스가 클립 공간에 도착하도록 하기 위해 버텍스 셰이더는 버텍스를 월드 공간으로 먼저 변환한 후 뷰포트 공간으로 변환합니다.

XR의 경우 여러 개의 뷰 매트릭스가 제공되어, 왼쪽 눈과 오른쪽 눈 모두에 하나씩 사용할 수 있습니다. 빌트인 UnityWorldToClipPos 메서드를 사용하면 Unity가 계산에 여러 개의 뷰 매트릭스가 필요한지 여부를 고려합니다. UnityWorldToClipPos 메서드를 사용하면 애플리케이션이 실행되는 플랫폼에 관계없이 셰이더가 변환 계산을 자동으로 수행합니다.

UnityCG.cginc에는 양안 셰이더 작성을 지원하는 다음과 같은 헬퍼 메서드도 있습니다.

프로퍼티 Parameters Description
UnityStereoScreenSpaceUVAdjust(uv, sb) uv: UV 텍스처 좌표입니다. 일반 UV의 경우 float2이고 UV 두 개로 패킹된 페어의 경우 float4입니다.
sb: 셰이더가 UV에 적용하는 2D 스케일과 2D 바이어스가 들어 있는 float4로, xy에는 스케일이 있고 zw에는 바이어스가 있습니다.
UNITY_SINGLE_PASS_STEREO를 정의하면 sb의 스케일과 바이어스를 uv의 텍스처 좌표에 적용한 결과를 반환합니다. 그렇지 않은 경우에는 텍스처 좌표가 수정되지 않은 상태로 반환됩니다. 싱글 패스 스테레오 렌더링 모드에서 각 눈의 스케일 및 바이어스를 적용하는 데 주로 사용됩니다.
UnityStereoTransformScreenSpaceTex(uv) uv: UV 텍스처 좌표입니다. 일반 UV의 경우 float2이고 UV 두 개로 패킹된 페어의 경우 float4입니다. UNITY_SINGLE_PASS_STEREO를 정의하면 현재 눈의 스케일과 바이어스를 uv의 텍스처 좌표에 적용한 결과를 반환합니다. 그렇지 않은 경우에는 텍스처 좌표가 변경되지 않은 상태로 반환됩니다.
UnityStereoClamp(uv, sb) uv: UV 텍스처 좌표입니다. 일반 UV의 경우 float2이고 UV 두 개로 패킹된 페어의 경우 float4입니다.
sb: 셰이더가 UV에 적용하는 2D 스케일과 2D 바이어스가 들어 있는 float4로, xy에는 스케일이 있고 zw에는 바이어스가 있습니다.
UNITY_SINGLE_PASS_STEREO를 정의하면 sb가 제공한 너비와 바이어스로 클램핑된 x값을 uv의 텍스처 좌표에 적용한 결과를 반환합니다. 그렇지 않은 경우에는 텍스처 좌표가 변경되지 않은 상태로 반환됩니다. 싱글 패스 스테레오 렌더링 모드에서 눈 사이의 컬러 번짐을 피하기 위해 눈마다 클램핑을 적용할 때 주로 사용됩니다.

셰이더는 상수 인빌트 변수 ‘unity_StereoEyeIndex’를 노출하여 Unity가 눈마다 계산을 수행하도록 해줍니다. unity_StereoEyeIndex 값은 왼쪽 눈 렌더링의 경우에는 0이고 오른쪽 눈 렌더링의 경우에는 1입니다.

다음은 UnityCG.cginc의 예제로, unity_StereoEyeIndex를 사용하여 스크린 공간 좌표를 수정하는 방법을 보여줍니다.

float2 TransformStereoScreenSpaceTex(float2 uv, float w)
{
    float4 scaleOffset = unity_StereoScaleOffset[unity_StereoEyeIndex];
    return uv.xy * scaleOffset.xy + scaleOffset.zw * w;
}

대부분의 경우 셰이더를 수정할 필요가 없습니다. 그러나 싱글 패스 스테레오 렌더링을 위해 단안 텍스처를 샘플링해야 하는 상황이 있습니다. 예를 들어, 전체화면 필름 그레인이나 노이즈 이펙트를 생성할 때 소스 이미지는 양안 이미지로 패킹되기보다는 양쪽 눈에 대해 동일해야 합니다. 이 경우 ComputeScreenPos() 대신 ComputeNonStereoScreenPos()를 사용해서 소스 텍스처 전체에서 위치를 계산할 수 있습니다.

포스트 프로세싱 효과

포스트 프로세싱 효과를 사용하려면 싱글 패스 스테레오 렌더링을 지원하기 위한 추가 작업이 필요합니다. 각 포스트 프로세싱 효과는 왼쪽 눈과 오른쪽 눈 이미지가 패킹된 렌더 텍스처에서 수행하지만 포스트 프로세싱 중에 실행되는 모든 드로우 커맨드는 왼쪽 눈 대상 렌더 텍스처 절반 한 번, 오른쪽 눈 절반 한 번으로 총 2번 적용됩니다.

포스트 프로세싱 효과는 싱글 패스 스테레오 렌더링을 자동으로 감지하지 않으므로, 패킹된 스테레오 렌더 텍스처를 조정해서 현재 렌더링되는 눈에서만 읽을 수 있도록 해야 합니다. 포스트 프로세싱 효과 렌더링 방법에 따라서 두 가지 방법이 있습니다.

위에서 언급한 조정이 없다면 각 드로우 커맨드는 왼쪽 눈과 오른쪽 눈 시야를 포함한 소스 렌더 텍스처 전체를 읽으며 렌더 텍스처 결과물에 왼쪽 눈과 오른쪽 눈 이미지 페어 전체를 출력해서 결과에 각 눈의 잘못된 소스 이미지가 중복됩니다.

이는 Graphics.Blit 또는 텍스처 맵이 포함된 전체화면 폴리곤을 사용하여 각 포스트 프로세싱 효과를 그릴 때 발생합니다. 두 메서드는 체인의 이전 포스트 프로세싱 효과 결과물 전체를 참조합니다. 패킹된 스테레오 렌더 텍스처의 영역을 참조하면 관련된 절반만이 아닌 패킹된 렌더 텍스처 전체를 참조합니다.

Graphics.Blit()

Blit()로 렌더링된 포스트 프로세싱 효과는 패킹된 스테레오 렌더 텍스처의 올바른 부분을 자동으로 참조하지 않으며 기본적으로 텍스처 전체를 참조합니다. 이는 포스트 프로세싱 효과를 양쪽 눈으로 잘못 늘립니다.

Blit()를 사용하는 싱글 패스 스테레오 렌더링은 셰이더의 텍스처 샘플러에 추가적으로 자동 계산된 변수를 사용해, 그려지는 눈에 따라 패킹된 스테레오 렌더 텍스처의 올바른 절반을 참조합니다. 변수에는 타겟 좌표를 올바른 위치로 변환해 주는 스케일과 오프셋 값이 있습니다.

이 변수에 액세스하려면 샘플러와 같은 이름의 셰이더에 half4를 선언하고 suffix _ST를 추가합니다(아래의 코드 예제 참조). UV 좌표를 조정하려면 _ST 변수를 scaleAndOffset으로 전달하고 UnityStereoScreenSpaceUVAdjust(uv, scaleAndOffset)를 사용합니다. 이 메서드는 싱글 패스 스테레오가 아닌 빌드에서 어떠한 컴파일 결과도 없으며 이로 인해 이 모드를 지원하기 위해 수정된 셰이더는 싱글 패스 스테레오가 아닌 빌드와도 호환됩니다.

다음 예제에서 싱글 패스 스테레오 렌더링을 지원하기 위해 프래그먼트 셰이더 코드에 어떤 요소를 변경해야 하는지 확인할 수 있습니다.

스테레오 렌더링이 없는 경우:

uniform sampler2D _MainTex;

fixed4 frag (v2f_img i) : SV_Target
{   
    fixed4 myTex = tex2D(_MainTex, i.uv);
    ...
}

스테레오 렌더링이 있는 경우:

uniform sampler2D _MainTex;
half4 _MainTex_ST;

fixed4 frag (v2f_img i) : SV_Target
{   
    fixed4 myTex = tex2D(_MainTex, UnityStereoScreenSpaceUVAdjust(i.uv, _MainTex_ST));
    ...
}

메시 기반 드로잉

메시를 사용해 렌더링된 포스트 프로세싱 효과는(예를 들어, 즉시 모드에서 낮은 레벨의 그래픽스 API를 사용해 사각형을 그릴 때) 각 눈을 렌더링할 때 타겟 텍스처의 UV 좌표를 조정해야 합니다. 이런 상황에서 좌표를 조정하려면 UnityStereoTransformScreenSpaceTex(uv)를 사용합니다. 이 메서드는 싱글 패스 스테레오 렌더링 모드에서 패킹된 스테레오 렌더 텍스처를 올바르게 조정하며, 싱글 패스 스테레오 렌더링 모드를 비활성화한 경우 패킹되지 않은 렌더 텍스처에 대해 자동으로 컴파일합니다. 그러나 동일한 모드에서 패킹 및 비패킹 렌더 텍스처에 대해 셰이더를 사용하려면 분리된 두 개의 셰이더가 필요합니다.

스크린 공간 효과

스크린 공간 효과는 프리 렌더 이미지 위에 그려지는 시각적 효과입니다. 스크린 공간 효과의 예제에는 앰비언트 오클루전, 피사계심도블룸이 있습니다.

예를 들어, 이미지를 스크린 위에 그려야하는 스크린 공간 효과(스크린에 먼지를 그려야 할 때 등)를 상상해 보십시오. 출력 디스플레이 전체에 효과를 적용해 양쪽 눈에 먼지 이미지를 늘리는 대신 한쪽 눈에 한 번씩 두 번을 적용해야 합니다. 이런 경우에는 팩한 렌더 텍스처 전체를 참조하는 텍스처 좌표를 사용해서 각 눈을 참조하는 좌표로 변환해야 합니다.

다음 코드 예제는 입력 텍스처(_Detail 이라고 함)를 8x6번 출력 이미지에 반복하는 표면 셰이더를 보여줍니다. 두 번째 예제에서 셰이더는 싱글 패스 스테레오 모드에서 대상 좌표를 변환하여 현재 렌더링 중인 눈을 나타내는 결과 텍스처의 일부를 참조하도록 만듭니다.

예제 1: 싱글 패스 스테레오를 지원하지 않는 Detail 텍스처

void surf(Input IN, inout SurfaceOutput o) 
{
    o.Albedo = tex2D(_MainTex, IN.uv_MainTex).rgb;
    float2 screenUV = IN.screenPos.xy / IN.screenPos.w;
    screenUV *= float2(8,6);
    o.Albedo *= tex2D(_Detail, screenUV).rgb * 2;
}

예제 2: 싱글 패스 스테레오를 지원하는 Detail 텍스처

void surf(Input IN, inout SurfaceOutput o) 
{
    o.Albedo = tex2D(_MainTex, IN.uv_MainTex).rgb;
    float2 screenUV = IN.screenPos.xy / IN.screenPos.w;
    #if UNITY_SINGLE_PASS_STEREO
    // If Single-Pass Stereo mode is active, transform the
    // coordinates to get the correct output UV for the current eye.
    float4 scaleOffset = unity_StereoScaleOffset[unity_StereoEyeIndex];
    screenUV = (screenUV - scaleOffset.zw) / scaleOffset.xy;
    #endif
    screenUV *= float2(8,6);
    o.Albedo *= tex2D(_Detail, screenUV).rgb * 2;
}

  • 2018–08–16 일부 편집 리뷰를 거쳐 페이지 게시됨
  • Unity 2017.3의 새로운 기능 NewIn20173
OpenVR
싱글 패스 인스턴스화 렌더링