Tidewater的WGSL着色器组合系统:ShaderModule如何优雅管理数百个着色器 【免费下载链接】tidewaterCoastal town built with Opus 5.5项目地址https://gitcode.com/gh_mirrors/ti/tidewater点击查看免费下载Tidewater 是一款基于 WebGPU 原生构建的浏览器海岛钓鱼游戏它的整个渲染引擎不使用任何框架直接以 WGSL 编写着色器。全项目共有近百个ShaderModule实例海洋、地形、云层、鲸鱼等每个系统都导出可复用的 WGSL 代码块。本文深入讲解 Tidewater 的 WGSL 着色器组合系统看它如何用模块拼装的方式优雅管理数百个着色器避免绑定声明重复和冲突。痛点手写数百个 WebGPU 着色器有多痛苦在 WebGPU 中每写一个着色器都要手工完成三件枯燥的事声明group(n) binding(m)变量、创建BindGroupLayout、把纹理/缓冲区塞进BindGroup。更麻烦的是共享代码——采样高度图求海拔这类函数地形材质需要、水面着色器需要、后处理 GTAO 也需要。如果每处复制一份改一处就要改几十处。Tidewater 的解法只有一个文件src/engine/gpu/Shader.js其中ShaderModule类是整个组合系统的地基。ShaderModule一块自带接口清单的 WGSL 乐高ShaderModule的构造参数非常简洁见 src/engine/gpu/Shader.js#L45-L59name模块名用于日志和调试deps依赖的其他模块形成依赖树code一段纯 WGSL——函数、结构体、常量bindings这段代码需要读取的资源纹理、缓冲区、uniform 块uniforms可选自动声明为varuniform的 uniform 块以地形系统为例它把高度图、法线图、splat 图全部打包成一个模块src/world/TerrainGPU.js#L101-L114this.module new ShaderModule( { name: terrain, deps: [ commonModule ], uniforms: this.uniforms, bindings: { terrainHeightTex: { texture: this.heightTexture }, terrainNormalTex: { texture: this.normalTexture }, // ... }, code: TERRAIN_WGSL, } );之后任何着色器只要把terrainModule加进自己的modules列表就能直接调用terrainHeightAt( xz )这样的函数——代码和它读取纹理的绑定一起被带过去一次声明处处可用。 共享基础工具也这样组织噪声、哈希、深度重建函数全部收在 src/engine/render/wgsl/common.js 的commonModule里被几乎所有系统依赖。依赖树自动去重与排序真正巧妙的地方在collectModules函数src/engine/gpu/Shader.js#L68-L84它做深度优先遍历先访问依赖再访问自身并用Set去重。也就是说即使十个系统都直接或间接依赖commonModule它生成的 WGSL 代码在最终着色器里也只出现一次。模块之间无需关心谁先谁后依赖顺序由系统自动推导。三组 Bind Group组合系统的隐藏骨架Tidewater 给所有着色器规定了统一的绑定分组见 src/engine/gpu/Shader.js#L23-L29 的注释分组内容特点group 0frame帧 uniform相机、时间、太阳方向 9 个共享采样器全局唯一所有着色器共用同一套group 1各模块 材质自己的资源自动组合本系统的核心group 2每次绘制per-draw数据仅渲染管线使用group0按渲染阶段懒加载并缓存src/engine/gpu/Shader.js#L480-L493而 9 个共享采样器线性/各向异性/最近邻/阴影比较让所有纹理共享少量 sampler 句柄绕开了每纹理一个采样器的经典浪费。BindingSet绑定组的自动缓存BindingSetsrc/engine/gpu/Shader.js#L323-L458把声明 WGSL 变量和创建运行时 BindGroup两件事合并了describe()根据 spec 自动生成 WGSL 声明——uniform 块、storage 缓冲区、采样纹理、存储纹理、sampler 五种类型全部覆盖declarations()直接吐出group(1) binding(i)声明拼进源码getBindGroup()带一个 4 槽小缓存帧间资源没变对比的是对象引用 版本号而非字符串就直接复用旧的BindGroup避免了逐帧重建的开销对于 ping-pong 交替的历史纹理时域抗锯齿、TAA 那一类这个缓存正好覆盖两种组合非常务实。迷你预处理器让 WGSL 也能写条件分支WGSL 本身没有 C 那样的#ifdef。Tidewater 在 src/engine/gpu/Shader.js#L88-L173 实现了preprocess()支持#if、#ifdef、#ifndef、#elif、#else还能比较数值#if NAME value和/||逻辑组合。define 从材质或核的defines字段传入这让同一份着色器源码可以在带/不带水下光照不同 LOD 等级等配置间切换而不必维护多份拷贝——materialSurface这类大模板里的分支全靠它。自动应对 GPU 的预算限制WebGPU 对每个着色阶段有硬性预算如每阶段 12 个 uniform 缓冲区。组合系统的危险在于模块堆多了uniform 块可能悄悄超预算、编译直接失败。composeShader在拼装时自动兜底src/engine/gpu/Shader.js#L533-L576按阶段划分绑定——用reachableIdentifiers做函数调用图分析src/engine/gpu/Shader.js#L598-L642找出顶点着色器根本用不到的绑定把可见性收窄到 fragment 阶段省下 uniform 名额降级demote——如果 uniform 块仍超预算把超出的块自动改绑为只读 storage 缓冲区结构布局不变WGSL 声明同步改写作者基本无需为这个组合会不会超限操心组合系统替他们管了。编译错误也能对号入座createShaderModulesrc/engine/gpu/Shader.js#L654-L676做了两件贴心事按源码文本缓存GPUShaderModule同源不重复创建异步拉取getCompilationInfo一旦有错误就打印行号 带标记的源码片段WGSL error in terrain ( 87:3 ): expected ;, found identifier let x terrainHeightAt( xz )调试几百行拼装出来的着色器时这比一坨原始日志救命多了。三个入口Material、ComputeKernel、FullscreenPass组合系统最终通过三个入口被使用写法高度一致入口文件场景Materialsrc/engine/render/Material.js网格材质modulesvertex/surface片段ComputeKernelsrc/engine/gpu/Compute.js#L21-L41FFT 海洋、泡沫、尾迹等 compute 核FullscreenPasssrc/engine/render/FullscreenPass.js后处理全屏 pass三者都调用同一个composeShadersrc/engine/gpu/Shader.js#L519-L595收集模块 → 合并绑定 → 拼上结构体与 group 0/1 声明 → 预处理器求值 → 产出最终 WGSL 源码和两个BindingSet。渲染端还会自动按 pass 类型决定要不要插入光照模块与钩子模块src/engine/render/MeshShader.js#L371-L381比如深度 pass 就不带光照省得白白占用 uniform 预算。项目还有一套严格的模块命名规范terrainHeightAt、oceanParams、cloudsShadow……统一前缀见 docs/PORTING.md保证跨系统调用不重名、可读性好。小结组合优于继承Tidewater 管理数百个着色器的思路可以概括为三句话系统即模块每个子系统导出一个ShaderModule函数、资源、依赖一起打包声明即绑定describe()从同一份 spec 同时生成 WGSL 声明和运行时 BindGroup两边永不脱节预算交给系统依赖去重、阶段划分、uniform 降级、BindGroup 缓存全自动这套纯 WebGPU 原生的设计没有引入一行框架代码却拿到了引擎级的复用能力和稳定性——对想在浏览器里写大型渲染项目的开发者来说src/engine/ 目录值得一读。赞分享【免费下载链接】tidewaterCoastal town built with Opus 5.5项目地址https://gitcode.com/gh_mirrors/ti/tidewater点击查看免费下载相关推荐react-native-skia 混合着色器指南用 Blend 与 ColorShader 组合着色效果react native skia 混合着色器指南用 Blend 与 ColorShader 组合着色效果 本篇指南基于 react native skia图形学移动开发跨平台UI组件ants源码解析三worker队列双实现——栈与环形队列、PreAlloc预分配的性能秘密ants源码解析三worker队列双实现——栈与环形队列、PreAlloc预分配的性能秘密 作为 Go 语言最流行的 goroutine 池开源库并发编程后端WebGL 着色器与 GLSL 完全指南顶点着色器、片段着色器与着色器语言详解WebGL 着色器与 GLSL 完全指南顶点着色器、片段着色器与着色器语言详解 本文是 WebGL 基础 https://link.gitcode.com/i图形学创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考