在 computeShader 代码运行后将数据存储在 GraphicsBuffer 中总是 (0,0,0)
Storing data inside GraphicsBuffer after computeShader code runs always are (0,0,0)
工作环境:Unity3D 2021.2.7.f1
Direct3D11
我尝试让拉普拉斯平滑在 GPU 上运行
对于这种情况,我在其他 VertexBuffer(输入图形缓冲区)和 outVertexBuffer(输出 Grpahics 缓冲区)中设置了 Compute Shader,不幸的是,我在将数据存储到 GraphicsBuffer(存储顶点 Vector3)时遇到了一个奇怪的问题,我将其用作计算着色器的“输出” .
ComputeShader 组件的分配:
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);
}
outVertexBuffer 的初始化:(stride 等于 12,因为类型为 float3)
outVertexBuffer =
new GraphicsBuffer(GraphicsBuffer.Target.Vertex, 5000000, 12);
在 LaplacianSmoothing ComputeShader 中,我想重新计算顶点的位置并使用 Store3 函数将它们存储到 outVertexBuffer 中。
下面是 ComputeShader 中 LaplacianSmoothing 的代码(HLSL 风格):
#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));
}
}
下一步我想从 outVertexBuffer 获取数据并将它们赋给 vertexArray GraphicsBuffer.GetData(Vector3[] array) 函数。
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}%");
}
我在getData函数后设置了断点,查看数据,都是(0,0,0)。
我使用 RenderDoc 检查缓冲区的工作方式,用谷歌搜索着色器和主程序中使用的所有函数的文档,但没有任何解释缺少分配数据。
我想补充一点,当我也使用 inputBuffer 作为输出时,我会得到数据(逻辑上不正确,但仍然不是 (0,0,0)。
试试这个:
outVertexBuffer = new GraphicsBuffer(GraphicsBuffer.Target.Structured, 5000000, 12);
请注意更改后的目标。图形缓冲区需要至少一个计算目标标志才能绑定到计算内核。 Target.Vertex 不是这样的标志。您仍然可以添加它,但在您的情况下不需要。
事实上 outVertexBuffer
可能只是一个 ComputeBuffer
。无需使用 GraphicsBuffer
,因为您只需将其复制到 CPU 侧的网格。
编辑---
另一件事:
SetVertexBufferData()
要求您首先配置所有顶点属性,您传递的数据应该基本上是原始顶点数据:位置、法线、uvs(如果有)等。
如果您只想设置顶点,也许使用 Mesh.SetVertices()
.
会更容易
工作环境:Unity3D 2021.2.7.f1 Direct3D11
我尝试让拉普拉斯平滑在 GPU 上运行 对于这种情况,我在其他 VertexBuffer(输入图形缓冲区)和 outVertexBuffer(输出 Grpahics 缓冲区)中设置了 Compute Shader,不幸的是,我在将数据存储到 GraphicsBuffer(存储顶点 Vector3)时遇到了一个奇怪的问题,我将其用作计算着色器的“输出” .
ComputeShader 组件的分配:
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);
}
outVertexBuffer 的初始化:(stride 等于 12,因为类型为 float3)
outVertexBuffer =
new GraphicsBuffer(GraphicsBuffer.Target.Vertex, 5000000, 12);
在 LaplacianSmoothing ComputeShader 中,我想重新计算顶点的位置并使用 Store3 函数将它们存储到 outVertexBuffer 中。 下面是 ComputeShader 中 LaplacianSmoothing 的代码(HLSL 风格):
#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));
}
}
下一步我想从 outVertexBuffer 获取数据并将它们赋给 vertexArray GraphicsBuffer.GetData(Vector3[] array) 函数。
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}%");
}
我在getData函数后设置了断点,查看数据,都是(0,0,0)。 我使用 RenderDoc 检查缓冲区的工作方式,用谷歌搜索着色器和主程序中使用的所有函数的文档,但没有任何解释缺少分配数据。 我想补充一点,当我也使用 inputBuffer 作为输出时,我会得到数据(逻辑上不正确,但仍然不是 (0,0,0)。
试试这个:
outVertexBuffer = new GraphicsBuffer(GraphicsBuffer.Target.Structured, 5000000, 12);
请注意更改后的目标。图形缓冲区需要至少一个计算目标标志才能绑定到计算内核。 Target.Vertex 不是这样的标志。您仍然可以添加它,但在您的情况下不需要。
事实上 outVertexBuffer
可能只是一个 ComputeBuffer
。无需使用 GraphicsBuffer
,因为您只需将其复制到 CPU 侧的网格。
编辑---
另一件事:
SetVertexBufferData()
要求您首先配置所有顶点属性,您传递的数据应该基本上是原始顶点数据:位置、法线、uvs(如果有)等。
如果您只想设置顶点,也许使用 Mesh.SetVertices()
.