This page may not work correctly inside the Unity editor. Open in browser

向自定义着色器添加 Procedural Instancing 支持

需要 Nature Renderer 1.3.0 或更高版本。

Nature Renderer 的 Procedural Instancing 功能使您能够使用计算着色器在 GPU 上运行整个渲染管线。这极大提升了性能。

由于整个渲染管线是在 GPU 上处理的,因此能够直接连接到您的着色器。这意味着您将必须对您的自定义着色器进行少量修改,以便让它们能够配合过程实例化功能使用。

Nature Renderer 随附的全部 Nature Shaders 已支持过程实例化。

注意:此代码可被升级至新的主要 Nature Renderer 版本。如果某些功能无法适用于未来版本的 Nature Renderer,请再次检查此页面以获得更新说明。

1. 添加标签

向您的着色器添加 "NatureRendererInstancing" = "True" 标签。这将会告知 Nature Renderer,着色器支持过程实例化。

nature-renderer-add-procedural-instancing-tag.jpg

2. 添加实例化选项

向您的着色器添加下列杂注。杂注 multi_compile_instancing 添加有 GPU 实例化支持的第二着色器变体。杂注 instancing_options procedural:SetupNatureRenderer 告知 Unity 在渲染之前调用此着色器的“SetupNatureRenderer”功能,以便 Nature Renderer 可以设置渲染数据。

#pragma multi_compile_instancing
#pragma instancing_options procedural:SetupNatureRenderer

如果着色器已包含这 2 行,您需要替换它们。

3. 参考 Nature Renderer 库

您需要在您的着色器种包含 Nature Renderer 的着色器代码。如果在您的项目中已有 Nature Renderer,您可以直接通过添加如下内容来简单引用包含文件:

#include "Assets/Visual Design Cafe/Nature Shaders/Common/Nodes/Integrations/Nature Renderer.cginc"

如果在您的项目中还没有 Nature Renderer,您可以将代码直接添加到您的着色器库。有两种方法可以实现此步骤,您可以将下列代码复制-粘贴到您的着色器,也可以使用代码创建 cginc 文件,然后引用该文件:

// Copyright 2020 Visual Design Cafe. All rights reserved.
// Package: Nature Shaders
// Website: https://www.visualdesigncafe.com/nature-shaders
// Documentation: https://support.visualdesigncafe.com/hc/categories/900000043503

#ifndef NODE_INSTANCED_INCLUDED
#define NODE_INSTANCED_INCLUDED

#ifdef UNITY_PROCEDURAL_INSTANCING_ENABLED

struct CompressedFloat4x4
{
    uint positionXY;
    uint positionZ_scaleXZ;
    uint scaleY_rotationX;
    uint rotationZW;
};

uniform float3 _CompressionRange;
uniform float3 _CompressionBase;

void UnpackInt( uint packedValue, out float a, out float b )
{
    a =  ( (float) (packedValue >> 16) ) / 65535.0;
    b =  ( (float) ( (packedValue << 16) >> 16 ) ) / 65535.0;
}

float4x4 QuaternionToMatrix(float4 quaternion)
{
    float4x4 result = (float4x4)0;
    float x = quaternion.x;
    float y = quaternion.y;
    float z = quaternion.z;
    float w = quaternion.w;

    float x2 = x + x;
    float y2 = y + y;
    float z2 = z + z;
    float xx = x * x2;
    float xy = x * y2;
    float xz = x * z2;
    float yy = y * y2;
    float yz = y * z2;
    float zz = z * z2;
    float wx = w * x2;
    float wy = w * y2;
    float wz = w * z2;

    result[0][0] = 1.0 - (yy + zz);
    result[0][1] = xy - wz;
    result[0][2] = xz + wy;

    result[1][0] = xy + wz;
    result[1][1] = 1.0 - (xx + zz);
    result[1][2] = yz - wx;

    result[2][0] = xz - wy;
    result[2][1] = yz + wx;
    result[2][2] = 1.0 - (xx + yy);

    result[3][3] = 1.0;

    return result;
}

void DecompressInstanceMatrix( inout float4x4 instance, CompressedFloat4x4 compressedMatrix )
{
    float positionX;
    float positionY;
    float positionZ;

    float scaleXZ;
    float scaleY;

    float rotationX;
    float rotationY;
    float rotationZ;
    float rotationW;

    UnpackInt( compressedMatrix.positionXY, positionX, positionY );
    UnpackInt( compressedMatrix.positionZ_scaleXZ, positionZ, scaleXZ );
    UnpackInt( compressedMatrix.scaleY_rotationX, scaleY, rotationX );
    UnpackInt( compressedMatrix.rotationZW, rotationZ, rotationW );

    positionX = positionX * _CompressionRange.x + _CompressionBase.x;
    positionY = positionY * _CompressionRange.y + _CompressionBase.y;
    positionZ = positionZ * _CompressionRange.z + _CompressionBase.z;

    scaleXZ *= 16.0;
    scaleY *= 16.0;

    rotationX = rotationX * 2.0 - 1.0;
    rotationZ = rotationZ * 2.0 - 1.0;
    rotationW = rotationW * 2.0 - 1.0;
    rotationY = 
        sqrt( 1.0 - (rotationX * rotationX + rotationZ * rotationZ + rotationW * rotationW) );

    float3 position = float3(positionX, positionY, positionZ);
    float3 scale = float3(scaleXZ, scaleY, scaleXZ);
    instance = QuaternionToMatrix( float4(rotationX, rotationY, rotationZ, rotationW) );
    
    instance[0][0] *= scale.x; instance[1][0] *= scale.y; instance[2][0] *= scale.z;
    instance[0][1] *= scale.x; instance[1][1] *= scale.y; instance[2][1] *= scale.z;
    instance[0][2] *= scale.x; instance[1][2] *= scale.y; instance[2][2] *= scale.z;
    instance[0][3] *= scale.x; instance[1][3] *= scale.y; instance[2][3] *= scale.z;

    instance[0][3] = position.x;
    instance[1][3] = position.y;
    instance[2][3] = position.z;
}

#if defined(SHADER_API_GLCORE) \
    || defined(SHADER_API_D3D11) \
    || defined(SHADER_API_GLES3) \
    || defined(SHADER_API_METAL) \
    || defined(SHADER_API_VULKAN) \
    || defined(SHADER_API_PSSL) \
    || defined(SHADER_API_XBOXONE)
uniform StructuredBuffer _NatureRendererBuffer;
#endif

float4x4 inverse(float4x4 input)
 {
     #define minor(a,b,c) determinant(float3x3(input.a, input.b, input.c))
     
     float4x4 cofactors = float4x4(
          minor(_22_23_24, _32_33_34, _42_43_44), 
         -minor(_21_23_24, _31_33_34, _41_43_44),
          minor(_21_22_24, _31_32_34, _41_42_44),
         -minor(_21_22_23, _31_32_33, _41_42_43),
         
         -minor(_12_13_14, _32_33_34, _42_43_44),
          minor(_11_13_14, _31_33_34, _41_43_44),
         -minor(_11_12_14, _31_32_34, _41_42_44),
          minor(_11_12_13, _31_32_33, _41_42_43),
         
          minor(_12_13_14, _22_23_24, _42_43_44),
         -minor(_11_13_14, _21_23_24, _41_43_44),
          minor(_11_12_14, _21_22_24, _41_42_44),
         -minor(_11_12_13, _21_22_23, _41_42_43),
         
         -minor(_12_13_14, _22_23_24, _32_33_34),
          minor(_11_13_14, _21_23_24, _31_33_34),
         -minor(_11_12_14, _21_22_24, _31_32_34),
          minor(_11_12_13, _21_22_23, _31_32_33)
     );
     #undef minor
     return transpose(cofactors) / determinant(input);
 }
#endif

void SetupNatureRenderer()
{
    #ifdef UNITY_PROCEDURAL_INSTANCING_ENABLED
        #ifdef HIGH_DEFINITION_RENDER_PIPELINE
            #undef unity_ObjectToWorld
            #undef unity_WorldToObject
        #endif
        
        DecompressInstanceMatrix(unity_ObjectToWorld, _NatureRendererBuffer[unity_InstanceID]);
        unity_WorldToObject = inverse(unity_ObjectToWorld);
    #endif
}

void Instanced_float( float3 vertex, out float3 vertexOut )
{
    vertexOut = vertex;
}
#endif
    

 

可选

如果您需要支持多个着色器,您可以使用 NATURE_RENDERER 关键字来包含/排除 Nature Renderer 的代码。Nature Renderer 自动为使用 Nature Renderer 渲染的材质设置 NATURE_RENDERER 关键字。

例如:

#ifdef NATURE_RENDERER
#include "Assets/Visual Design Cafe/Nature Shaders/Common/Nodes/Integrations/Nature Renderer.cginc"
#endif

如果您需要支持多种着色器,不得忘记添加 NATURE_RENDERER 作为着色器功能,这样会创建两个着色器变体:一个使用 Nature Renderer,另一个不用:

#pragma shader_feature_local NATURE_RENDERER

 

这篇文章有帮助吗?
11 人中有 5 人觉得有帮助