Version: 2022.3
Stereo rendering
VR 帧计时

Single-pass instanced rendering and custom shaders

URP, HDRP, ShaderGraph, Surface shaders, and built-in shaders already support single-pass stereo instanced rendering. However, shaders from the Asset Store, from other third parties, or those that you have written yourself might need to be updated.

For more information about supporting instanced rendering in your shaders, see GPU Instancing. The information in this section specifically talks about stereo rendering and might not include all changes you need to make to support instanced rendering in general.

Update the vertex input attributes struct

Add the UNITY_VERTEX_INPUT_INSTANCE_ID macro to the appdata struct.

示例:

struct appdata
{
    float4 vertex : POSITION;
    float2 uv : TEXCOORD0;

    UNITY_VERTEX_INPUT_INSTANCE_ID //插入
};

Update the vertex output attributes struct

Add UNITY_VERTEX_OUTPUT_STEREO macro to the v2f output struct.

示例:

struct v2f
{
    float2 uv : TEXCOORD0;
    float4 vertex : SV_POSITION;

    UNITY_VERTEX_OUTPUT_STEREO //插入
};

Update the main vertex shader function

Add the following macros to the beginning of your main vert method (in order):

  1. UNITY_SETUP_INSTANCE_ID()
  2. UNITY_INITIALIZE_OUTPUT(v2f, o)
  3. UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO()

UNITY_SETUP_INSTANCE_ID() calculates and sets the built-in unity_StereoEyeIndex and unity_InstanceID shader variables to the correct values based on which eye the GPU is currently rendering.

UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO 根据 unity_StereoEyeIndex 的值告诉 GPU 应该渲染到纹理数组中的哪只眼睛。此宏还从顶点着色器传输 unity_StereoEyeIndex 的值,确保仅当在片元着色器 frag 方法中调用 UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX 时才能在片元着色器中访问该值。

UNITY_INITALIZE_OUTPUT(v2f,o) 将所有 v2f 值初始化为 0。

示例:

v2f vert (appdata v)
{
    v2f o;

    UNITY_SETUP_INSTANCE_ID(v); //插入
    UNITY_INITIALIZE_OUTPUT(v2f, o); //插入
    UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o); //插入

    o.vertex = UnityObjectToClipPos(v.vertex);

    o.uv = v.uv;

    return o;
}

后期处理着色器

If you want your Post-Processing shaders to support single-pass stereo instancing, follow the steps for custom shaders as well as the steps below.

Note: You can download all Unity base shader scripts from the Unity website.

Do the following for each Post-Processing shader that you want to support single-pass instancing:

  1. Add the UNITY_DECLARE_SCREENSPACE_TEXTURE(tex) macro outside the frag method (see the example below for placement) in your Shader script, so that when you use a particular stereo rendering method the GPU uses the appropriate texture sampler. For example, if you use multi-pass rendering, the GPU uses a texture 2D sampler. For single-pass instancing or multi-view rendering, the texture sampler is a texture array.

  2. Add UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(i) at the beginning of the fragment shader frag method (See the example below for placement). You only need to add this macro if you want to use the unity_StereoEyeIndex built-in shader variable to find out which eye the GPU is rendering to. This is useful when testing post-processing effects.

  3. Use the UNITY_SAMPLE_SCREENSPACE_TEXTURE() macro when sampling 2D textures (See the example below). Standard shaders use a 2D texture-based back buffer to sample textures. Single-pass stereo instancing does not use this type of back buffer, so if you do not specify a different method for 2D texture sampling, your shader does not render correctly. To prevent rendering issues, the UNITY_SAMPLE_SCREENSPACE_TEXTURE() macro detects which stereo rendering path you are using and then automatically samples the texture in the correct manner. See Unity documentation on HLSLSupport.cginc to learn more about similar macros used for depth textures and screen-space shadow maps.

示例:

UNITY_DECLARE_SCREENSPACE_TEXTURE(_MainTex); //插入

fixed4 frag (v2f i) : SV_Target
{
    UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(i); //插入
    
    fixed4 col = UNITY_SAMPLE_SCREENSPACE_TEXTURE(_MainTex, i.uv); //插入
    
    // 仅插入颜色
    
    col = 1 - col;
    
    return col;
}

完整的示例着色器代码

Below is a simple example of the template image effect shader with all of the previously mentioned changes applied to support single-pass stereo instancing. The lines added to the shader code are marked with the comment: //Insert.

struct appdata
{
    float4 vertex : POSITION;
    float2 uv : TEXCOORD0;
    
    UNITY_VERTEX_INPUT_INSTANCE_ID //插入
};

//v2f 输出结构

struct v2f
{

    float2 uv : TEXCOORD0;
    float4 vertex : SV_POSITION;
    
    UNITY_VERTEX_OUTPUT_STEREO //插入
};

v2f vert (appdata v)
{
    v2f o;
    
    UNITY_SETUP_INSTANCE_ID(v); //插入
    UNITY_INITIALIZE_OUTPUT(v2f, o); //插入
    UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o); //插入
    
    o.vertex = UnityObjectToClipPos(v.vertex);
    o.uv = v.uv;
    return o;
}

UNITY_DECLARE_SCREENSPACE_TEXTURE(_MainTex); //插入

fixed4 frag (v2f i) : SV_Target
{
    UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(i); //插入
    
    fixed4 col = UNITY_SAMPLE_SCREENSPACE_TEXTURE(_MainTex, i.uv); //插入
    
    // 插入颜色
    
    col = 1 - col;
    
    return col;
}

程序化几何体

If you use the Graphics.DrawProceduralIndirect() and CommandBuffer.DrawProceduralIndirect() methods to draw fully procedural geometry on the GPU, note that both methods receive their arguments from a compute buffer. This means that it is difficult to increase the instance count at run time. To increase the instance count, you must manually double the instance count contained in your compute buffers.

调试着色器

以下着色器代码将游戏对象渲染为绿色(用户左眼)和红色(右眼)。此着色器对于调试立体渲染非常有用,因为它可用于验证所有立体图形是否正常工作。

Shader "XR/StereoEyeIndexColor"
{
   Properties
   {
       _LeftEyeColor("Left Eye Color", COLOR) = (0,1,0,1)
       _RightEyeColor("Right Eye Color", COLOR) = (1,0,0,1)
   }

   SubShader
   {
      Tags { "RenderType" = "Opaque" }

      Pass
      {
         CGPROGRAM

         #pragma vertex vert
         #pragma fragment frag

         float4 _LeftEyeColor;
         float4 _RightEyeColor;

         #include "UnityCG.cginc"

         struct appdata
         {
            float4 vertex : POSITION;

            UNITY_VERTEX_INPUT_INSTANCE_ID
         };

         struct v2f
         {
            float4 vertex : SV_POSITION;

            UNITY_VERTEX_INPUT_INSTANCE_ID 
            UNITY_VERTEX_OUTPUT_STEREO
         };

         v2f vert (appdata v)
         {
            v2f o;

            UNITY_SETUP_INSTANCE_ID(v);
            UNITY_INITIALIZE_OUTPUT(v2f, o);
            UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);

            o.vertex = UnityObjectToClipPos(v.vertex);

            return o;
         }

         fixed4 frag (v2f i) : SV_Target
         {
            UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(i);

            return lerp(_LeftEyeColor, _RightEyeColor, unity_StereoEyeIndex);
         }
         ENDCG
      }
   }
}

ShaderGraph debug shader

ShaderGraph automatically adds the macros required to support single-pass stereo rendering. To implement the debug shader in ShaderGraph you can use a Custom Function Node that sets the base color based on the eye index.

Use the unity_StereoEyeIndex shader attribute to determine the base color depending on which eye instance is being rendered. The Custom Function Node in the example above contains the following code:

Out = lerp(LeftColor, RightColor, unity_StereoEyeIndex);
Stereo rendering
VR 帧计时