关于 shader-programming-glsl
面向使用 GLSL 编写 GPU 着色器的开发者,介绍着色器结构、变量、向量运算和渲染实践。内容还包括 WebGL 场景、效果示例、性能建议及编译问题排查。
主要功能
- 01讲解顶点与片段着色器
- 02说明 uniform 与 varying
- 03介绍向量运算与分量重排
- 04提供光线步进示例
- 05涵盖编译排查与性能建议
适用场景
- 01为 WebGL 或游戏引擎制作视觉效果
- 02实现模糊、泛光或色彩校正
- 03在 GPU 上生成纹理或几何体
- 04排查着色器编译或黑屏问题
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ sickn33/ agentic-awesome-skills --skill shader-programming-glsl安装后在 Claude Code 中输入/shader-programming-glsl确认已加载。
详细介绍
Shader 编程 GLSL
概述
这是一份使用 GLSL(OpenGL 着色语言)编写 GPU 着色器的综合指南。了解语法、uniform、varying 变量,以及混合向量分量和向量运算等关键数学概念,从而实现视觉效果。
何时使用此技能
- 用于在 WebGL、Three.js 或游戏引擎中创建自定义视觉效果。
- 用于优化图形渲染性能。
- 用于实现后期处理效果(模糊、泛光、颜色校正)。
- 用于在 GPU 上以程序化方式生成纹理或几何体。
分步指南
1. 结构:顶点着色器与片元着色器
以下基本语法面向 GLSL ES 1.00/WebGL 1。宿主引擎可能会注入矩阵/属性;原生 WebGL 必须显式绑定它们。GLSL ES 3.00 使用 in/out 和声明的片元输出,而不是 attribute/varying/gl_FragColor。分别编译顶点着色器和片元着色器,将它们链接为一个程序,并检查编译/链接日志。
了解渲染管线:
- 顶点着色器:将 3D 坐标转换为 2D 屏幕空间坐标(
gl_Position)。 - 片元着色器:为单个像素着色(
gl_FragColor)。
// Vertex Shader (basic)
attribute vec3 position;
uniform mat4 modelViewMatrix;
uniform mat4 projectionMatrix;
void main() {
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}
// Fragment Shader (basic)
precision mediump float;
uniform vec3 color;
void main() {
gl_FragColor = vec4(color, 1.0);
}
2. Uniform 和 Varying
uniform:所有顶点/片元共享的常量数据(由 CPU 传入)。varying:从顶点着色器插值传递到片元着色器的数据。
// Separate vertex and fragment shader snippets; both declare varying vec2 vUv.
// Vertex shader also needs attribute vec2 uv and the matrices/position above.
// Raw WebGL fragment shader needs a float precision declaration.
varying vec2 vUv;
// In Vertex Shader
void main() {
vUv = uv;
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}
// In Fragment Shader
void main() {
// Gradient based on UV
gl_FragColor = vec4(vUv.x, vUv.y, 1.0, 1.0);
}
3. 分量混合与向量运算
可自由访问向量分量:vec4 color = vec4(1.0, 0.5, 0.0, 1.0);
color.rgb->vec3(1.0, 0.5, 0.0)color.zyx->vec3(0.0, 0.5, 1.0)(重新排序)
示例
示例 1:简单光线步进(SDF 球体)
这是 Shadertoy 风格的片元入口点,使用宿主提供的 iResolution;在原生 WebGL 中需要宿主包装器。
float sdSphere(vec3 p, float s) {
return length(p) - s;
}
void mainImage(out vec4 fragColor, in vec2 fragCoord) {
vec2 uv = (fragCoord - 0.5 * iResolution.xy) / iResolution.y;
vec3 ro = vec3(0.0, 0.0, -3.0); // Ray Origin
vec3 rd = normalize(vec3(uv, 1.0)); // Ray Direction
float t = 0.0;
bool hit = false;
for(int i = 0; i < 64; i++) {
vec3 p = ro + rd * t;
float d = sdSphere(p, 1.0); // Sphere radius 1.0
if(d < 0.001) { hit = true; break; }
t += d;
if(t > 10.0) break;
}
vec3 col = vec3(0.0);
if(hit) {
vec3 p = ro + rd * t;
vec3 normal = normalize(p);
col = normal * 0.5 + 0.5; // Color by normal
}
fragColor = vec4(col, 1.0);
}
最佳实践
- ✅ 应做: 使用
mix()进行线性插值,而不是手动计算。 - ✅ 应做: 使用
step()和smoothstep()进行阈值判断和柔和边缘处理(避免if分支)。 - ✅ 应做: 将数据打包到向量(
vec4)中,以尽量减少内存访问。 - ❌ 不应做: 尽可能避免在循环中使用繁重的分支(
if-else);这会损害 GPU 并行性。 - ❌ 不应做: 在着色器中计算常量;应在 CPU 上预先计算(作为 uniform)。
故障排除
问题: 着色器编译成功,但屏幕是黑色的。
解决方案: 检查完整的裁剪空间变换和齐次坐标 gl_Position.w;透视投影通常不会产生 w = 1。检查宿主应用是否确实设置了 uniform。确认 UV 坐标位于 [0, 1] 范围内。
关于 GLSL ES 片元精度的可移植性,请参阅 Khronos 指南。请在目标 GPU 上分析分支/向量优化的性能;这些风格建议并不保证能提升速度。
局限性
- 仅当任务明确符合上述范围时才使用此技能。
- 不要将输出视为对特定环境验证、测试或专家审查的替代。
- 如果缺少必要的输入、权限、安全边界或成功标准,请暂停并请求澄清。
评论
支持 Markdown