Skip to content

课程 30 - 后处理与渲染图

本节课中我们会回顾下传统基于 Shader 后处理的图像处理手段。

基于后处理的效果

基于 Shader 可以实现常见的图像处理效果,例如高斯模糊、Perlin 噪音、Glitch 等,当然还有最近火热的“液态玻璃”:

source: https://help.figma.com/hc/en-us/articles/360041488473-Apply-effects-to-layers
Adjust in Photoshop Web

更多效果详见

在实现中,Pixi.js filters 会根据对象的包围盒计算应用区域,将对象渲染到一个临时的渲染纹理(render texture)上,然后再对该纹理应用着色器效果。

渲染到 RenderTarget 中

在 Pixi.js 中

ts
// src/filters/FilterSystem.ts
const quadGeometry = new Geometry({
    attributes: {
        aPosition: {
            buffer: new Float32Array([0, 0, 1, 0, 1, 1, 0, 1]),
            format: 'float32x2',
            stride: 2 * 4,
            offset: 0,
        },
    },
    indexBuffer: new Uint32Array([0, 1, 2, 0, 2, 3]),
});

采样

接下来需要从整个画布的纹理中,对目标图形所在的区域进行采样:

glsl
void main() {
  v_Uv = a_Position * (u_OutputFrame.zw / u_InputSize.xy) + u_OutputFrame.xy / u_InputSize.xy;

  gl_Position = vec4((a_Position * 2.0 - 1.0) * u_OutputTexture.xy / u_OutputFrame.zw * u_OutputTexture.z, 0.0, 1.0);
}

全屏三角形

这里就可以使用全屏三角形了,相比 Quad 可以减少一个顶点:

ts
this.#bigTriangleVertexBuffer = this.device.createBuffer({
    viewOrSize: new Float32Array([1, 3, -3, -1, 1, -1]),
    usage: BufferUsage.VERTEX,
    hint: BufferFrequencyHint.DYNAMIC,
});

Vertex shader 非常简单,只需要正确映射 v_Uv 纹理坐标即可:

glsl
void main() {
  v_Uv = 0.5 * (a_Position + 1.0);
  gl_Position = vec4(a_Position, 0.0, 1.0);

  #ifdef VIEWPORT_ORIGIN_TL
    v_Uv.y = 1.0 - v_Uv.y;
  #endif
}

Brightness

我们可以使用 CSS filter 语法,例如 filter: brightness(0.4);

glsl
uniform sampler2D u_Texture;
in vec2 v_Uv;

layout(std140) uniform PostProcessingUniforms {
  float u_Brightness;
};

out vec4 outputColor;

void main() {
  outputColor = texture(SAMPLER_2D(u_Texture), v_Uv);

  outputColor.rgb *= u_Brightness;
}

渲染图

Render Graph(有时称为 FrameGraph)是一种将渲染过程抽象为有向无环图(DAG)的现代渲染架构。在这一架构下,每个渲染 Pass 以及它们使用的资源都被视为图节点与边,通过图结构自动管理资源状态转换、同步和生命周期。

Frame graph example

实现

Render graph in bevy

rs
// @see https://docs.rs/bevy/latest/bevy/render/render_graph/struct.RenderGraph.html
let mut graph = RenderGraph::default();
graph.add_node(Labels::A, MyNode);
graph.add_node(Labels::B, MyNode);
graph.add_node_edge(Labels::B, Labels::A);

Released under the MIT License.