New: Nature Shaders

Interaction, Wind, Dynamic Masking, and Better Shading for your vegetation.

Learn more
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

 

这篇文章有帮助吗?
17 人中有 8 人觉得有帮助