这里是全站文章的归档入口,下面是按时间倒序排列的全部文章。若想按主题浏览,请从首页的专题分类进入。
《游戏引擎实现原理》14.2 WebGPU 约束下的帧图
Vulkan 类 API 的帧图要替开发者插屏障、把瞬态资源别名进共享显存;WebGPU 把这两件事整个从 API 里拿掉了——同步在 pass 边界隐式完成,纹理也没有别名入口。本节在 helio 里把这个约束差异量化:同一批瞬态资源,别名规划器给出两种显存占用,再在真实设备上跑通一条三 Pass 链。
posts
这里是全站文章的归档入口,下面是按时间倒序排列的全部文章。若想按主题浏览,请从首页的专题分类进入。
Vulkan 类 API 的帧图要替开发者插屏障、把瞬态资源别名进共享显存;WebGPU 把这两件事整个从 API 里拿掉了——同步在 pass 边界隐式完成,纹理也没有别名入口。本节在 helio 里把这个约束差异量化:同一批瞬态资源,别名规划器给出两种显存占用,再在真实设备上跑通一条三 Pass 链。
帧图不是一张画出来的图,而是一组 Pass 与它们读写资源的声明。本节在 helio 里从零实现帧图的 IR:把「谁读什么、谁写什么」翻译成数据流依赖与三类 hazard,跑拓扑排序排出执行序,再算出每个资源的生命周期区间,为后面的内存规划与执行打底。
阴影的两大旋钮——每像素采样数与深度偏置——分别换质量和换成本。PCF 半径从 1 到 3 让采样数按平方增长,把硬边软化成柔化像素;偏置从 0 往上加能压住自遮挡粉刺,但加过头阴影就脱离接触点。本节在 helio 里实现采样核与斜率缩放偏置,并在 GPU 上量化这两条权衡曲线。
一张固定分辨率的阴影贴图罩住整个视锥,近处必然糊、远处必然浪费。级联阴影把视锥沿深度切成几级,每级单独拟合一个光源空间正交盒,并在相机移动时把正交盒对齐到 texel 网格。本节在 helio 里实现切分、角点提取与拟合,再在 GPU 上复算逐级联的包围盒。
光照决定物体长什么样,阴影决定它站在哪。本节在 helio 上从零实现阴影贴图:先从光源视角把遮挡物渲染成一张光空间深度图,再把相机视角的地面点投影回光源空间做深度比较,最后在真实 WebGPU 设备上回读像素,与 CPU 解析参考逐点比对。
上一节把单个光源的衰减算清楚,但场景里往往有上百个光源,逐物体遍历全部光源太贵。集群光照把视图空间切成三维网格,每个集群预存一份命中的光源列表。本节在 helio 里实现集群网格与光源装配的 CPU 参考,再用 compute 着色器在真实 WebGPU 设备上复算并逐集群比对。
上一节把着色路径打通,这一节解决「光从哪来、怎么变弱」。方向光没有距离衰减,点光用 UE4 的窗口化反平方,聚光再叠一个锥形衰减。本节在 helio 里实现三类光源的衰减公式,并用一个 compute 着色器在真实 WebGPU 设备上回读每个距离的衰减值,与 CPU 参考逐样本比对。
光照不是一句「乘以点积」,而是「法线 + 光方向 + 视线 + 材质」到一根颜色的映射。本节在 helio 里从零实现 Lambert、Blinn-Phong、GGX 三条着色路径的 CPU 参考,再原样搬进 WGSL,在真实 WebGPU 设备上渲染球体并回读像素,要求 CPU 与 GPU 逐个字节对齐。
着色器写错时报错只有一行天书,定位全靠猜:本节真跑 getCompilationInfo(),拿到带行号、列号与字符偏移的诊断,并用 11.1 的 include 行映射把它还原成原始文件的文件:行:列,让编辑器能一键跳到出错的那一行源码。
uniform 结构体在 CPU 侧要按完全一致的内存布局填充,手写偏移是 bug 温床:本节从 WGSL 的 struct 反射出每个成员的偏移与整个缓冲区大小,按该布局自动打包参数,并真跑一次渲染回读像素,验证绑定确实生效,偏移对了像素才会对。
WGSL 没有 #include,但引擎要把公共函数拆成多个文件复用:本节在 createShaderModule() 之前做一层文本级 include 展开,处理递归、去重与循环检测,并真跑一次编译与像素回读,验证拼接后的模块确实可编译可用。
六个开关朴素地给出 2^6=64 个着色器变体,但特性之间有依赖,真实可达集只有 32 个。这一节按依赖枚举可达变体、用内容寻址(源码 + 开关集合的哈希)给编译结果去重,并在 headless Chromium 里把全部可达变体编译一遍,验证 0 error、非法组合会被拒。
WGSL 没有预处理器,材质却要在多种开关组合下生成不同程序。这一节实现一个只认 #ifdef / #ifndef / #else / #endif 的行级宏展开器,把一份模板裁剪成具体变体,并在 headless Chromium 里逐个编译验证 0 error。
把材质拆成「模型(参数声明)+ 实例(参数值)」两层,按 std140 算出每个参数的字节偏移并打包成 uniform buffer;再在 headless Chromium 里把它绑进管线、渲染一帧、回读中心像素,断言它就是 baseColor。
从 Tracer/Span 门面为什么存在讲起,用本机 jar 核实的签名拆解一次 span 的生命周期,说明 ObservationHandler 链如何让一次观察同时产出指标与 span,对比 Brave 与 OpenTelemetry 两种桥接、MDC 日志关联、概率与限速采样的组合,以及 W3C traceparent 与 B3 的报文差异。
把 18.1 的架构决策落成代码:分层骨架与关键接口、跨模块契约与统一错误码、借阅上限的乐观锁加唯一约束兜底、借书事件的可靠投递,并把单元、切片、Testcontainers 集成与契约测试串成四层测试,最后给出本地起依赖到联调前端的可复现步骤。
15.2 的校验只会说「过 / 不过」,但反作弊需要的是「可疑到什么程度」。本节给 aegis 写三个真跑的零件:令牌桶限速、指数衰减的嫌疑分、以及把分数映射成处置的阈值表。核心是一个处置梯度——限速、标记、断开、封禁,宁可分级也不一刀切,把误伤的代价压到最低。
移动和战斗是外挂最爱下手的两条路径。本节给 aegis 写一套真跑的校验:把「目的地」当提议、用位移与时间的比值判定超速、用单帧位移上限抓瞬移、用线段采样挡穿墙,再让服务端独占技能冷却与命中判定;并说明为什么校验函数要写成无时钟、无 I/O 的纯函数。每条规则都配了断言,跑得绿才贴进来。
外挂的根子不在客户端有漏洞,而在服务端信了不该信的话。本节把权威边界画清楚:哪些字段客户端只能提议、哪些必须由服务端裁决;再用一个真跑的信任契约表证明——客户端一旦发来服务端自有字段就是违规。最后说明为什么信任模型必须落到字段级,以及「把一切搬上服务端」并不是正确答案。
延迟补偿不能无限给:给得越多,被击中的玩家越觉得「明明躲开了」。本节给补偿套上上限——回退窗口取「绝对上限」与「最大补偿 RTT 的一半」的较小值,并用一个 RTT 追踪器识别「平时很快、开枪瞬间掉包」的延迟作弊,最后讨论被高延迟玩家击杀的体感如何用反馈层缓解。