如何获取网格的每个顶点并使立方体生成并移动到每个顶点? (统一 C#)
How to take every vertex of a mesh and make a cube spawn and move to each one? (Unity C#)
这是我目前为对象 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
}
}
}
这有两个部分。
首先,您必须更改为渲染三角形而不是点,并为着色器提供构成立方体的所有三角形的顶点。
更改 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
,所以可能有一些遗漏,但这至少应该被视为部分答案。
这是我目前为对象 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
}
}
}
这有两个部分。
首先,您必须更改为渲染三角形而不是点,并为着色器提供构成立方体的所有三角形的顶点。
更改 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
,所以可能有一些遗漏,但这至少应该被视为部分答案。