Skip to content

手写 Fullscreen Shader

自 2.2.0

真实 Post Effect Render Graph

手写 Fullscreen Shader 是一个由 Render Graph Pass 调用的 Minecraft Core Shader。适合移植已有 Post FX、编写复杂循环,或实现 Fullscreen Shader Graph 暂时没有的算法。它与 Graph Pass 使用相同的全屏 Quad、HDR Target Pool、Priority、Mask 和混合系统。

最小资源结构

通常不需要自己写 Vertex Shader。Photon 随 LDLib2 提供了 ldlib2:fast_blit:输入全屏 Position,输出归一化的 texCoord

text
assets/wiki/shaders/core/postfx/wave_tint.json
assets/wiki/shaders/core/postfx/wave_tint.fsh

Render Graph 中填写的 Shader ID 是 wiki:postfx/wave_tint

完整例子

这个 Pass 从 DiffuseSampler 读取上一个画面,以像素为单位做横向波动,再乘一个可调颜色。

json
{
  "vertex": "ldlib2:fast_blit",
  "fragment": "wiki:postfx/wave_tint",
  "samplers": [
    { "name": "DiffuseSampler" }
  ],
  "uniforms": [
    { "name": "DiffuseSampler_TexelSize", "type": "float", "count": 4,
      "values": [1, 1, 1, 1] },
    { "name": "ScreenSize", "type": "float", "count": 2,
      "values": [1, 1] },
    { "name": "GameTime", "type": "float", "count": 1,
      "values": [0] },
    { "name": "Strength", "type": "float", "count": 1,
      "values": [4] },
    { "name": "TintColor", "type": "float", "count": 4,
      "values": [1, 1, 1, 1] }
  ]
}

ScreenSize 在这个最小算法中没有参与计算,保留它是为了展示 Engine Uniform 的声明方式。无用 uniform 可能被 GLSL Compiler 优化掉,Photon 会安全地跳过不存在的 Location。

接入 Render Graph

  1. 创建或打开一个 Render Graph。
  2. 添加 Pass 节点。
  3. Pass Source Type 改为 Custom Shader
  4. Shader 填写 wiki:postfx/wave_tint
  5. Pass 会从 JSON 反射出 DiffuseSamplerStrengthTintColor Port。
  6. Scene Color 或上一个 Pass 的输出连接到 DiffuseSampler
  7. 将 Pass 输出连接到 Effect Output

StrengthTintColor 可以直接填写常量,也可以连接 Render Graph Blackboard 参数,供 Timeline 或 Java 覆盖。要让请求 Weight 驱动某个 uniform,可将 Effect Weight 连接到对应 Value Port;手写 Shader 没有强制名为 Weight 的内置 uniform。

修改 JSON 的 sampler/uniform 接口后,执行 Resource Reload,并在 Pass 上重新选择一次 Shader,让节点重建 Port。现有源码损坏时编辑器会暂时保留最后一次有效 Port,但 Render Graph 不会成功编译。

Sampler:每一个都是 Texture Port

JSON 的 samplers 数组决定 Pass 的纹理输入。名称没有固定语义:

json
"samplers": [
  { "name": "SceneColor" },
  { "name": "SceneDepth" },
  { "name": "NoiseTexture" }
]

这会生成三个必连 Port。可以分别连接:

Render Graph 来源常见用途
Scene Color原始或前序效果后的 HDR 颜色
Scene DepthDepth Fade、DOF、重建位置
Custom Mask按 Mask Group 限制区域
Custom DepthPhoton 标记对象的深度
更早的 Pass OutputBlur、Bloom、Composite 等多 Pass 链
Texture InputLUT、Noise、Ramp 或外部参数纹理

与粒子 Custom Shader 不同

Post FX 不使用 SamplerSceneColorSamplerSceneDepth 这类自动绑定名称。Sampler 的数据来源完全由 Render Graph 连线决定;漏连任意 sampler 都会导致编译失败。

<Sampler>_TexelSize

对 sampler DiffuseSampler 声明同名后缀 uniform:

glsl
uniform vec4 DiffuseSampler_TexelSize;

Photon 会写入 (width, height, 1/width, 1/height)。它不会成为 Value Port,适合 Blur、Outline 和任意像素偏移。

Scene、Depth、Mask 和 Pass Output 的尺寸已知,可以正确更新。Texture Input 使用固定 Asset 或 Runtime 参数时,Executor 不一定知道原图尺寸,此时保留 JSON 默认值;需要尺寸的算法应把尺寸作为独立参数传入。

可暴露的自定义 Uniform

CustomShaderPass 只把以下 JSON uniform 转成 Pass Value Port:

JSONPort 类型
"type": "float", "count": 1float
"type": "float", "count": 2vec2
"type": "float", "count": 3vec3
"type": "float", "count": 4vec4

int、矩阵、count 超出 1–4 的声明不会成为参数 Port。JSON 的 values 是默认值:每次 Dispatch 先恢复默认值,再应用 Pass 常量、Blackboard 参数或 Effect Weight,避免上一帧的值泄漏到下一次执行。

颜色没有专用反射规则。需要颜色时使用 float/count 4,并在 Render Graph 中以 vec4 传递。

Engine Uniform

以下名称由 Executor 管理,不会显示为 Pass Port。

名称类型内容
ScreenSizevec2当前 Pass 输出 Target 的 (width, height)。半分辨率 Pass 得到半分辨率尺寸。
GameTimefloatRenderSystem.getShaderGameTime(),一个 Minecraft 日周期为 0..1
ModelViewMatmat4捕获该画面的相机 View/ModelView Matrix。
ProjMatmat4捕获该画面的 Projection Matrix。
<Sampler>_TexelSizevec4已知输入纹理的 (width, height, 1/width, 1/height)

相机与屏幕空间 Uniform

自 2.2.3
名称类型内容
U_ViewPortvec4当前相机在 Pass Target 中的 (x, y, width, height);编辑器 Scene 可能只占子区域。
kg_TimefloatGameTime 同源的秒值,GameTime × 1200
kg_ViewMatmat4捕获的 View Matrix。
kg_IViewMatmat4View Matrix 的逆矩阵。
kg_IModelViewMatmat4当前 1.21.1 相机路径下同样为逆 View Matrix。
kg_IProjMatmat4Projection Matrix 的逆矩阵。
kg_CameraBlockPosvec3Camera 整数方块坐标部分。
kg_CameraOffsetvec3精度拆分偏移;绝对坐标为 kg_CameraBlockPos - kg_CameraOffset

使用 Fragment 坐标时先按 Viewport 归一化:

glsl
uniform vec4 U_ViewPort;

vec2 viewportUV = (gl_FragCoord.xy - U_ViewPort.xy) / U_ViewPort.zw;

这样同一 Shader 才能同时适配游戏全屏、Photon 编辑器子窗口和降分辨率 Pass。读取 Scene Depth 并重建位置时使用 kg_IProjMatkg_IViewMat 和精度拆分的 Camera Position。

自己编写 Vertex Shader

一般直接使用 ldlib2:fast_blit。如果必须自定义,Pass 使用 DefaultVertexFormat.POSITION,最小接口为:

glsl
#version 150

in vec3 Position;
out vec2 texCoord;

void main() {
    gl_Position = vec4(Position.xy, 0.0, 1.0);
    texCoord = Position.xy * 0.5 + 0.5;
}

Vertex 和 Fragment 的 varying 名称、类型必须匹配。Fullscreen Quad 已覆盖 NDC -1..1,不需要再乘普通世界对象的 Model Matrix。

缓存与排错

  • Shader JSON 的接口和编译后的 ShaderInstance 都按 Shader ID 缓存。
  • Resource Reload 会清除缓存;仅修改文件但不 Reload 不会重新解析 Port。
  • 先用 fragColor = texture(Input, texCoord) 验证连线,再增加 Depth、Mask 和数学逻辑。
  • JSON 必须是合法 JSON,不能包含注释和尾随逗号。
  • Shader 加载失败、JSON 无法读取或 sampler 漏连时,查看 latest.log 与 Render Graph 编译错误。

完整的 Pass/Target 连接规则见 Render Graph 与 Pass;不需要手写 GLSL 时使用 Fullscreen Graph

Released under the MIT License.