你能在着色器中修改制服吗?如果是这样。怎么样?

时间:2016-04-28 09:33:51

标签: c++ opengl vertex-shader

所以我想将所有网格存储在一个大的VBO中。问题是,你怎么做只有一个绘图调用,但让每个网格都有自己的模型到世界矩阵?

我的想法是在绘制前向制服提交一系列矩阵。在VBO中,我会使网格的每个第一个顶点的颜色为负(所以我使用签名位来检查顶点是否是网格的第一个)。

好的,所以我可以检测到一个新网格何时开始并且我已经准备好了一系列矩阵,并且可能是一个名为' index'的统一体。但是,每当遇到新网格时,如何将此索引增加1?

您可以在着色器中修改制服吗?如果是这样,怎么样?

2 个答案:

答案 0 :(得分:4)

  

您可以在着色器中修改制服吗?

如果可以的话,它将不再是制服,是吗?

此外,即使使用图像加载/存储或SSBO也无法完成您想要做的事情,这两者都允许着色器写入数据。它不起作用,因为不需要按顺序顺序执行顶点着色器调用。很多都是在同一时间发生的,并且任何着色器调用都无法知道它会发生"在"之后#34; "第一个顶点"在网格中。

解决这个问题的最简单方法是明显的解决方案。单独渲染每个网格,但在每次绘制调用之前为每个网格设置制服。当然,不需要在绘制之间更改缓冲区。统一的变化虽然不是很便宜,但并不是最昂贵的状态变化。

有更复杂的绘图方法可以让您获得更高的性能。但这种形式足以满足大多数需求。您已经完成了困难的部分:除了统一状态之外,您不再需要任何状态更改(纹理,缓冲区,顶点格式等)。

答案 1 :(得分:1)

最小化绘制调用有两种方法 - 实例化和批处理。第一个(实例化)允许您在一次绘制调用中绘制相同网格的多个副本,但它取决于API(可从OpenGL 3.1获得)。批处理类似于实例化,但允许您绘制不同的网格。这两种方法都有限制 - 网格应该使用相同的材​​质和着色器。

如果你想在一个VBO中绘制不同的网格,那么实例化不是一个选项。因此,批量处理需要将所有网格保持在“大”状态。 VBO与应用世界变换。它不是静态网格物体的问题,但对动画有一些不适。我给你一些带有批处理实现的伪代码

struct SGeometry
{
    uint64_t offsetVB;
    uint64_t offsetIB;
    uint64_t sizeVB;
    uint64_t sizeIB;

    glm::mat4 oldTransform;
    glm::mat4 transform;
}
std::vector<SGeometry> cachedGeometries;

...


void CommitInstances()
{
    uint64_t vertexOffset = 0;
    uint64_t indexOffset = 0;

    for (auto instance in allInstances)
    {
        Copy(instance->Vertexes(), VBO);

        for (uint64_t i = 0; i < instances->Indices().size(); ++i)
        {
            auto index = instances->Indices()[i];
            index += indexOffset;
            IBO[i] = index;
        }

        cachedGeometries.push_back({vertexOffset, indexOffset});

        vertexOffset += instance->Vertexes().size();
        indexOffset += instance->Indices().size();
    }

    Commit(VBO);
    Commit(IBO);
}

void ApplyTransform(glm::mat4 modelMatrix, uint64_t instanceId)
{
    const SGeometry& geom = cachedGeometries[i];

    glm::mat4 inverseOldTransform = glm::inverse(geom.oldTransform);

    VertexStream& stream = VBO->GetStream(Position, geom.offsetVB);
    for (uint64_t i = 0; i < geom.sizeVB; ++i)
    {
        glm::vec3 pos = stream->Get(i);
        // We need to revert absolute transformation before applying new
        pos = glm::vec3(inverseOldNormalTransform * glm::vec4(pos, 1.0f));
        pos = glm::vec3(normalTransform * glm::vec4(pos, 1.0f));
        stream->Set(i);
    }

    // .. Apply normal transformation
}

GPU Gems 2有一篇关于几何实例化的好文章http://www.amazon.com/GPU-Gems-Programming-High-Performance-General-Purpose/dp/0321335597