这个系列文章我们来介绍一位海外工程师如何探索 Vulkan 音视频技术,对于想要开始学习音视频技术的朋友,这些文章是份不错的入门资料,本篇介绍 Vulkan GLSL 着色器指南。
——来自公众号“关键帧Keyframe”的分享
着色器是任何 Vulkan 应用程序的核心,定义了图形和计算操作如何在 GPU 上执行。在该仓库中,GLSL(OpenGL 着色语言)着色器被系统地组织并编译为 SPIR-V,以供 Vulkan 使用。本指南将带你了解着色器结构、编译工作流以及整个示例中使用的集成模式。
该仓库维护了一个组织良好的着色器结构,镜像了示例的组织结构。所有 GLSL 着色器位于 shaders/glsl/ 目录中,每个示例都有自己的子目录,包含必要的着色器文件。这种模块化方法使得定位和了解特定于每种技术的着色器变得容易。
来源:shaders/README.md, shaders/glsl/
1、基本着色器结构
该仓库中的 GLSL 着色器遵循 Vulkan 的现代着色实践,具有清晰的输入/输出布局和统一缓冲绑定。让我们检查三角形示例中的基本顶点-片段着色器对:
1.1、顶点着色器
顶点着色器处理逐顶点处理和变换。典型的顶点着色器包括:
- 版本声明(
#version 450) - 顶点数据的输入属性布局
- 变换矩阵的统一缓冲布局
- 输出变量以将数据传递给片段着色器
- 执行顶点变换的主函数
#version 450
layout (location = 0) in vec3 inPos;
layout (location = 1) in vec3 inColor;
layout (binding = 0) uniform UBO
{
mat4 projectionMatrix;
mat4 modelMatrix;
mat4 viewMatrix;
} ubo;
layout (location = 0) out vec3 outColor;
out gl_PerVertex
{
vec4 gl_Position;
};
void main()
{
outColor = inColor;
gl_Position = ubo.projectionMatrix * ubo.viewMatrix * ubo.modelMatrix * vec4(inPos.xyz, 1.0);
}
来源:shaders/glsl/triangle/triangle.vert
1.2、片段着色器
片段着色器处理逐像素操作并确定最终颜色输出:
#version 450
layout (location = 0) in vec3 inColor;
layout (location = 0) out vec4 outFragColor;
void main()
{
outFragColor = vec4(inColor, 1.0);
}
来源:shaders/glsl/triangle/triangle.frag
2、着色器编译工作流
GLSL 着色器必须先编译为 SPIR-V(标准可移植中间表示),然后才能在 Vulkan 中使用。该仓库提供了一个 Python 脚本,使用 glslangValidator 自动化编译过程。
2.1、支持的着色器类型
编译脚本通过文件扩展名识别几种着色器类型:
| 扩展名 | 着色器类型 | 描述 |
|---|---|---|
.vert | 顶点着色器 | 处理顶点属性和变换 |
.frag | 片段着色器 | 处理像素颜色并输出 |
.comp | 计算着色器 | 通用 GPU 计算 |
.geom | 几何着色器 | 处理图元并生成新几何体 |
.tesc | 曲面细分控制着色器 | 控制曲面细分级别 |
.tese | 曲面细分评估着色器 | 评估曲面细分的面片 |
.rgen | 光线生成着色器 | 为光线追踪生成光线 |
.rchit | 最近命中着色器 | 处理光线-对象相交 |
.rmiss | 未命中着色器 | 处理错过所有对象的光线 |
.mesh | 网格着色器 | 在网格管线中处理网格块 |
.task | 任务着色器 | 在网格工作中放大任务 |
2.2、编译过程
compileshaders.py 脚本使用适当的 Vulkan 版本要求处理着色器编译:
# 要编译的文件扩展名
file_extensions = tuple([".vert", ".frag", ".comp", ".geom", ".tesc", ".tese",
".rgen", ".rchit", ".rmiss", ".mesh", ".task"])
# 高级着色器的特殊要求
if file.endswith(".rgen") or file.endswith(".rchit") or file.endswith(".rmiss"):
add_params = add_params + " --target-env vulkan1.2"
if root.endswith("rayquery") and file.endswith(".frag"):
add_params = add_params + " --target-env vulkan1.2"
if file.endswith(".mesh") or file.endswith(".task"):
add_params = add_params + " --target-env spirv1.4"
来源:shaders/glsl/compileshaders.py
3、高级着色器模式
3.1、计算着色器
计算着色器支持通用 GPU 计算。粒子模拟示例演示了典型的计算着色器模式:
#version 450
struct Particle
{
vec2 pos;
vec2 vel;
vec4 gradientPos;
};
// 粒子数据的存储缓冲区
layout(std140, binding = 0) buffer Pos
{
Particle particles[ ];
};
// 工作组大小定义
layout (local_size_x = 256) in;
// 参数的统一缓冲区
layout (binding = 1) uniform UBO
{
float deltaT;
float destX;
float destY;
int particleCount;
} ubo;
void main()
{
// 当前 SSBO 索引
uint index = gl_GlobalInvocationID.x;
// 不要尝试写入超出粒子数量的范围
if (index >= ubo.particleCount)
return;
// 读取位置和速度
vec2 vVel = particles[index].vel.xy;
vec2 vPos = particles[index].pos.xy;
// 模拟逻辑...
// 写回更新的值
particles[index].vel.xy = vVel;
particles[index].gradientPos.x += 0.02 * ubo.deltaT;
}
计算着色器中的关键模式包括:
- 用于输入/输出数据的存储缓冲区绑定
- 工作组大小定义(
layout(local_size_x = ...)) - 用于参数的统一缓冲区
- 用于线程标识的全局调用 ID
- 边界检查以防止越界访问
来源:shaders/glsl/computeparticles/particle.comp
3.2、光线追踪着色器
光线追踪着色器使用专门的扩展并遵循与传统图形着色器不同的模式:
#version 460
#extension GL_EXT_ray_tracing : enable
layout(binding = 0, set = 0) uniform accelerationStructureEXT topLevelAS;
layout(binding = 1, set = 0, rgba8) uniform image2D image;
layout(binding = 2, set = 0) uniform CameraProperties
{
mat4 viewInverse;
mat4 projInverse;
} cam;
layout(location = 0) rayPayloadEXT vec3 hitValue;
void main()
{
const vec2 pixelCenter = vec2(gl_LaunchIDEXT.xy) + vec2(0.5);
const vec2 inUV = pixelCenter/vec2(gl_LaunchSizeEXT.xy);
vec2 d = inUV * 2.0 - 1.0;
vec4 origin = cam.viewInverse * vec4(0,0,0,1);
vec4 target = cam.projInverse * vec4(d.x, d.y, 1, 1) ;
vec4 direction = cam.viewInverse*vec4(normalize(target.xyz), 0) ;
float tmin = 0.001;
float tmax = 10000.0;
hitValue = vec3(0.0);
traceRayEXT(topLevelAS, gl_RayFlagsOpaqueEXT, 0xff, 0, 0, 0, origin.xyz, tmin, direction.xyz, tmax, 0);
imageStore(image, ivec2(gl_LaunchIDEXT.xy), vec4(hitValue, 0.0));
}
光线追踪着色器包括:
- 光线生成着色器(
.rgen)发起光线 - 最近命中着色器(
.rchit)处理相交 - 未命中着色器(
.rmiss)处理错过几何体的光线 - 加速结构绑定用于场景表示
- 用于着色器阶段之间通信的光线有效载荷变量
来源:shaders/glsl/raytracingbasic/raygen.rgen, shaders/glsl/raytracingbasic/closesthit.rchit
4、着色器加载和集成
着色器通过基础示例框架加载到 Vulkan 应用程序中。该过程通常涉及:
- 从文件系统加载预编译的 SPIR-V 文件
- 从 SPIR-V 数据创建 Vulkan 着色器模块
- 将着色器模块集成到管线创建中
基础框架提供了加载着色器的实用程序,每个示例都实现了其特定的管线创建逻辑。着色器文件 expected 与源 GLSL 文件在同一目录结构中,带有 .spv 扩展名。
5、最佳实践
在处理该仓库中的 GLSL 着色器时,请遵循以下最佳实践:
- 使用显式布局:始终为输入/输出指定
location,为资源指定binding - 版本兼容性:为基本着色器使用
#version 450,为光线追踪使用#version 460 - 扩展要求:仅包含着色器实际使用的扩展
- 资源绑定:遵循每个示例中建立的绑定约定
- 着色器专业化:对运行时可配置的值使用专业化常量
- 调试符号:在开发期间使用
-g标志编译以获得更好的错误报告
学习和提升音视频开发技术,推荐你加入我们的知识星球:【关键帧的音视频开发圈】

版权声明:本文内容转自互联网,本文观点仅代表作者本人。本站仅提供信息存储空间服务,所有权归原作者所有。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至1393616908@qq.com 举报,一经查实,本站将立刻删除。