Files
西风逍遥游andCursor Agent 3c38a633fc feat(graphics,animation): hair cards + DynamicBone hair helpers (#400)
* feat(graphics,animation): solidify hair cards + DynamicBone hair helpers

Improve card-path hair for game characters: Kajiya-Kay wrap/scatter knobs,
procedural card mesh/material/LOD helpers, Material hair surface defaults,
and animation setupHairChain/setupHairHeadCollider convenience APIs.

Co-authored-by: 西风逍遥游 <sunxfancy@gmail.com>

* fix(animation): avoid copying Result in hair DynamicBone tests

zeroerr REQUIRE on Result<T> tries to copy a move-only type; assert .ok() instead.

Co-authored-by: 西风逍遥游 <sunxfancy@gmail.com>

* fix(graphics): document hair card pointer API ownership/lifetime

Source-quality api-lifetime rejected undocumented raw-pointer returns in
HairCards.h and the HairWgsl string literal; match WaterfallWgsl contracts.

Co-authored-by: 西风逍遥游 <sunxfancy@gmail.com>

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
2026-09-15 09:11:32 +08:00

8.3 KiB
Raw Permalink Blame History

Graphics 高级渲染与屏幕空间效果

返回模块概览

可编译渲染控制与 GBuffer

RenderControl 用字符串特性开关,再 compile() 成有序 Pass(默认:shadow → forward → hair):

local rc = gfx.getRenderControl();
rc.enable("gbuffer");      // 额外填充可采样深度/法线
rc.enable("gbufferAlbedo");
rc.compile();
gfx.render3D();
local gb = rc.getGBuffer();
if (gb.isValid()) {
    local depth = gb.getDepthTexture();     // RGBA8,R = 线性深度 0..1(Canvas / 体积雾)
    local hwDepth = gb.getHwDepthTexture(); // D32,.r = Vulkan NDC z(3D AO / GI)
    local nrm = gb.getNormalTexture();      // RGB = 法线*0.5+0.5
}

3D 前向仍启用硬件 z-buffer;GBuffer 是给 AO / 体积雾 / 风格描边等中后期用的采样目标。阴影仍走 CSM shadow map。

一帧里各 buffer 谁写谁读、对应函数和 shader,见开发文档 3D渲染管线.md。

大面积平铺 albedo(地面、墙面)若出现明显重复,可对实体调用 setTexCellBomb(cellScale, strength, rotAmount=1):按 UV 划分 cell,对邻接 cell 做随机偏移/旋转并混合。strength=0(默认)关闭,行为与原先一致;cellScale 一般为 2~16。

砖墙、石板等需要假深度时,用 setHeightTexture(heightTex) + setParallax(scale, minLayers=8, maxLayers=32) 开启视差遮蔽贴图(POM)。高度图取 R 通道(白=凸起朝向观察者);scale=0(默认)关闭。典型 scale 为 0.02~0.08;掠射角下层数会自适应增加。

毛发 / 皮毛渲染(Hair Cards)

适用于 VRoid / 角色发片、动物皮毛等 alpha 卡片网格。引擎提供内置 Kajiya-Kay 各向异性高光 shader(含 soft wrap 漫反射与廉价 back-scatter),并在 RenderSystem3D 中于不透明物体之后、按距离从远到近绘制。

推荐路径(发卡,非 strands):

local hairMesh = gfx.newHairCardMesh(0.12, 0.45)   // 程序化单片;也可用 DCC 发卡模型
local farMesh  = gfx.newHairCardMesh(0.2, 0.35)    // 远距更宽更矮的代理片
local hairMat  = gfx.newHairCardMaterial(hairAlbedo) // 自动 transparent/双面/不写深度/不投阴影
hairMat.getShader().sendFloat("specExp", 90.0)
hairMat.getShader().sendFloat("diffuseWrap", 0.4)
hairMat.getShader().sendFloat("scatterStrength", 0.3)

local hair = Renderable3D.create()
hair.setMaterial(hairMat)
// LOD:近距发卡 → 远距代理片
// C++: eve::graphics::hair::configureCardLod(hair, hairMesh, farMesh, 8.0)
hair.setMeshLod(0, hairMesh, 0)
hair.setMeshLod(1, farMesh, 8)
hair.setHair(true)

等价手写:

local hairShader = gfx.newHairShader()
local hair = Renderable3D.create()
hair.setMesh(hairCardMesh)
hair.setTexture(hairAlbedo)
hair.setShader(hairShader)
hair.setHair(true)   // Material/标志会带上双面、透明排序等发卡默认值

可调 push 参数:specExp、specStrength、primaryShift、secondaryShift、alphaCutoff、rimStrength、strandDir(发束方向,全 0 时由顶点自动推导)、diffuseWrap、scatterStrength。

发骨摆动请用 animation 的 setupHairChain / DynamicBoneSolver(见 animation 模块),不要在 graphics 里直接依赖骨骼求解器。

屏幕空间体积光(尘雾光柱)与体积雾

vol <- gfx.newVolumetric()。setQuality("low"|"medium"|"high") 控制采样与 resolutionFor。

  • screenspace:beginOcclusionMap → drawOccluders2D → scatter;或 applyFromScene
  • raymarch:setMode("raymarch") + setCamera + 线性深度 → rayMarch
  • fog:setMode("fog") + setFogHeight* / setFogStart/End + 线性深度 → applyFog(雾色 alpha 叠加场景)
  • froxel:configureFroxelGrid → clearFroxelGrid → injectFroxelHeightFog → integrateFroxel → uploadFroxel;在 gfx.render3D() 后将 GBuffer 线性深度传给 applyFroxel 或 applyFroxelTo。介质未变化时不必每帧重新上传。
  • cloud:setMode("cloud"),用 setCloudLayer、setCloudCoverage、 setCloudDensity、setCloudScale、setCloudWind 和 setCloudLightColor 调整云层;线性深度输入通过 renderClouds 或 renderCloudsTo 渲染, getCloudShader 可用于高级参数检查与调试。

细节见 体积光模块设计.md。

屏幕空间环境光遮蔽(SSAO / HBAO / GTAO)

ao <- gfx.newAmbientOcclusion()。setMode("ssao"|"hbao"|"gtao"),setQuality 控制采样与 resolutionFor。

  1. setCamera + 线性深度纹理(与体积雾相同约定)
  2. compute / computeTo → AO 图(RGB=遮蔽因子,A=深度)
  3. 可选 blur / blurTo(双边)
  4. applyOverlay 以黑 + alpha=(1-ao)*intensity 叠到已有场景

3D 默认路径:RenderControl 特性 "ao"(默认开)会在 forward 之后对 GBuffer 的 D32 + 法线做 applyFromGBuffer,不必手动建 Canvas。Canvas 上的 compute 仍用 8-bit 线性深度。

细节见 环境光遮蔽模块设计.md。

屏幕空间全局光照(SSGI)

gi <- gfx.newGlobalIllumination()。setQuality 控制采样数与半径;setLightDirection / setLightColor 提供反弹用的太阳光。

3D 默认路径:RenderControl 特性 "gi" 仍默认开(mesh 半球天空/地面 + wrap fill)。fullscreen applyFromScene 不会自动叠到 3D 回读:从 lit scene color 采样会把帘子/花盆印到地面上形成游走鬼影。需要时仍可手动 applyFromScene / applyFromDepth。Canvas 测试仍可用打包的 applyFromDepth(A=线性深度)。

local rc = gfx.getRenderControl();
rc.enable("gi");   // 默认已开
rc.compile();

AAA 时域反射链(TAA + RTGI + SSR)

"reflectionChain" 一次启用 TAA、fullscreen RTGI、PBR 感知 SSR 及其 GBuffer 依赖,并默认关闭重复的硬件 MSAA 成本。基础材质的环境 IBL 是 SSR 未命中、出屏和高粗糙度区域的后备反射;SSR 结果按时域置信度覆盖,避免屏幕边界硬切。

local rc = gfx.getRenderControl();
rc.setPostProcessQuality("high"); // low | medium | high | ultra
rc.enable("reflectionChain");
rc.compile();

室内或盒状空间可在相机环境 cubemap 上启用 box-projected reflection probe,使墙面和物体反射对应有限房间而不是无限远天空:

camera.setEnvMap(roomCubemap);
camera.setEnvIntensity(1.0);
camera.setEnvProbe(0, 2, 0, 8, 2, 6); // center xyz, half-extent xyz

只有位于 probe AABB 内的 fragment 会修正镜面反射方向;盒外自动使用普通环境方向。camera.clearEnvProbe() 恢复无限远 IBL。任一 extent 为零时 probe 视为关闭。

运行时切档会同步更新 TAA、RTGI 与 SSR,并废弃不兼容的历史帧;ultra 在 TAA 上映射到其最高 high 档,在 RTGI/SSR 上使用原生 ultra。关闭 reflectionChain 会同步关闭三个子链路;如果需要逐项调试,也可分别启用 "taa"、"rtgi"、"ssr"。

抗锯齿(硬件 MSAA + 经典后处理)

硬件 MSAA(3D 模型/体素边缘效果最好):3D scene color pass 默认 4x 多重采样再 resolve。用 RenderControl "msaa"(默认开)开关,gfx.setMsaaSamples(n) 设采样数(0/1=关,2/4/8 按设备能力 clamp):

gfx.setMsaaSamples(8);          // 升到 8x(若设备支持)
gfx.setMsaaSamples(0);          // 关闭硬件 MSAA
rc.disable("msaa");             // 或通过 RenderControl 特性关

经典后处理:aa <- gfx.newAntiAliasing()。setQuality("low"|"medium"|"high") 调整阈值与搜索;setMode 选择算法:

  • fxaa:FXAA 3.11 风格亮度边搜索
  • smaa:SMAA 启发的单 Pass 形态学 AA
  • ssaa:超采样 Resolve(先画到 resolutionFor 尺寸的 Canvas)
  • nfaa:沿亮度梯度切向的 Normal Filter AA

典型流程:场景 → Canvas → aa.applyCanvas / applyCanvasTo → 屏幕。

3D 默认路径:begin3DFrame 画到可采样的 scene color("msaa" 开时先 Nx 多重采样再 resolve 到 1x),present 时按 RenderControl "aa"(默认开)做 FXAA resolve 再叠 AO/HUD。手动 Canvas 路径仍然可用。细节见 抗锯齿模块设计.md。