三维几何

修改Vertex结构体,使用三维向量来表示顶点的位置信息,更新对应的VkVertexInputAttributeDescription结构体的format成员变量设置:

struct Vertex {
    glm::vec3 pos;
    glm::vec3 color;
    glm::vec2 texCoord;

        ...

    static std::array<VkVertexInputAttributeDescription, 3> getAttributeDescriptions() {
        std::array<VkVertexInputAttributeDescription, 3>
        attributeDescriptions = {};

        attributeDescriptions[0].binding = 0;
        attributeDescriptions[0].location = 0;
        attributeDescriptions[0].format = VK_FORMAT_R32G32B32_SFLOAT;
        attributeDescriptions[0].offset = offsetof(Vertex, pos);

        ...
    }
};

接着,修改顶点着色器接收三维顶点位置输入。最后不要忘记重新编译修改后的顶点着色器代码:

layout(location = 0) in vec3 inPosition;

        ...

void main() {
    gl_Position = ubo.proj * ubo.view * ubo.model * vec4(inPosition, 1.0);
    fragColor = inColor;
    fragTexCoord = inTexCoord;
}

更新vertices中的顶点数据,添加z坐标信息:

const std::vector<Vertex> vertices = {
{{-0.5f, -0.5f, 0.0f}, {1.0f, 0.0f, 0.0f}, {0.0f, 0.0f}},
{{0.5f, -0.5f, 0.0f}, {0.0f, 1.0f, 0.0f}, {1.0f, 0.0f}},
{{0.5f, 0.5f, 0.0f}, {0.0f, 0.0f, 1.0f}, {1.0f, 1.0f}},
{{-0.5f, 0.5f, 0.0f}, {1.0f, 1.0f, 1.0f}, {0.0f, 1.0f}}
};

现在如果编译运行程序,我们会看到和之前一样的渲染结果。为了显示深度缓冲解决的问题,我们再添加一个矩形的数据:

const std::vector<Vertex> vertices = {
{{-0.5f, -0.5f, 0.0f}, {1.0f, 0.0f, 0.0f}, {0.0f, 0.0f}},
{{0.5f, -0.5f, 0.0f}, {0.0f, 1.0f, 0.0f}, {1.0f, 0.0f}},
{{0.5f, 0.5f, 0.0f}, {0.0f, 0.0f, 1.0f}, {1.0f, 1.0f}},
{{-0.5f, 0.5f, 0.0f}, {1.0f, 1.0f, 1.0f}, {0.0f, 1.0f}},

{{-0.5f, -0.5f, -0.5f}, {1.0f, 0.0f, 0.0f}, {0.0f, 0.0f}},
{{0.5f, -0.5f, -0.5f}, {0.0f, 1.0f, 0.0f}, {1.0f, 0.0f}},
{{0.5f, 0.5f, -0.5f}, {0.0f, 0.0f, 1.0f}, {1.0f, 1.0f}},
{{-0.5f, 0.5f, -0.5f}, {1.0f, 1.0f, 1.0f}, {0.0f, 1.0f}}
};

const std::vector<uint16_t> indices = {
            0, 1, 2, 2, 3, 0,
            4, 5, 6, 6, 7, 4
};

新添加的矩形和之前的矩形除了z坐标相差0.5f外,其它数据完全一样。另外我们将第二矩形使用的顶点索引数据添加到了indices数组中。

image

image

现在编译运行程序,应该可以看到类似上图的画面。

我们会发现较低的矩形却绘制在了较高的矩形上面,这是因为较低矩形的索引数据在较高矩形的索引数据之后出现,绘图调用在绘制完较高的矩形后才绘制较低的矩形。

有两种方式可以解决这个问题:

  • 按照深度坐标对绘制调用进行排序

  • 使用深度测试

通常我们使用第一种方式来绘制需要透明的对象。使用第二种方式来绘制一般对象。使用第二种方式需要用到深度缓冲。深度缓冲是用于存储片段深度值的缓冲对象。光栅化后生成的片段包含了一个深度值可以被深度测试检查是否可以使用这一片段覆盖之前的数据。我们可以在片段着色器对深度值进行处理。

#define GLM_FORCE_RADIANS
#define GLM_FORCE_DEPTH_ZERO_TO_ONE

#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>

默认情况下,GLM库的透视投影矩阵使用OpenGL的深度值范围(-1.0,1.0)。我们需要定义GLM_FORCE_DEPTH_ZERO_TO_ONE宏来让它使用Vulkan的深度值范围(0.0,1.0)。