GPU 实例化着色器的 InstanceID 顺序错误

问题描述 投票:0回答:1

我对此一筹莫展。我一直在尝试编写一个脚本,让我可以使用相对于游戏对象的特定变换来渲染给定网格和材质的许多实例。我一直在使用 Graphics.RenderMeshInstanced() 方法在这个有点简陋的脚本中实现这一点:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class InstancedRenderer : MonoBehaviour
{
    public Mesh mesh;
    public Material sharedMaterial;

    private Transform tsfm;

    public Material material { get; private set; }
    private Matrix4x4[] matrices;

    private List<ComputeBuffer> buffers;

    private void Awake()
    {
        tsfm = transform;

        if(sharedMaterial != null)
            SetMaterial(sharedMaterial);
    }

    private void Update()
    {
        if(tsfm.hasChanged)
            material.SetMatrix("transform", transform.localToWorldMatrix);

        RenderMeshes();
    }

    private void RenderMeshes()
    {
        if (matrices == null)
            return;

        RenderParams parameters = new RenderParams()
        {
            camera = null,
            layer = 0,
            material = this.material,
            receiveShadows = true,
            worldBounds = new Bounds(transform.position, Vector3.one * 100),
            
            shadowCastingMode = UnityEngine.Rendering.ShadowCastingMode.On,
        };

        material.SetInteger("instanceCount", matrices.Length);

        Graphics.RenderMeshInstanced(parameters, mesh, 0, matrices);
    }

    public void SetMaterial(Material mat)
    {
        sharedMaterial = mat;
        material = Instantiate(mat);
    }

    public void SetTransforms(Matrix4x4[] transforms)
    {
        matrices = transforms;
    }

    public void ApplyBuffer(string name, ComputeBuffer buffer)
    {
        material.SetBuffer(name, buffer);

        if (buffers == null)
            buffers = new List<ComputeBuffer>();

        buffers.Add(buffer);
    }

    private void OnDestroy()
    {
        foreach (ComputeBuffer buffer in buffers)
        {
            buffer.Release();
        }
    }
}

在顶点着色器中我使用这些线

            v.vertex = mul(unity_WorldToObject, mul(transform, mul(unity_ObjectToWorld, v.vertex)));
            float3 normal = normalize(mul(unity_WorldToObject, mul(transform, mul(unity_ObjectToWorld, float3(v.normal.xyz)))) );
            v.normal = normal;

使渲染的网格跟随游戏对象(变换为 float4x4)

我这样做是为了在我的低多边形游戏中高效地渲染大量草,并且它在很大程度上有效。在此之前,我一直将所有实例组合成一个网格并将其渲染为单个对象,这比拥有单个对象要好,但会产生真正奇怪的故障和伪影。

当我尝试对每个草实例应用独特的颜色时,问题就显现出来了。我将颜色传递到 float4s 的结构化缓冲区中,并通过顶点着色器中的实例 id 访问它。一开始这似乎有效,但颜色的顺序似乎完全错误。

草的颜色应与地形颜色相匹配。我已经在互联网上搜索了可能导致此问题的任何内容。我什至恢复到性能较差的一个网格实现,以仔细检查我是否以与矩阵数组相同的顺序生成颜色数组,但我看不出有什么问题。我真的不知道我在这里做什么,任何见解都将不胜感激。我愿意接受有关如何使其发挥作用的想法,或者我可以用其他东西来取代整个系统的想法。

此外,这是草着色器的其余部分,尽管它已经足够混乱,迫切需要从头开始重写。

Shader "Custom/InstancedGrass"
{
    Properties
    {
        _Color ("Color", Color) = (1,1,1,1)
        _MainTex ("Albedo (RGB)", 2D) = "white" {}
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        LOD 200

        CGPROGRAM
        #pragma multi_compile_instancing
        #pragma surface surf Lambert vertex:vert addshadow

        // Use shader model 3.0 target, to get nicer looking lighting
        #pragma target 5.0

        #ifdef SHADER_API_D3D11
            StructuredBuffer<fixed4> _TestColors;
        #endif

        sampler2D _MainTex;

        int instanceCount;
        float4x4 transform;

        struct appdata
        {
            float4 vertex : POSITION;
            float3 normal : Normal;
            UNITY_VERTEX_INPUT_INSTANCE_ID
        };

        struct Input
        {
            float2 uv_MainTex : TEXCOORD0;

            float4 instanceColor;
        };


        void vert(inout appdata v, out Input o)
        {
            UNITY_SETUP_INSTANCE_ID(v);

            v.vertex = mul(unity_WorldToObject, mul(transform, mul(unity_ObjectToWorld, v.vertex)));
            float3 normal = normalize(mul(unity_WorldToObject, mul(transform, mul(unity_ObjectToWorld, float3(v.normal.xyz)))) );
            v.normal = normal;
            
            UNITY_INITIALIZE_OUTPUT(Input, o);

            #ifdef UNITY_INSTANCING_ENABLED

                uint id = UNITY_GET_INSTANCE_ID(v);
                o.instanceColor = _TestColors[id];
            #endif
        }

        fixed4 _Color;

        // Add instancing support for this shader. You need to check 'Enable Instancing' on materials that use the shader.
        // See https://docs.unity3d.com/Manual/GPUInstancing.html for more information about instancing.
        // #pragma instancing_options assumeuniformscaling
        UNITY_INSTANCING_BUFFER_START(Props)

        UNITY_INSTANCING_BUFFER_END(Props)

        void surf (Input IN, inout SurfaceOutput o)
        {
            fixed4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color;

            fixed4 testColor = IN.instanceColor;

            o.Albedo = testColor;
            o.Alpha = 1;
        }
        ENDCG
    }
    FallBack "Diffuse"
}
c# unity-game-engine hlsl gpu-instancing
1个回答
0
投票

我不知道这种行为的原因,但我确实有关于 RenderMeshInstanced 的好消息。

您当前正在为 InstanceData 传递一个 Matrix4x4 数组,但只要您给它一个 objectToWorld 矩阵,该函数就可以接受任何结构。您可以向它传递一个包含变换和 float4 (Vector4) 颜色的结构数组。这将确保您的数据一起传输。

如果这还不足以继续下去,我可能会启动一个项目并研究这个问题。我对如何在着色器中访问自定义 InstanceData 成员仍然有点模糊,但如果您仍然遇到困难,我很乐意尝试一下。

© www.soinside.com 2019 - 2024. All rights reserved.