mcpField
G
glsl

glsl

github.com/martinholovsky设计与 UI48370 次安装47 周前更新
代码仓库

关于 glsl

面向为 JARVIS AI Assistant HUD 开发实时视觉效果的 GLSL 着色器技能,涵盖着色器模式、GPU 性能、安全与测试实践。

主要功能

  1. 01全息面板与能量场模式
  2. 02数据可视化着色器示例
  3. 03GPU 性能优化指导
  4. 04着色器与视觉回归测试
  5. 05GPU 安全实践

适用场景

  1. 01制作动画 HUD 面板与能量效果
  2. 02用自定义着色器渲染数据可视化
  3. 03测试着色器输出与性能
  4. 04避免 GPU 挂起和渲染瑕疵

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/martinholovsky/claude-skills-generator --skill glsl

安装后在 Claude Code 中输入/glsl确认已加载。

附带文件references/advanced-patterns.md

详细介绍

GLSL 着色器编程技能

文件组织:此技能采用拆分结构。高级着色器模式请参阅 references/。

1. 概览

此技能提供 GLSL 着色器专业知识,用于在 JARVIS AI Assistant HUD 中创建全息视觉效果。重点是面向实时渲染的高效 GPU 编程。

风险级别:低——GPU 端代码的攻击面有限,但可能导致性能问题

主要用例:

  • 带扫描线的全息面板效果
  • 动态能量场和粒子系统
  • 使用自定义渲染进行数据可视化
  • 后期处理效果(泛光、故障效果、色差)

2. 核心职责

2.1 基本原则
  1. 优先采用 TDD:实现前先编写视觉回归测试和着色器单元测试
  2. 关注性能:分析 GPU 性能,并针对 60 FPS 目标进行优化
  3. 精度很重要:使用适当的精度限定符以提升性能
  4. 避免分支:尽量减少着色器中的条件判断,提高 GPU 效率
  5. 优化数学运算:使用内置函数,避免开销较大的运算
  6. 确保 Uniform 安全:将 Uniform 输入发送到 GPU 前先进行验证
  7. 限定循环边界:始终使用常量循环边界,防止 GPU 卡死
  8. 优化内存访问:优化纹理查询和 varying 插值

3. 实现工作流(TDD)

3.1 第 1 步:先编写失败的测试
// tests/shaders/holographic-panel.test.ts
import { describe, it, expect, beforeEach } from 'vitest'
import { WebGLTestContext, captureFramebuffer, compareImages } from '../utils/webgl-test'

describe('HolographicPanelShader', () => {
  let ctx: WebGLTestContext

  beforeEach(() => {
    ctx = new WebGLTestContext(256, 256)
  })

  // Unit test: Shader compiles
  it('should compile without errors', () => {
    const shader = ctx.compileShader(holoFragSource, ctx.gl.FRAGMENT_SHADER)
    expect(shader).not.toBeNull()
    expect(ctx.getShaderErrors()).toEqual([])
  })

  // Unit test: Uniforms are accessible
  it('should have required uniforms', () => {
    const program = ctx.createProgram(vertSource, holoFragSource)
    expect(ctx.getUniformLocation(program, 'uTime')).not.toBeNull()
    expect(ctx.getUniformLocation(program, 'uColor')).not.toBeNull()
    expect(ctx.getUniformLocation(program, 'uOpacity')).not.toBeNull()
  })

  // Visual regression test
  it('should render scanlines correctly', async () => {
    ctx.renderShader(holoFragSource, { uTime: 0, uColor: [0, 0.5, 1], uOpacity: 1 })
    const result = captureFramebuffer(ctx)
    const baseline = await loadBaseline('holographic-scanlines.png')
    expect(compareImages(result, baseline, { threshold: 0.01 })).toBeLessThan(0.01)
  })

  // Edge case test
  it('should handle extreme UV values', () => {
    const testCases = [
      { uv: [0, 0], expected: 'no crash' },
      { uv: [1, 1], expected: 'no crash' },
      { uv: [0.5, 0.5], expected: 'no crash' }
    ]
    testCases.forEach(({ uv }) => {
      expect(() => ctx.renderAtUV(holoFragSource, uv)).not.toThrow()
    })
  })
})
3.2 第 2 步:实现最少代码以通过测试
// Start with minimal shader that passes tests
#version 300 es
precision highp float;

uniform float uTime;
uniform vec3 uColor;
uniform float uOpacity;

in vec2 vUv;
out vec4 fragColor;

void main() {
  // Minimal implementation to pass compilation test
  fragColor = vec4(uColor, uOpacity);
}
3.3 第 3 步:通过完整实现进行重构
// Expand to full implementation after tests pass
void main() {
  vec2 uv = vUv;
  float scanline = sin(uv.y * 100.0) * 0.1 + 0.9;
  float pulse = sin(uTime * 2.0) * 0.1 + 0.9;
  vec3 color = uColor * scanline * pulse;
  fragColor = vec4(color, uOpacity);
}
3.4 第 4 步:运行完整验证
# Run all shader tests
npm run test:shaders

# Visual regression tests
npm run test:visual -- --update-snapshots  # First time only
npm run test:visual

# Performance benchmark
npm run bench:shaders

# Cross-browser compilation check
npm run test:webgl-compat

4. 技术栈与版本

4.1 GLSL 版本
版本环境特性
GLSL ES 3.00WebGL 2.0现代特性,精度更高
GLSL ES 1.00WebGL 1.0兼容旧版
4.2 着色器设置
#version 300 es
precision highp float;
precision highp int;

// WebGL 2.0 shader header

5. 性能模式

5.1 避免分支——使用 Mix/Step
// ❌ BAD - GPU branch divergence
vec3 getColor(float value) {
  if (value < 0.3) {
    return vec3(1.0, 0.0, 0.0);  // Red
  } else if (value < 0.7) {
    return vec3(1.0, 1.0, 0.0);  // Yellow
  } else {
    return vec3(0.0, 1.0, 0.0);  // Green
  }
}

// ✅ GOOD - Branchless with mix/step
vec3 getColor(float value) {
  vec3 red = vec3(1.0, 0.0, 0.0);
  vec3 yellow = vec3(1.0, 1.0, 0.0);
  vec3 green = vec3(0.0, 1.0, 0.0);

  vec3 color = mix(red, yellow, smoothstep(0.3, 0.31, value));
  color = mix(color, green, smoothstep(0.7, 0.71, value));
  return color;
}
5.2 纹理图集——减少绘制调用
// ❌ BAD - Multiple texture bindings
uniform sampler2D uIcon1;
uniform sampler2D uIcon2;
uniform sampler2D uIcon3;

vec4 getIcon(int id) {
  if (id == 0) return texture(uIcon1, vUv);
  if (id == 1) return texture(uIcon2, vUv);
  return texture(uIcon3, vUv);
}

// ✅ GOOD - Single atlas texture
uniform sampler2D uIconAtlas;
uniform vec4 uAtlasOffsets[3];  // [x, y, width, height] for each icon

vec4 getIcon(int id) {
  vec4 offset = uAtlasOffsets[id];
  vec2 atlasUV = offset.xy + vUv * offset.zw;
  return texture(uIconAtlas, atlasUV);
}
5.3 细节层次(LOD)——基于距离的质量控制
// ❌ BAD - Same quality regardless of distance
const int NOISE_OCTAVES = 8;

float noise(vec3 p) {
  float result = 0.0;
  for (int i = 0; i < NOISE_OCTAVES; i++) {
    result += snoise(p * pow(2.0, float(i)));
  }
  return result;
}

// ✅ GOOD - Reduce octaves based on distance
uniform float uCameraDistance;

float noise(vec3 p) {
  // Fewer octaves when far away (detail not visible)
  int octaves = int(mix(2.0, 8.0, 1.0 - smoothstep(10.0, 100.0, uCameraDistance)));
  float result = 0.0;
  for (int i = 0; i < 8; i++) {
    if (i >= octaves) break;
    result += snoise(p * pow(2.0, float(i)));
  }
  return result;
}
5.4 Uniform 批处理——尽量减少 CPU 与 GPU 之间的数据传输
// ❌ BAD - Many individual uniforms
uniform float uPosX;
uniform float uPosY;
uniform float uPosZ;
uniform float uRotX;
uniform float uRotY;
uniform float uRotZ;
uniform float uScaleX;
uniform float uScaleY;
uniform float uScaleZ;

// ✅ GOOD - Packed into vectors/matrices
uniform vec3 uPosition;
uniform vec3 uRotation;
uniform vec3 uScale;
// Or even better:
uniform mat4 uTransform;
5.5 精度优化——使用适当的精度
// ❌ BAD - Everything highp (wastes GPU cycles)
precision highp float;

highp vec3 color;
highp float alpha;
highp vec2 uv;

// ✅ GOOD - Match precision to data needs
precision highp float;  // Default for calculations

mediump vec3 color;     // 0-1 range, mediump sufficient
mediump float alpha;    // 0-1 range
highp vec2 uv;          // Need precision for texture coords
lowp int flags;         // Boolean-like values
5.6 缓存纹理查询
// ❌ BAD - Redundant texture fetches
void main() {
  vec3 diffuse = texture(uTexture, vUv).rgb;
  // ... some code ...
  float alpha = texture(uTexture, vUv).a;  // Same lookup!
  // ... more code ...
  vec3 doubled = texture(uTexture, vUv).rgb * 2.0;  // Again!
}

// ✅ GOOD - Cache the result
void main() {
  vec4 texSample = texture(uTexture, vUv);
  vec3 diffuse = texSample.rgb;
  float alpha = texSample.a;
  vec3 doubled = texSample.rgb * 2.0;
}

6. 实现模式

6.1 全息面板着色器
// shaders/holographic-panel.frag
#version 300 es
precision highp float;

uniform float uTime;
uniform vec3 uColor;
uniform float uOpacity;
uniform vec2 uResolution;

in vec2 vUv;
out vec4 fragColor;

const int SCANLINE_COUNT = 50;

void main() {
  vec2 uv = vUv;

  // Scanline effect
  float scanline = 0.0;
  for (int i = 0; i < SCANLINE_COUNT; i++) {
    float y = float(i) / float(SCANLINE_COUNT);
    scanline += smoothstep(0.0, 0.002, abs(uv.y - y));
  }
  scanline = 1.0 - scanline * 0.3;

  // Edge glow
  float edge = 1.0 - smoothstep(0.0, 0.05, min(
    min(uv.x, 1.0 - uv.x),
    min(uv.y, 1.0 - uv.y)
  ));

  // Animated pulse
  float pulse = sin(uTime * 2.0) * 0.1 + 0.9;

  vec3 color = uColor * scanline * pulse;
  color += vec3(0.0, 0.5, 1.0) * edge * 0.5;

  fragColor = vec4(color, uOpacity);
}
6.2 能量场着色器
// shaders/energy-field.frag
#version 300 es
precision highp float;

uniform float uTime;
uniform vec3 uColor;

in vec2 vUv;
in vec3 vNormal;
in vec3 vViewPosition;
out vec4 fragColor;

float snoise(vec3 v) {
  return fract(sin(dot(v, vec3(12.9898, 78.233, 45.543))) * 43758.5453);
}

void main() {
  vec3 viewDir = normalize(-vViewPosition);
  float fresnel = pow(1.0 - abs(dot(viewDir, vNormal)), 3.0);
  float noise = snoise(vec3(vUv * 5.0, uTime * 0.5));

  vec3 color = uColor * fresnel;
  color += uColor * noise * 0.2;
  float alpha = fresnel * 0.8 + noise * 0.1;

  fragColor = vec4(color, alpha);
}
6.3 数据可视化着色器
// shaders/data-bar.frag
#version 300 es
precision highp float;

uniform float uValue;
uniform float uThreshold;
uniform vec3 uColorLow;
uniform vec3 uColorHigh;
uniform vec3 uColorWarning;

in vec2 vUv;
out vec4 fragColor;

void main() {
  float fill = step(vUv.x, uValue);
  vec3 color = mix(uColorLow, uColorHigh, uValue);
  color = mix(color, uColorWarning, step(uThreshold, uValue));
  float gradient = vUv.y * 0.3 + 0.7;
  fragColor = vec4(color * gradient * fill, fill);
}

7. 安全与性能标准

7.1 GPU 安全
风险缓解措施
无限循环始终使用常量循环边界
GPU 卡死先使用小型数据集测试着色器
内存耗尽限制纹理大小
7.2 循环安全模式
// ❌ BAD - Dynamic loop bound
for (int i = 0; i < int(uCount); i++) { }

// ✅ GOOD - Constant loop bound
const int MAX_ITERATIONS = 100;
for (int i = 0; i < MAX_ITERATIONS; i++) {
  if (i >= int(uCount)) break;
}

8. 常见错误与反模式

8.1 切勿:使用动态循环边界
// ❌ DANGEROUS - May cause GPU hang
for (int i = 0; i < uniformValue; i++) { }

// ✅ SAFE - Constant bound with early exit
const int MAX = 100;
for (int i = 0; i < MAX; i++) {
  if (i >= uniformValue) break;
}
8.2 切勿:未检查零值就进行除法
// ❌ DANGEROUS - Division by zero
float result = value / divisor;

// ✅ SAFE - Guard against zero
float result = value / max(divisor, 0.0001);

9. 实现前检查清单

阶段 1:编写代码前
  • 编写着色器编译测试
  • 编写 Uniform 可访问性测试
  • 为视觉回归测试创建基准图像
  • 定义性能目标(FPS、绘制调用)
  • 检查现有着色器,寻找可复用的模式
阶段 2:实现过程中
  • 所有循环均使用常量边界
  • 不存在除以零的可能
  • 使用无分支模式(mix/step)
  • 使用适当的精度限定符
  • 缓存纹理查询
  • 将 Uniform 批处理到向量/矩阵中
阶段 3:提交前
  • 所有着色器测试均通过:npm run test:shaders
  • 视觉回归测试均通过:npm run test:visual
  • 性能基准测试达到目标:npm run bench:shaders
  • 已验证跨浏览器兼容性
  • 边缘情况(UV 0,0 和 1,1)下无瑕疵
  • 已验证动画时序流畅

10. 总结

GLSL 着色器为 JARVIS HUD 中的视觉效果提供支持:

  1. 先进行 TDD:编写着色器前先编写测试——编译、uniform 和视觉回归
  2. 性能:使用无分支模式、纹理图集、LOD 和精度优化
  3. 安全性:使用固定的循环边界,并防止除零
  4. 测试:在目标浏览器中验证,并对 GPU 性能进行基准测试

请记住:着色器运行在 GPU 上——一个有问题的着色器就可能让整个系统卡死。


参考资料:

  • references/advanced-patterns.md - 复杂着色器技术

评论

支持 Markdown