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

싱글 패스 스테레오 렌더링(Single-Pass Stereo rendering)

싱글 패스 스테레오 렌더링은 PC 또는 Playstation 4 기반 VR 앱을 위한 기능입니다. 양 눈의 이미지를 동시에 하나의 렌더 텍스처로 모아 렌더링하고, 씬 전체가 한 번만 렌더링되며 CPU 프로세싱 시간이 대폭 감소됩니다. 이 기능이 없으면 Unity는 동일한 씬을 왼쪽 눈 이미지 한 번, 오른쪽 눈 이미지 한 번, 이렇게 2번을 렌더해야 합니다.

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

일반적인 VR 렌더링

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

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

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

이 기능을 활성화하려면 PlayerSettings(메뉴: Edit > Project Settings > Player)를 여십시오. PlayerSettings 에서 Other Settings 로 이동하여 Virtual Reality Supported 체크박스가 선택되어 있는지 확인하고, 아래의 __Single-Pass Stereo Rendering __ 체크박스를 선택합니다.

Unity 빌트인 렌더링 기능과 스탠다드 에셋은 이 기능과 모두 호환됩니다. 그러나 커스텀 빌드한 셰이더와 에셋 스토어에서 다운로드한 셰이더는 수정이 필요할 수 있습니다(예를 들어, 스크린 공간 좌표를 절반으로 패킹된 렌더 텍스처에 액세스하기 위해 크기를 조정하고 상쇄되어야 할 수 있습니다).

싱글 패스 스테레오 렌더링을 지원할 수 있도록 셰이더(Shaders) 작성 및 수정

UnityCG.cginc에 존재하는 헬퍼 함수는 싱글 패스 스테레오 렌더링을 지원합니다.

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

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

추가적으로, 셰이더에서 unity_StereoEyeIndex 상수를 사용하여 눈마다 계산을 수행할 수 있습니다. 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 또는 텍스처 맵을 사용한 전체 화면 폴리곤 드로잉으로 그려지기 때문에 이러한 경우가 발생합니다. 두 메서드는 체인의 이전 포스트 프로세싱 효과 결과물 전체를 참조합니다. 팩된 스테레오 렌더 텍스처 영역 참조를 사용하면 관련된 절반만이 아닌 렌더 텍스처 팩 전체를 참조합니다.

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));
    
    ...
}

메시(Mesh) 기반 드로잉

메시를 사용해 렌더링된 포스트 프로세싱 효과는(예를 들어, 즉시 모드에서 낮은 레벨의 그래픽스 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;
}

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