【问题标题】:How to take every vertex of a mesh and make a cube spawn and move to each one? (Unity C#)如何获取网格的每个顶点并生成立方体并移动到每个顶点? (统一 C#)
【发布时间】:2019-06-03 18:14:12
【问题描述】:

这是我目前为对象emptsh 使用的脚本,它使用计算着色器使一个点出现在屏幕上,BoyMesh 网格上的每个顶点都在该屏幕上。

现在我正在尝试,而不是在屏幕上为每个BoyMesh 顶点位置显示简单的点,而是从不同的位置生成立方体,然后让它们飞到每个顶点位置(每个顶点位置一个立方体)。有什么想法吗?

using UnityEngine;
using System.Collections;

public class emptsh : MonoBehaviour
{
    public BoyMesh BoyMesh;
    public Mesh meshdata;

    public ComputeShader cshader;
    public Material mat;

    private int kernel;
    private int num4pos;

    private ComputeBuffer posbuffer;
    private int num4vertex;
    private ComputeBuffer vertexbuffer;


    private void meshInfo()
    {
        Vector3[] vertics = meshdata.vertices;
        int[] triangles = meshdata.triangles;
        num4vertex = triangles.Length;

        Vector3[] newVertics = new Vector3[num4vertex];
        for (int i = 0; i < num4vertex; ++i)
        {
            newVertics[i] = vertics[triangles[i]];
        }

        vertexbuffer = new ComputeBuffer(num4vertex, 12);
        vertexbuffer.SetData(newVertics);
    }

    void Start()
    {   
        meshdata = BoyMesh.Mesh;

        kernel = cshader.FindKernel("CSMain");
        num4pos = 1; //this determines how many appear
        //num4vertex = ;
        meshInfo();
        //float3 
        posbuffer = new ComputeBuffer(num4pos, 12); 
    }

    private void BufferSet()
    {
        cshader.SetBuffer(kernel, "posbuffer", posbuffer); 
        mat.SetBuffer("posbuffer", posbuffer);     
        mat.SetBuffer("vertexbuffer", vertexbuffer);             
    }

    private void OnRenderObject()
    {
        BufferSet();
        //1
        cshader.Dispatch(kernel, 1, 1, 1);
        //2
        mat.SetPass(0);
        //3
        Graphics.DrawProceduralNow(MeshTopology.Points, num4vertex, num4pos);      
    }

    private void OnDestroy()
    {
        posbuffer.Release();
        vertexbuffer.Release();
    }
}

然后是实际的着色器代码:

Shader "Unlit/cshader4"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
    }
    CGINCLUDE
        #define time _Time.y 
        float3 Rotx(in float3 p,in float a){
            float c,s; float3 q = p;
            c = cos(a); s = sin(a);
            q.y = c * p.y - s * q.z;
            q.z = s * p.y + c * q.z;
            return q;
        }

        float random(float id){
            return frac(sin(id)*678.342231);
        }
    ENDCG
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        LOD 100

        Pass{
            CGPROGRAM
                #pragma target 5.0
                #pragma vertex vert
                #pragma fragment frag 
                #include "UnityCG.cginc"

                sampler2D _MainTex;

                StructuredBuffer<float3> vertexbuffer;
                StructuredBuffer<float3> posbuffer;
                struct vertIN{
                    uint vID : SV_VertexID;
                    uint ins : SV_InstanceID;
                };

                struct vertOUT{
                    float4 pos : SV_POSITION;
                };

                vertOUT vert(vertIN i){
                    vertOUT o = (vertOUT)0;
                        float4 position = float4(vertexbuffer[i.vID],1);
                        position.xyz = Rotx(position.xyz,time*(1+random(i.ins)));
                        position.xyz += posbuffer[i.ins];
                        o.pos = UnityObjectToClipPos(position);
                    return o;
                }

                fixed4 frag(vertOUT ou):SV_Target{

                    return 1;
                }
            ENDCG
        }
    }
}

【问题讨论】:

    标签: c# unity3d shader hlsl


    【解决方案1】:

    这有两个部分。

    首先,您必须更改为渲染三角形而不是点,并为着色器提供构成立方体的所有三角形的顶点。

    更改您的 MeshInfo 以创建 36 * 网格中顶点数的顶点缓冲区。这是因为您需要为每个顶点制作 12 个三角形(立方体的 6 条边各有 2 个三角形)。对于 36 个顶点中的每一个,您都需要改变它的位置以将其从立方体的中心移动到它的一个角:

    private void meshInfo()
    {
        float cubeHalfWidth = 0.01f; // determines how big the cube is.
        num4vertex = 36 * meshdata.vertices;
    
        Vector3[] newVertics = new Vector3[num4vertex];
        for (int i = 0; i < meshdata.vertices; ++i)
        {
            Vector3 curVertex = meshdata.vertices[i];
    
            // find corner positions
            Vector3 bottomBackLeftCorner = new Vector3(curVertex.x - cubeHalfWidth, curVertex.y - cubeHalfWidth, curVertex.z - cubeHalfWidth);
            Vector3 bottomFrontLeftCorner = new Vector3(curVertex.x - cubeHalfWidth, curVertex.y - cubeHalfWidth, curVertex.z + cubeHalfWidth_;
            Vector3 bottomFrontRightCorner = new Vector3(curVertex.x + cubeHalfWidth, curVertex.y - cubeHalfWidth, curVertex.z + cubeHalfWidth);
            Vector3 bottomBackRightCorner = new Vector3(curVertex.x + cubeHalfWidth, curVertex.y - cubeHalfWidth, curVertex.z - cubeHalfWidth);
            Vector3 topBackLeftCorner = new Vector3(curVertex.x - cubeHalfWidth, curVertex.y + cubeHalfWidth, curVertex.z - cubeHalfWidth);
            Vector3 topFrontLeftCorner = new Vector3(curVertex.x - cubeHalfWidth, curVertex.y + cubeHalfWidth, curVertex.z + cubeHalfWidth);
            Vector3 topFrontRightCorner = new Vector3(curVertex.x + cubeHalfWidth, curVertex.y + cubeHalfWidth, curVertex.z + cubeHalfWidth);
            Vector3 topBackRightCorner = new Vector3(curVertex.x + cubeHalfWidth, curVertex.y + cubeHalfWidth, curVertex.z - cubeHalfWidth)};
    
            // create triangles, clockwise looking at visible side
            int o=i*36;
            // back Face
            newVertics[o++] = bottomBackLeftCorner;
            newVertics[o++] = topBackLeftCorner;
            newVertics[o++] = topBackRightCorner;
    
            newVertics[o++] = topBackRightCorner;
            newVertics[o++] = bottomBackRightCorner;
            newVertics[o++] = bottomBackLeftCorner;
    
            // bottom Face
            newVertics[o++] = bottomFrontRightCorner;
            newVertics[o++] = bottomFrontLeftCorner;
            newVertics[o++] = bottomBackLeftCorner;
    
            newVertics[o++] = bottomBackLeftCorner;
            newVertics[o++] = bottomBackRightCorner;
            newVertics[o++] = bottomFrontRightCorner;
    
            // front Face
            newVertics[o++] = bottomFrontRightCorner;
            newVertics[o++] = topFrontRightCorner;
            newVertics[o++] = topFrontLeftCorner;
    
            newVertics[o++] = topFrontLeftCorner;
            newVertics[o++] = bottomFrontLeftCorner;
            newVertics[o++] = bottomFrontRightCorner;
    
            // top Face
            newVertics[o++] = topBackRightCorner;
            newVertics[o++] = topBackLeftCorner;
            newVertics[o++] = topFrontLeftCorner;
    
            newVertics[o++] = topFrontLeftCorner;
            newVertics[o++] = topFrontRightCorner;
            newVertics[o++] = topBackRightCorner;
    
            // left Face
            newVertics[o++] = bottomFrontLeftCorner;
            newVertics[o++] = topFrontLeftCorner;
            newVertics[o++] = topBackLeftCorner;
    
            newVertics[o++] = topBackLeftCorner;
            newVertics[o++] = bottomBackLeftCorner;
            newVertics[o++] = bottomFrontLeftCorner;
    
            // right Face
            newVertics[o++] = bottomBackRightCorner;
            newVertics[o++] = topBackRightCorner;
            newVertics[o++] = topFrontRightCorner;
    
            newVertics[o++] = topFrontRightCorner;
            newVertics[o++] = bottomFrontRightCorner;
            newVertics[o] = bottomBackRightCorner;
        }
    
        vertexbuffer = new ComputeBuffer(num4vertex, Marshal.SizeOf(newVertics.GetType().GetElementType()));
    
        vertexbuffer.SetData(newVertics);
    }
    

    您还需要将MeshTopology 更改为Triangles

    Graphics.DrawProceduralNow(MeshTopology.Triangles, num4vertex, num4pos); 
    

    第二部分是让立方体移动。这是更简单的步骤。

    在着色器中,将float _moveCubeT 参数添加到着色器,并根据_moveCubeT 参数从某个起始位置到您已有的位置:

    Shader "Unlit/cshader4"
    {
        Properties
        {
            _moveCubeT ("MoveCubeT", Float) = 0
            _MainTex ("Texture", 2D) = "white" {}
        }
        CGINCLUDE
            #define time _Time.y 
            float3 Rotx(in float3 p,in float a){
                float c,s; float3 q = p;
                c = cos(a); s = sin(a);
                q.y = c * p.y - s * q.z;
                q.z = s * p.y + c * q.z;
                return q;
            }
    
            float random(float id){
                return frac(sin(id)*678.342231);
            }
        ENDCG
        SubShader
        {
            Tags { "RenderType"="Opaque" }
            LOD 100
    
            Pass{
                CGPROGRAM
                    #pragma target 5.0
                    #pragma vertex vert
                    #pragma fragment frag 
                    #include "UnityCG.cginc"
    
                    sampler2D _MainTex;
    
                    StructuredBuffer<float3> vertexbuffer;
                    StructuredBuffer<float3> posbuffer;
                    float _moveCubeT;
    
                    struct vertIN{
                        uint vID : SV_VertexID;
                        uint ins : SV_InstanceID;
                    };
    
                    struct vertOUT{
                        float4 pos : SV_POSITION;
                    };
    
                    vertOUT vert(vertIN i){
                        vertOUT o = (vertOUT)0;
                            float3 startingpos = float3(0,0,0); //set starting pos for each cube here
                            float4 position = float4(vertexbuffer[i.vID],1);
                            position.xyz = Rotx(position.xyz,time*(1+random(i.ins)));
                            position.xyz += posbuffer[i.ins];
    
                            position.xyz = lerp(startingpos, position.xyz, _moveCubeT); // lerp based on time
    
                            o.pos = UnityObjectToClipPos(position);
                        return o;
                    }
    
                    fixed4 frag(vertOUT ou):SV_Target{
                        return 1;
                    }
                ENDCG
            }
        }
    }
    

    然后回到您的 C# 代码中,根据立方体的运输位置设置此 _moveCubeT 浮点数:

    private void BufferSet()
    {
        // Move cubes for 2 seconds and pause for 8 seconds, repeat.
        float t = Mathf.Clamp( (Time.time % 10f) / 2f, 0f, 1f); 
        cshader.SetBuffer(kernel, "posbuffer", posbuffer); 
        mat.SetBuffer("posbuffer", posbuffer);     
        mat.SetBuffer("vertexbuffer", vertexbuffer);
        mat.SetFloat("_moveCubeT", t);
    } 
    

    总而言之,这旨在为您提供完全无光照、无纹理的白色立方体,这些立方体移动到网格上顶点所在的位置。如果您想对这些立方体进行光照、纹理或着色,则必须进行一些更改,但这更适合另一个问题。

    我从未使用过DrawProceduralNow,因此可能会有一些缺失的部分,但这至少应该被视为部分答案。

    【讨论】:

    • 非常感谢。出于某种原因,position.xyz = lerp(startingpos, position.xyz, _moveCubeT); 使网格不显示在屏幕上。如果我将其更改为 position.xyz = lerp(startingpos, position.xyz, 1.0f); 之类的东西作为测试,那么它会显示出来,但当然没有动作。我现在正试图弄清楚,但想知道你是否知道可能出了什么问题。将其标记为已解决,因为它基本上是,而且令人难以置信的是。非常感谢!
    • @matthew 尝试使用 mat.SetFloat("_moveCubeT", t); 我编辑了答案以反映这一变化。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-08-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-07-14
    相关资源
    最近更新 更多