mcpField
W
webgl-shader-constraints

webgl-shader-constraints

github.com/adevra开发1643 次安装31 周前更新
代码仓库

关于 webgl-shader-constraints

面向 Unity 浏览器构建的开发者参考技能,涵盖 WebGL 着色器限制、变通方法与优化。内容区分 WebGL 1.0 和 2.0 的要求,并指导减少着色器变体及构建开销。

主要功能

  1. 01WebGL 1.0 与 2.0 限制
  2. 02循环与数组索引变通方法
  3. 03着色器变体剔除
  4. 04纹理与内存建议
  5. 05WebGL 性能建议

适用场景

  1. 01排查仅在浏览器构建中失败的着色器
  2. 02减少着色器构建体积与首次加载编译
  3. 03使 Unity 着色器适配 WebGL 1.0 或 2.0

安装

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

$npx skills add https://github.com/adevra/unity-shader-agent-skills --skill webgl-shader-constraints

安装后在 Claude Code 中输入/webgl-shader-constraints确认已加载。

详细介绍

Unity 的 WebGL 着色器限制与解决方法

触发条件

当用户正在为 WebGL 浏览器部署开发 Unity 着色器时,使用此技能。触发词包括:“WebGL”、“浏览器游戏”、“Web 构建”、“WebGL 1.0”、“WebGL 2.0”、“WebGPU”、“GLSL”、“OpenGL ES 2.0/3.0”、“着色器编译 Web”、“Web 构建大小”、“Web 着色器变体”、“Always Included Shaders”,或任何提及将 Unity 内容部署到浏览器的情况。如果用户报告着色器错误仅出现在 Web 构建中,或遇到与着色器相关的构建大小问题,也应触发此技能。


核心规则

  1. WebGL 1.0(OpenGL ES 2.0)禁止动态数组索引。 只能使用常量或循环计数器变量作为数组索引。这是 WebGL 1.0 上着色器失败的首要原因。

  2. WebGL 1.0 只允许计数型 for 循环。 不允许使用 while 循环或 do-while 循环。循环计数器必须在 for 语句中初始化、比较和递增。循环上限必须是常量表达式。

  3. WebGL 2.0(OpenGL ES 3.0)取消了循环和索引限制,但新增了强制精度限定符要求。所有变量都必须显式指定精度(highp、mediump、lowp),或使用默认精度语句。

  4. WebGL 只支持烘焙 GI 和非定向光照贴图。 不支持实时 GI、定向光照贴图或采用 L2 球谐函数的光照探针。

  5. WebGL 构建中绝不要使用 Standard Shader。 它会编译数百个变体,并可能让构建大小增加 50MB 以上。请使用 Simple Lit、Unlit,或仅包含必要关键字的自定义着色器。

  6. 积极剥离 Web 着色器变体。 每个变体都会增加下载大小和初始着色器编译时间(这会阻塞浏览器主线程)。


WebGL 1.0 与 2.0 功能对比

功能WebGL 1.0(ES 2.0)WebGL 2.0(ES 3.0)
数组索引仅常量或循环索引允许动态索引
循环仅计数型 for 循环所有循环类型
GPU 实例化不支持支持
3D 纹理不支持支持
纹理数组不支持支持
整数纹理不支持支持
texelFetch不支持支持
Uniform Buffer Objects不支持支持
顶点纹理单元保证为 0保证 ≥16
MRT(多重渲染目标)不支持支持
延迟渲染无法实现可以实现(但开销较大)
定向光照贴图不支持不支持(Unity 限制)
精度限定符可选必需

循环与索引的解决方法(WebGL 1.0)

// FAILS on WebGL 1.0 — dynamic array indexing
half4 colors[4];
int index = someCalculation();
half4 result = colors[index]; // ERROR: non-constant index

// WORKAROUND — unroll with if/else
half4 result;
if (index == 0) result = colors[0];
else if (index == 1) result = colors[1];
else if (index == 2) result = colors[2];
else result = colors[3];

// FAILS on WebGL 1.0 — while loop
int i = 0;
while (i < count) { /* ... */ i++; } // ERROR

// WORKAROUND — counting for loop with constant bound
for (int i = 0; i < 16; i++) // 16 is a constant
{
    if (i >= count) break; // dynamic exit
    // ... loop body
}

// FAILS on WebGL 1.0 — non-constant loop bound
for (int i = 0; i < _DynamicCount; i++) { } // ERROR

// WORKAROUND — constant max with early break
#define MAX_ITERATIONS 32
for (int i = 0; i < MAX_ITERATIONS; i++)
{
    if (i >= _DynamicCount) break;
    // ... loop body
}

Web 着色器变体剥离

着色器变体是造成 WebGL 构建体积膨胀的最大因素。每个变体都必须由浏览器下载并编译。

步骤 1:移除 Always Included Shaders

在 Project Settings > Graphics > Always Included Shaders 中,移除所有不需要的内容。仅 Standard Shader 就会增加数百个变体。

步骤 2:使用 shader_feature 替代 multi_compile
// BAD for web — compiles ALL keyword combinations
#pragma multi_compile _ _FEATURE_A
#pragma multi_compile _ _FEATURE_B
// = 4 variants (2 × 2)

// GOOD for web — only compiles variants used by materials in build
#pragma shader_feature _ _FEATURE_A
#pragma shader_feature _ _FEATURE_B
// = only variants referenced by materials in scenes
步骤 3:使用本地关键字(每个着色器最多 64 个)
// Global keywords (shared pool of 256, wasteful)
#pragma multi_compile _ _MY_KEYWORD

// Local keywords (per-shader pool of 64, efficient)
#pragma shader_feature_local _ _MY_KEYWORD
步骤 4:针对 Web 进行条件编译
// Strip expensive features on WebGL
#if !defined(SHADER_API_GLES) && !defined(SHADER_API_GLES3)
    // Desktop-only features here (SSR, complex lighting, etc.)
#endif

// Or target mobile/web specifically
#if defined(SHADER_API_MOBILE) || defined(SHADER_API_GLES) || defined(SHADER_API_GLES3)
    // Simplified mobile/web path
#else
    // Full desktop path
#endif
步骤 5:使用 IPreprocessShaders 进行程序化剥离
using UnityEditor.Build;
using UnityEditor.Rendering;
using System.Collections.Generic;

class WebGLShaderStripper : IPreprocessShaders
{
    public int callbackOrder => 0;
    
    public void OnProcessShader(Shader shader, ShaderSnippetData snippet, 
                                 IList<ShaderCompilerData> data)
    {
        for (int i = data.Count - 1; i >= 0; i--)
        {
            // Strip deferred variants (useless on WebGL)
            if (snippet.passType == PassType.Deferred)
            {
                data.RemoveAt(i);
                continue;
            }
            
            // Strip specific keywords
            var keywords = data[i].shaderKeywordSet;
            if (keywords.IsEnabled(new ShaderKeyword("_SHADOWS_SOFT")) ||
                keywords.IsEnabled(new ShaderKeyword("_ADDITIONAL_LIGHTS")))
            {
                data.RemoveAt(i);
            }
        }
    }
}

WebGL 性能提示

  1. 着色器编译会阻塞主线程。 与原生构建不同,WebGL 会同步编译着色器。着色器过多会导致首次加载时长时间卡顿。请尽量减少变体数量。

  2. Web 平台优先使用 URP,而非 Built-in。 配置得当时,URP 能生成更小的构建,并减少着色器变体。

  3. 纹理压缩至关重要。 WebGL 2.0 使用 ASTC(首选)或 ETC2。DXT/BC 格式仅适用于桌面浏览器。

  4. 内存极其有限。 WebGL 运行在浏览器的内存沙箱中。大量着色器变体可能导致 OOM 崩溃。

  5. 在移动浏览器上测试。 iOS Safari 的 WebGL 性能明显不如桌面版 Chrome。许多能在桌面版 Chrome 上运行的效果,在移动版 Safari 上会失效或卡顿。


WebGPU(未来)

Unity 6 已开始实验性支持 WebGPU。WebGPU 可用后,将解除 WebGL 的许多限制:支持计算着色器、存储缓冲区,并提供更好的线程处理能力。不过,截至 2025 年,WebGL 2.0 仍是浏览器游戏的生产目标。着色器应以 WebGL 2.0 为目标,并将 WebGPU 作为渐进式增强进行规划。


资料来源

评论

支持 Markdown