1. 着色器性能优化

1.1. GPU架构理解与优化原则

1.1.1. GPU并行架构特点

javascript

// GPU vs CPU
// CPU: 少量强大核心,适合复杂逻辑
// GPU: 大量简单核心,适合并行计算

1.1.2. 着色器优化核心原则

1.2. 着色器编写优化

1.2.1. 减少算术运算

glsl

// ❌ 低效 - 多次除法
float result = (a / b) / c;

// ✅ 高效 - 预计算倒数
float invBC = 1.0 / (b * c);
float result = a * invBC;

// ❌ 低效 - 多次pow
float value = pow(x, 2.0) + pow(y, 2.0);

// ✅ 高效 - 使用乘法
float value = x * x + y * y;

1.2.2. 避免动态分支(if语句)

glsl

// ❌ 低效 - 动态分支
float calculateLight(vec3 normal, vec3 lightDir) {
    float dotProduct = dot(normal, lightDir);
    if (dotProduct > 0.0) {
        return dotProduct;
    } else {
        return 0.0;
    }
}

// ✅ 高效 - 使用max/min/clamp
float calculateLight(vec3 normal, vec3 lightDir) {
    return max(dot(normal, lightDir), 0.0);
}

// ✅ 高效 - 使用step/smoothstep
float calculateLight(vec3 normal, vec3 lightDir) {
    float dotProduct = dot(normal, lightDir);
    return dotProduct * step(0.0, dotProduct);
}

// ✅ 高效 - 使用mix代替分支
vec4 getColor(float condition) {
    // 代替:if (condition > 0.5) return color1; else return color2;
    float factor = step(0.5, condition);
    return mix(color2, color1, factor);
}

1.2.3. 优化循环

glsl

// ❌ 低效 - 动态循环次数
for (int i = 0; i < int(iterations); i++) {
    // ...
}

// ✅ 高效 - 编译时常量循环
#define MAX_ITERATIONS 10
for (int i = 0; i < MAX_ITERATIONS; i++) {
    // 展开循环(编译器会自动展开小循环)
}

// ✅ 高效 - 循环展开(手动)
vec4 color = vec4(0.0);
color += sampleTexture(uv + offsets[0]) * weights[0];
color += sampleTexture(uv + offsets[1]) * weights[1];
color += sampleTexture(uv + offsets[2]) * weights[2];
// ... 而不是循环

1.2.4. 使用内置函数

glsl

// 内置函数通常经过高度优化
// ✅ 使用内置函数
float distance = length(vec);
float clamped = clamp(value, 0.0, 1.0);
float lerped = mix(a, b, t);
float stepped = step(edge, x);

1.2.5. 优化数据精度

glsl

// 根据需求选择合适精度
precision highp float;   // 32位,高精度
precision mediump float; // 16位,中等精度(默认)
precision lowp float;    // 10位,低精度

// 局部变量可以使用更低的精度
mediump vec2 uv;
lowp vec3 color;  // 对于颜色计算,低精度通常足够

// 位置计算需要高精度
highp vec4 position;

1.3. 纹理优化

1.3.1. 纹理采样优化

glsl

// ❌ 低效 - 多次采样相同纹理
float r = texture2D(map, uv + vec2(offset, 0.0)).r;
float g = texture2D(map, uv + vec2(0.0, offset)).g;
float b = texture2D(map, uv - vec2(offset, 0.0)).b;

// ✅ 高效 - 一次采样,多次使用
vec4 texel = texture2D(map, uv);
float r = texel.r;
float g = texel.g;
float b = texel.b;

// ✅ 高效 - 使用mipmaps
// 确保纹理启用mipmaps
texture.minFilter = THREE.LinearMipMapLinearFilter;

// 在着色器中自动使用LOD
vec4 color = texture2DLodEXT(texture, uv, mipLevel);

1.3.2. 纹理缓存友好访问

glsl

// ❌ 低效 - 随机访问(缓存不友好)
float value = 0.0;
for (int i = 0; i < 100; i++) {
    vec2 randomUV = uv + vec2(randomOffset(i), randomOffset(i));
    value += texture2D(noiseTex, randomUV).r;
}

// ✅ 高效 - 空间连续性访问
for (int x = -2; x <= 2; x++) {
    for (int y = -2; y <= 2; y++) {
        vec2 sampleUV = uv + vec2(x, y) * pixelSize;
        value += texture2D(tex, sampleUV).r;
    }
}

1.3.3. 使用纹理图集

javascript

// 将多个小纹理打包成大纹理
const atlasTexture = new THREE.TextureLoader().load('atlas.png');
const material = new THREE.ShaderMaterial({
  uniforms: {
    atlas: { value: atlasTexture },
    uvOffset: { value: new THREE.Vector2(0, 0) },
    uvScale: { value: new THREE.Vector2(0.25, 0.25) }
  },
  // ...
});

glsl

// 在着色器中
uniform sampler2D atlas;
uniform vec2 uvOffset;
uniform vec2 uvScale;

vec2 atlasUV = uv * uvScale + uvOffset;
vec4 color = texture2D(atlas, atlasUV);

1.4. Uniform优化

1.4.1. Uniform分组

javascript

// ❌ 低效 - 单独更新
uniforms.time.value = clock.getElapsedTime();
uniforms.lightPos.value = light.position;

// ✅ 高效 - 批量更新
const batchUpdates = () => {
  material.uniforms.time.value = clock.getElapsedTime();
  material.uniforms.lightPos.value = light.position;
  // 所有uniforms一起更新
  material.uniformsNeedUpdate = true;
};

// ✅ 高效 - 使用UniformsUtils
const commonUniforms = {
  time: { value: 0 },
  resolution: { value: new THREE.Vector2() }
};

const material1 = new THREE.ShaderMaterial({
  uniforms: THREE.UniformsUtils.merge([
    commonUniforms,
    { color: { value: new THREE.Color(0xff0000) } }
  ]),
  // ...
});

1.4.2. 减少Uniform更新频率

javascript

// 只在必要时更新
let lastCameraPosition = new THREE.Vector3();
let lastTime = 0;

function updateUniforms() {
  const now = performance.now();

  // 每帧必须更新的
  material.uniforms.time.value = now * 0.001;

  // 只有变化时才更新
  if (!camera.position.equals(lastCameraPosition)) {
    material.uniforms.cameraPos.value.copy(camera.position);
    lastCameraPosition.copy(camera.position);
  }

  // 低频更新(例如每秒更新)
  if (now - lastTime > 1000) {
    material.uniforms.lightIntensity.value = calculateLightIntensity();
    lastTime = now;
  }
}

1.5. 几何体和顶点优化

1.5.1. 减少顶点计算

glsl

// ❌ 低效 - 在顶点着色器中进行复杂计算
attribute vec3 extraData;
varying float complexValue;

void main() {
  // 复杂计算
  complexValue = sin(extraData.x) * cos(extraData.y) * tan(extraData.z);
  gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}

// ✅ 高效 - 在片元着色器中进行复杂计算
// 或者在CPU中预计算

1.5.2. 使用Instancing

javascript

// 实例化渲染大量相同几何体
const geometry = new THREE.InstancedBufferGeometry();
geometry.copy(baseGeometry);

// 添加实例化属性
const offsets = new Float32Array(count * 3);
const colors = new Float32Array(count * 3);

geometry.setAttribute('offset', new THREE.InstancedBufferAttribute(offsets, 3));
geometry.setAttribute('color', new THREE.InstancedBufferAttribute(colors, 3));

// 着色器中使用
const vertexShader = `
  attribute vec3 offset;
  attribute vec3 color;
  varying vec3 vColor;

  void main() {
    vColor = color;
    vec3 pos = position + offset;
    gl_Position = projectionMatrix * modelViewMatrix * vec4(pos, 1.0);
  }
`;

1.6. 后处理优化

1.6.1. 降低分辨率

javascript

// 对后处理效果使用半分辨率
const composer = new THREE.EffectComposer(renderer, {
  multisampling: 4,
  frameBufferType: THREE.HalfFloatType
});

// 降低渲染目标分辨率
const renderTarget = new THREE.WebGLRenderTarget(
  window.innerWidth * 0.5,  // 半宽
  window.innerHeight * 0.5, // 半高
  {
    minFilter: THREE.LinearFilter,
    magFilter: THREE.LinearFilter,
    format: THREE.RGBAFormat,
    type: THREE.HalfFloatType
  }
);

1.6.2. 多Pass优化

javascript

// 合并多个后处理效果
class CombinedShader {
  constructor() {
    this.uniforms = {
      tDiffuse: { value: null },
      bloomStrength: { value: 1.0 },
      vignetteAmount: { value: 0.5 }
    };

    this.fragmentShader = `
      uniform sampler2D tDiffuse;
      uniform float bloomStrength;
      uniform float vignetteAmount;

      // 合并多个效果到一个shader中
      void main() {
        vec4 color = texture2D(tDiffuse, vUv);

        // 绽放效果
        vec4 bloom = getBloom(color);
        // 晕影效果
        float vignette = getVignette(vUv);
        // 颜色校正
        vec4 corrected = colorCorrection(color);

        // 合并
        gl_FragColor = mix(corrected, bloom, bloomStrength) * vignette;
      }
    `;
  }
}

1.7. 性能监控与调试

1.7.1. 使用着色器分析器

javascript

// 检查着色器编译状态
material.onCompile = (shader) => {
  console.log('Shader info:', {
    program: shader.program,
    uniforms: Object.keys(shader.uniforms),
    attributes: Object.keys(shader.attributes)
  });
};

// 使用Three.js的ShaderChunk进行调试
const customShader = THREE.ShaderLib.standard;
customShader.fragmentShader = customShader.fragmentShader.replace(
  '#include <output_fragment>',
  `
    #include <output_fragment>

    // 添加调试信息
    #ifdef DEBUG_PERFORMANCE
    gl_FragColor.r += 0.1; // 标记此shader
    #endif
  `
);

1.7.2. 性能计数器

javascript

class ShaderPerformance {
  constructor(material) {
    this.frameCount = 0;
    this.totalTime = 0;
    this.startTime = 0;

    // 替换原始着色器
    this.originalVertexShader = material.vertexShader;
    this.originalFragmentShader = material.fragmentShader;

    // 添加性能计数代码
    material.vertexShader = this.instrumentShader(this.originalVertexShader, 'vertex');
    material.fragmentShader = this.instrumentShader(this.originalFragmentShader, 'fragment');
  }

  instrumentShader(source, type) {
    return `
      #define SHADER_PERF_${type.toUpperCase()}

      ${source}

      #ifdef SHADER_PERF_VERTEX
      uniform float uPerfTime;
      #endif
    `;
  }
}

1.8. 移动端优化

1.8.1. 移动端特定优化

glsl

// 使用mediump作为默认精度
precision mediump float;

// 避免使用高精度计算
mediump float distance = length(vec);

// 减少纹理采样次数
#if defined(GL_FRAGMENT_PRECISION_HIGH)
  // 高性能设备
  #define SAMPLE_COUNT 16
#else
  // 低性能设备
  #define SAMPLE_COUNT 8
#endif

// 根据设备性能动态调整
uniform float quality;
float adjustedQuality = quality * devicePixelRatio;

1.8.2. 热代码路径优化

javascript

// 检测设备性能并调整着色器
function adaptShaderForDevice() {
  const isMobile = /Mobi|Android/i.test(navigator.userAgent);
  const isLowEnd = navigator.hardwareConcurrency <= 4;

  if (isMobile || isLowEnd) {
    // 使用简化版着色器
    material.fragmentShader = simplifiedFragmentShader;
    material.defines['LOW_QUALITY'] = 1;
    material.precision = 'mediump';
  } else {
    // 使用完整版着色器
    material.fragmentShader = fullFragmentShader;
    material.precision = 'highp';
  }
}

1.9. 缓存与预编译

1.9.1. 着色器缓存

javascript

class ShaderCache {
  constructor() {
    this.cache = new Map();
  }

  getShader(key, createShader) {
    if (this.cache.has(key)) {
      return this.cache.get(key);
    }

    const shader = createShader();
    this.cache.set(key, shader);
    return shader;
  }
}

// 使用
const cache = new ShaderCache();
const material = cache.getShader('wave-effect', () => {
  return new THREE.ShaderMaterial({
    vertexShader: waveVertexShader,
    fragmentShader: waveFragmentShader,
    uniforms: waveUniforms
  });
});

1.9.2. 预编译着色器

javascript

// 在场景加载时预编译着色器
function precompileShaders() {
  const dummyScene = new THREE.Scene();
  const dummyCamera = new THREE.Camera();
  const dummyRenderer = new THREE.WebGLRenderer();

  const shaders = [
    { material: waterMaterial, mesh: waterMesh },
    { material: fireMaterial, mesh: fireMesh },
    // ...
  ];

  shaders.forEach(({ material, mesh }) => {
    dummyScene.add(mesh);
    dummyRenderer.render(dummyScene, dummyCamera);
    dummyScene.remove(mesh);
  });
}

1.10. 工具推荐

  1. ShaderToy to Three.js转换器:在线转换工具

  2. Spector.js:WebGL调试器

  3. WebGL Inspector:Chrome扩展

  4. RenderDoc:图形调试工具

  5. Three.js Shader Editor扩展:VSCode插件

记住:先实现功能,再优化性能,最后验证结果。使用性能分析工具找到真正的瓶颈,避免过早优化。