Environment of work: Unity3D 2021.2.7.f1 Direct3D11
I try to make Laplacian Smoothing working on GPU For this case I set up Compute Shader among others VertexBuffer(input Graphics Buffer) and outVertexBuffer( output Grpahics Buffer), unfortunatelly I have a weird problem with storing data into GraphicsBuffer (storing vertices Vector3) which i use as "output" of compute shader.
Assigning of ComputeShader component:
public void RunSmoothingShader(int kernel,int verticesNo)
{
ComputeBuffer connectionsBuffer = new
ComputeBuffer(_builder._triangleBudget*6,sizeof(int));
ComputeBuffer offsetsBuffer = new ComputeBuffer(_builder._triangleBudget, sizeof(int));
ComputeBuffer _counterBuffer = new ComputeBuffer(1, 4, ComputeBufferType.Counter);
connectionsBuffer.SetData(VertexConnection.connectionsTable);
offsetsBuffer.SetData(VertexConnection.offsetsTable);
smoothingShader.SetBuffer(0,"ConnectionsBuffer",connectionsBuffer);
smoothingShader.SetBuffer(0,"OffsetsBuffer",offsetsBuffer);
smoothingShader.SetBuffer(0, "Counter", _counterBuffer);
smoothingShader.SetBuffer(0,"OutVertexBuffer",outVertexBuffer);
smoothingShader.SetInt("MaxVerticesNumber", verticesNo);
smoothingShader.SetBuffer(0,"VertexBuffer", _builder._vertexBuffer);
smoothingShader.Dispatch(kernel,64,64,64);
}
Initialization of outVertexBuffer: (stride is equal 12 because type of float3)
outVertexBuffer =
new GraphicsBuffer(GraphicsBuffer.Target.Vertex, 5000000, 12);
In LaplacianSmoothing ComputeShader I want to recalculate positions of vertices and store them into outVertexBuffer using Store3 function. Below is code for LaplacianSmoothing in ComputeShader (HLSL style):
#pragma kernel LaplacianSmooth
RWTexture2D<float4> Result;
RWByteAddressBuffer VertexBuffer;
RWStructuredBuffer<uint> Counter; // used only for counting
RWByteAddressBuffer ConnectionsBuffer;
RWByteAddressBuffer OffsetsBuffer;
RWByteAddressBuffer OutVertexBuffer;
int MaxVerticesNumber;
#define SIZEOF_UINT 4
#define SIZEOF_FLOAT3 12
[numthreads(8,8,8)]
void LaplacianSmooth (uint3 id : SV_DispatchThreadID)
{
uint currentIndex = 512*512*id.z 512*id.y id.x;
if (currentIndex < MaxVerticesNumber)
{
float3 currentVertex = asfloat(VertexBuffer.Load3(2*currentIndex*SIZEOF_FLOAT3));
float3 sumNeighVertex = 0;
uint neighborAdrr=0;
int currentOffset = OffsetsBuffer.Load(currentIndex*SIZEOF_UINT);
int nextOffset = OffsetsBuffer.Load((currentIndex 1)*SIZEOF_UINT);
int connectionCount = nextOffset-currentOffset;
for (int i=0; i<connectionCount;i )
{
neighborAdrr = ConnectionsBuffer.Load((currentOffset i)*SIZEOF_UINT);// Adress of neighbor;
sumNeighVertex = asfloat(VertexBuffer.Load3(neighborAdrr*2*SIZEOF_FLOAT3));
}
sumNeighVertex = sumNeighVertex / connectionCount;
float3 newVert = sumNeighVertex currentVertex;
OutVertexBuffer.Store3(currentIndex*SIZEOF_FLOAT3,asuint(currentVertex));
}
}
In next step I want to get data from outVertexBuffer and assing them into vertexArray GraphicsBuffer.GetData(Vector3[] array) function.
public void OnSmoothMeshLaplacian()
{
PrepareSmoothing();
var mesh = GetComponent<MeshFilter>().mesh;
Mesh testMesh = Instantiate(mesh);
GameObject.Find("Laplacian").GetComponent<MeshFilter>().sharedMesh = testMesh;
//testMesh = LaplacianFilter(testMesh, 5,1f);
Vector3[] vertexArray;
vertexArray = new Vector3[2*testMesh.vertices.Length];
var network = VertexConnection.BuildNetwork(testMesh.triangles);
RunSmoothingShader(smoothingShader.FindKernel("LaplacianSmooth"), testMesh.vertexCount);
outVertexBuffer.GetData(vertexArray);
testMesh.SetVertexBufferData(vertexArray, 0, 0, vertAndPosList.Length);
testMesh.RecalculateBounds();
Debug.Log("Mesh:" mesh.bounds.size);
Debug.Log("Test Mesh:" testMesh.bounds.size);
Debug.Log($"Shrinkage: {(mesh.bounds.size - testMesh.bounds.size).magnitude / mesh.bounds.size.magnitude * 100f}%");
}
I set up the breakpoint after getData function and checked the data, all of them are (0,0,0). I use RenderDoc for checking how buffers works, googled documentations for all function used in shader and main program, but nothing explain this lack of assigning data. I want to add that when I use inputBuffer also as output then I get data (logicaly incorrect, but still not (0,0,0).
CodePudding user response:
Try this:
outVertexBuffer = new GraphicsBuffer(GraphicsBuffer.Target.Structured, 5000000, 12);
Notice the changed Target. Graphics buffer needs at least one of the Compute target flags to be able to be bound to a compute kernel. Target.Vertex is not such a flag. You could still add it but in your case it's not needed.
In fact outVertexBuffer
could just be a ComputeBuffer
. No need to use GraphicsBuffer
since you just copy it to the mesh on the CPU side.
EDIT---
Another thing:
SetVertexBufferData()
requires you to configure all vertex attributes first and the data you pass should basically be raw vertex data: positions, normals, uvs (if any) and so on.
If you only want to set vertices perhaps it would be easier to just use Mesh.SetVertices()
.