Vulkan shader编译

大家好,接下来将为大家介绍Vulkan shader编译。

一、编译shader

在项目根目录下创建一个子目录,名shaders用于存储顶点着色器文件shader.vert和片段着色器文件shader.frag。GLSL着色器官方没有约定的扩展名,但是这两个扩展名是比较普遍通用的。

shader.vert内容如下:

#version 450
#extension GL_ARB_separate_shader_objects : enable

out gl_PerVertex {
    vec4 gl_Position;
};

layout(location = 0) out vec3 fragColor;

vec2 positions[3] = vec2[](
    vec2(0.0, -0.5),
    vec2(0.5, 0.5),
    vec2(-0.5, 0.5)
);

vec3 colors[3] = vec3[](
    vec3(1.0, 0.0, 0.0),
    vec3(0.0, 1.0, 0.0),
    vec3(0.0, 0.0, 1.0)
);

void main() {
    gl_Position = vec4(positions[gl_VertexIndex], 0.0, 1.0);
    fragColor = colors[gl_VertexIndex];
}

shader.frag文件内容如下:

#version 450
#extension GL_ARB_separate_shader_objects : enable

layout(location = 0) in vec3 fragColor;

layout(location = 0) out vec4 outColor;

void main() {
    outColor = vec4(fragColor, 1.0);
}

现在我们尝试使用glslangValidator程序编译SPIR-V二进制码。

Windows

创建一个compile.bat批处理文件,内容如下:

C:/VulkanSDK/1.0.17.0/Bin32/glslangValidator.exe -V shader.vert
C:/VulkanSDK/1.0.17.0/Bin32/glslangValidator.exe -V shader.frag
pause

glslangValidator.exe的path路径替换为你的VulkanSDK安装路径,然后双击该文件运行。

End of platform-specific instructions

这两个命令使用-V标志调用编译器,该标志告诉它将GLSL源文件编译为SPIR-V字节码。运行编译脚本时,会看到创建了两个SPIR-V二进制文件:vert.spvfrag.spv。这些名称从着色器中派生而来,但是可以重命名为任何名字。在编译着色器时,可能收到关于某些功能缺失的警告信息,在这里放心的忽略它们。

如果着色器包含语法错误,那么编译器会按照您的预期告诉具体的行号和问题。尝试省略一个分号,然后重新运行编译脚本。还可以尝试运行编译器,而无需任何参数来查看它支持哪些类型的标志。例如,它可以将字节码输出为可读的格式,以便准确了解着色器正在执行的操作以及在此阶段应用的任何优化。

二、加载shader

现在我们有一种产生SPIR-V着色器的方法,是时候加载它们到我们的程序中,以便在适当的时候插入到图形管线中。首先我们编写一个辅助函数用以加载二进制数据文件。

#include <fstream>

...

static std::vector<char> readFile(const std::string& filename) {
    std::ifstream file(filename, std::ios::ate | std::ios::binary);

    if (!file.is_open()) {
        throw std::runtime_error("failed to open file!");
    }
}

readFile函数将会从文件中读取所有的二进制数据,并用std::vector字节集合管理。我们使用两个标志用以打开文件:

  • ate:在文件末尾开始读取
  • binary:以二进制格式去读文件(避免字符格式的转义)

从文件末尾开始读取的优点是我们可以使用读取位置来确定文件的大小并分配缓冲区:

size_t fileSize = (size_t) file.tellg();
std::vector<char> buffer(fileSize);

之后我们可以追溯到文件的开头,同时读取所有的字节:

file.seekg(0);
file.read(buffer.data(), fileSize);

最后关闭文件,返回字节数据:

file.close();

return buffer;

我们调用函数createGraphicsPipeline加载两个着色器的二进制码:

void createGraphicsPipeline() {
    auto vertShaderCode = readFile("shaders/vert.spv");
    auto fragShaderCode = readFile("shaders/frag.spv");
}

确保着色器正确加载,并打印缓冲区的大小是否与文件实际大小一致。

发布了69 篇原创文章 · 获赞 77 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/u010281924/article/details/105407546
今日推荐