When creating a Mesh from a script, you must always set up a SubMesh.
Additionally, if you access it as a StructuredBuffer from a ComputeShader, make sure to set GraphicsBuffer.Target.Structured.
As for SetVertexBufferData and SetIndexBufferData, you probably don’t need to call them.
Here is some sample code for your reference.
using System.Runtime.InteropServices;
using UnityEngine;
using UnityEngine.Rendering;
public class SampleComputeMesh : MonoBehaviour
{
[StructLayout(LayoutKind.Sequential)]
struct Vertex
{
public Vector3 position;
public Vector3 normal;
public Vector4 color;
public Vector2 uv0;
}
[SerializeField] Material material;
[SerializeField] ComputeShader computeShader;
[SerializeField] Bounds bounds = new Bounds(Vector3.zero, new Vector3(1000, 1000, 1000)); // set your value
Mesh mesh;
GraphicsBuffer indexBuffer;
GraphicsBuffer vertexBuffer;
float simulationTime;
const int vertexCount = 3;
const int indexCount = 3;
void Start()
{
var meshFilter = this.gameObject.AddComponent<MeshFilter>();
var meshRenderer = this.gameObject.AddComponent<MeshRenderer>();
mesh = CreateMesh();
meshFilter.sharedMesh = mesh;
meshRenderer.sharedMaterial = material;
indexBuffer = mesh.GetIndexBuffer();
vertexBuffer = mesh.GetVertexBuffer(0);
}
void OnDestroy()
{
indexBuffer.Dispose();
indexBuffer = null; // for GC
vertexBuffer.Dispose();
vertexBuffer = null; // for GC
Destroy(mesh);
mesh = null; // for GC
}
private void Update()
{
var kernel = computeShader.FindKernel("CSMain");
computeShader.GetKernelThreadGroupSizes(kernel, out var x, out _, out _);
var groups = (indexCount + (int)x - 1) / (int)x;
computeShader.SetFloat("Time", simulationTime);
computeShader.SetBuffer(kernel, "IndexBuffer", indexBuffer);
computeShader.SetBuffer(kernel, "VertexBuffer", vertexBuffer);
computeShader.Dispatch(kernel, groups, 1, 1);
simulationTime += Time.deltaTime;
}
Mesh CreateMesh()
{
var mesh = new Mesh();
mesh.name = "TestComputeMesh";
mesh.vertexBufferTarget |= GraphicsBuffer.Target.Structured; // for access as StructuredBuffer from compute shaders
mesh.indexBufferTarget |= GraphicsBuffer.Target.Structured; // for access as StructuredBuffer from compute shaders
mesh.bounds = bounds;
var vertexLayout = new[]
{
new VertexAttributeDescriptor(VertexAttribute.Position, VertexAttributeFormat.Float32, 3),
new VertexAttributeDescriptor(VertexAttribute.Normal, VertexAttributeFormat.Float32, 3),
new VertexAttributeDescriptor(VertexAttribute.Color, VertexAttributeFormat.Float32, 4),
new VertexAttributeDescriptor(VertexAttribute.TexCoord0, VertexAttributeFormat.Float32, 2)
};
mesh.SetVertexBufferParams(vertexCount, vertexLayout);
var initialVertices = new Vertex[vertexCount] {
new Vertex { position = new Vector3(0f, 0f, 0f), normal = new Vector3(0f, 0f, -1f), color = Vector4.one, uv0 = new Vector2(0, 0) },
new Vertex { position = new Vector3(1f, 1f, 0f), normal = new Vector3(0f, 0f, -1f), color = Vector4.one, uv0 = new Vector2(1, 1) },
new Vertex { position = new Vector3(1f, 0f, 0f), normal = new Vector3(0f, 0f, -1f), color = Vector4.one, uv0 = new Vector2(1, 0) }
};
mesh.SetVertexBufferData(initialVertices, 0, 0, vertexCount);
mesh.SetIndexBufferParams(indexCount, IndexFormat.UInt32);
var indices = new int[indexCount] { 0, 1, 2 };
mesh.SetIndexBufferData(indices, 0, 0, indexCount);
mesh.subMeshCount = 1;
mesh.SetSubMesh(0, new SubMeshDescriptor(0, indexCount), MeshUpdateFlags.DontRecalculateBounds);
return mesh;
}
}
#pragma kernel CSMain
struct Vertex
{
float3 pos;
float3 norm;
float4 color;
float2 uv0;
};
float Time;
RWStructuredBuffer<uint> IndexBuffer;
RWStructuredBuffer<Vertex> VertexBuffer;
[numthreads(32,1,1)]
void CSMain (uint id : SV_DispatchThreadID)
{
float offset = sin(Time * 2) * 0.01;
VertexBuffer[id].pos = VertexBuffer[id].pos + float3(offset, offset, offset);
IndexBuffer[id] = id;
}