多种顶点类型的着色器设计

Shader design for multiple vertex types

我正在编写一个 OpenGL 库,我偶然发现了一个关于多顶点类型和顶点着色器的问题。我是否需要为每个处理其属性的新顶点类型编写一个新的 vertex/fragment 着色器?或者我是否需要编写一个单独的 vertex/fragment 着色器来处理所有可能的属性?

这些是我用于顶点类型的一些基本 class "patterns"。

 struct simple_vertex
{
    glm::vec3 position;

    simple_vertex(glm::vec3 pos) {
        position = pos;
    }

    simple_vertex() {
        position = glm::vec3(0, 0, 0);
    }


    static void enable_attributes() {
        glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(simple_vertex),
                              (const GLvoid *) offsetof(simple_vertex, position));
        glEnableVertexAttribArray(0);
    }


    glm::vec3 get_position() const {
        return position;
    }
};

struct colored_vertex {//vertex that holds position and color data

    glm::vec3 position;
    glm::vec3 color;

    colored_vertex(glm::vec3 pos, glm::vec3 c) {
        color = c;
        position = pos;
    }

    colored_vertex() {
        color = glm::vec3(0, 0, 0);
        position = glm::vec3(0, 0, 0);
    }

    static void enable_attributes() {
        glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(colored_vertex),
                              (const GLvoid *) offsetof(colored_vertex, position));
        glEnableVertexAttribArray(0);
        glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(colored_vertex),
                              (const GLvoid *) offsetof(colored_vertex, color));
        glEnableVertexAttribArray(1);

    }

    glm::vec3 get_position() const {
        return position;
    }


};


struct textured_vertex {//vertex that holds position and texture coordinates

    glm::vec3 position;
    glm::vec2 texture_coords;

    textured_vertex(glm::vec3 pos, glm::vec2 text_coords) {
        texture_coords = text_coords;
        position = pos;
    }

    textured_vertex() {
        texture_coords = glm::vec2(0, 0);
        position = glm::vec3(0, 0, 0);
    }

    static void enable_attributes() {
        glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(textured_vertex),
                              (const GLvoid *) offsetof(textured_vertex, position));
        glEnableVertexAttribArray(0);

        glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(textured_vertex),
                              (const GLvoid *) offsetof(textured_vertex, texture_coords));
        glEnableVertexAttribArray(1);

    }

    glm::vec3 get_position() const {
        return position;
    }


};


struct normal_textured_vertex {//vertex that holds position normal and texture coordinates

    glm::vec3 position;
    glm::vec2 texture_coords;
    glm::vec3 normal;

    normal_textured_vertex(glm::vec3 pos, glm::vec2 text_coords, glm::vec3 n) {
        texture_coords = text_coords;
        position = pos;
        normal = n;
    }

    normal_textured_vertex() {
        texture_coords = glm::vec2(0, 0);
        position = glm::vec3(0, 0, 0);
        normal = glm::vec3(0, 0, 0);

    }

    static void enable_attributes() {
        glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(normal_textured_vertex),
                              (const GLvoid *) offsetof(normal_textured_vertex, position));
        glEnableVertexAttribArray(0);

        glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(normal_textured_vertex),
                              (const GLvoid *) offsetof(normal_textured_vertex, texture_coords));
        glEnableVertexAttribArray(1);

        glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, sizeof(normal_textured_vertex),
                              (const GLvoid *) offsetof(normal_textured_vertex, normal));
        glEnableVertexAttribArray(2);

    }

    glm::vec3 get_position() const {
        return position;
    }


};

正确的解决方案是选项 3:不要有大量具有截然不同的顶点格式的网格。 "vertex format" 是指网格提供的一组属性(包括它们在缓冲区中的编码方式)。

一般来说,您应该确定一组相当有限的顶点格式并调整网格(离线)以适应这些格式。您可能有一种用于非蒙皮网格的格式、一种用于蒙皮网格的格式、一种用于 GUI 对象的格式、一种用于粒子的格式,也许还有一种或两种其他格式。

如果您编写的应用程序无法控制所给数据的形式并且必须使用任何数据,即便如此我还是建议为数据未提供的属性创建无害的数据。例如,如果有人给你一个有位置和 UV 但没有颜色的网格,创建只是重复值 (1.0, 1.0, 1.0, 1.0) 的颜色数据。您的照明方程式应该可以很好地处理该颜色。如果有人给你一个有位置和颜色但没有纹理坐标的网格,创建仅为 0 的 UV 值(并且应该给它一个小的白色纹理以从中采样)。等等

不要根据数据调整代码;根据您的代码调整您的数据。