问题:
我正在尝试绘制一个场景,其中包含很多(大约10000个)相似的对象(在我的情况下是灌木丛,但可能是任何东西)。对于每个灌木丛,我有三个具有不同三角形数量的网格,并且我想根据距相机的距离使用这些网格之一。

第一种方法:
我可以选择正确的在CPU上划分网格,在每个衬套之后更新模型矩阵,然后使用正确的网格绘制VBO。问题是我需要每帧更新10000次模型矩阵...您可以猜测这对性能有何影响。

第二种方法:
再次在CPU上选择正确的网格,但是这次使用实例渲染在一次绘制调用中绘制所有具有最低lod的灌木丛,然后绘制具有中等lod的灌木丛,然后绘制具有高lod的灌木丛。我已经将灌木丛位置存储在GPU上了。但是我的问题是,我仍然需要发送一个数组作为统一变量,其中包含必须在哪个位置绘制网格的信息。通过这种方法,我遇到了统一变量的限制。

问题:
我想做的是计算从灌木丛到GPU上相机的距离(对于顶点着色器中的示例)。然后根据结果仅绘制具有正确详细程度的VBO。因此,我的问题是,是否有可能选择要在GPU上绘制哪个VBO(我看不到有这样做的可能性,因为在“顶点和片段”着色器中,我已经准备好了很多)?还是您有其他想法可以尝试?

最好的阿尔伯特

评论

如果将顶点输出中的w分量设置为0,则三角形所在的三角形不会被推下栅格化器。如果瓶颈是顶点处理,那可能没有帮助。

感谢您的回答。但是,如果我理解正确,则需要使用每个LOD渲染每个位置,然后检查每个顶点到相机的距离,如果w属于该位置的错误LOD,则将w设置为0。如果这是正确的,那么我认为这不会有帮助,因为对每个位置上具有最高LOD的所有顶点进行此测试都会导致大量的测试。即使我没有将大部分这些顶点发送到栅格化器,我也不认为这会起作用,但是如果我找不到更好的解决方案,我还是会尝试一下,并在此处评论我的结果。

您是否考虑过一个不完善的解决方案,可以在多个周期内分散10000个灌木的LOD更新?我怀疑您的看法仍然会改变。而且,如果变化很大,通常不会引起一点延迟。

我没有尝试过,因为在我的情况下,通过使用实例数组(请参见接受的答案下的评论)获得的性能已足够。但是,如果需要更高的性能,可以将其与实例数组结合使用。

#1 楼

IMO,您第二种方法的变化将是最简单的,并且可能会很快。

您是否概要介绍了任何内容? CPU应该非常快地咀嚼10000个矩阵乘法。如果您想获得更高的性能,则可以对更新进行SIMD /线程化。也就是说,最慢的部分可能是通过PCIE总线发送10000个矩阵的带宽。

但是,这又都是假设。试试看,然后进行配置。



我在想这样的事情:

(我为DX函数道歉。我不是。非常熟悉相应的OpenGL函数。也就是说,OpenGL应该非常相似。

// Variable definitions

vector<matrix> lod0;
vector<matrix> lod1;
vector<matrix> lod2;

ID3D11Buffer lod0VertexBuffer;
uint lod0VertexCount;

ID3D11Buffer lod1VertexBuffer;
uint lod1VertexCount;

ID3D11Buffer lod2VertexBuffer;
uint lod2VertexCount;

ID3D11Buffer instanceBuffer;

/// ..................


/// Per frame...
lod0.clear();
lod1.clear();
lod2.clear();

for (uint i = 0; i < bushes.size(); ++i) {
    float distance = distance(bushes[i].pos, camera.origin);
    if (distance < 50.0f) {
        lod0.push_back(bushes[i].matrix);
    } else if (distance < 100.0f) {
        lod1.push_back(bushes[i].matrix);
    } else {
        lod2.push_back(bushes[i].matrix);
    }
}

D3D11_MAPPED_SUBRESOURCE mappedResource;
ID3D11Buffer vertexBuffers[] = {
    lod0VertexBuffer,
    instanceBuffer
};

// Update lod0 matrices
context->Map(instanceBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedResource);
memcpy(mappedResource.pData, &lod0[0], sizeof(matrix) * lod0.size());
context->Unmap(instanceBuffer, 0);

// Draw lod0
context->IASetVertexBuffers(0, 2, vertexBuffers, nullptr, nullptr);
context->DrawInstanced(lod0VertexCount, lod0.size(), 0, 0);


// Update lod1 matrices
context->Map(instanceBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedResource);
memcpy(mappedResource.pData, &lod1[0], sizeof(matrix) * lod1.size());
context->Unmap(instanceBuffer, 0);

// Draw lod1
vertexBuffers[0] = lod1VertexBuffer;
context->IASetVertexBuffers(0, 2, vertexBuffers, nullptr, nullptr);
context->DrawInstanced(lod1VertexCount, lod1.size(), 0, 0);


// Update lod2 matrices
context->Map(instanceBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedResource);
memcpy(mappedResource.pData, &lod2[0], sizeof(matrix) * lod1.size());
context->Unmap(instanceBuffer, 0);

// Draw lod2
vertexBuffers[0] = lod2VertexBuffer;
context->IASetVertexBuffers(0, 2, vertexBuffers, nullptr, nullptr);
context->DrawInstanced(lod1VertexCount, lod2.size(), 0, 0);


如果这样做太慢,则可以尝试在计算着色器,然后使用间接绘制调用绘制结果。请参阅AZDO。

评论


$ \ begingroup $
感谢您的回答和广泛的代码示例。我不熟悉DX,但是它为我指明了正确的方向。
$ \ endgroup $
–AlbertM
16年6月28日在0:27

$ \ begingroup $
并且您认为pcie总线是瓶颈的假设似乎是正确的(在我的硬件上,我现在可以渲染大约300k的灌木丛,并且对于中等和粗略的LOD,我得到相同的帧速率,因此顶点的数量是而非限制因素)。对于有相同问题的任何人:使用实例数组对我来说是成功的窍门(此处的解释很好:learningopengl.com/#!Advanced-OpenGL/Instancing)。因此,您不会遇到制服的限制,这是我第二种方法的问题。
$ \ endgroup $
–AlbertM
16年6月28日在0:33