跳到主要内容

自定义后处理效果

已弃用

本教程使用已弃用的旧版后处理效果。新项目应将该效果实现为自定义 CameraFrame 效果。

本教程将介绍如何在 PlayCanvas 中创建自定义水彩后处理,为场景应用柔化滤镜和纸张颗粒纹理。完成后,你将得到一个美观的水彩效果,可以应用到任意三维场景。

第一步:配置着色器​

首先创建定义水彩效果的着色器,包括顶点着色器和片元着色器两个资源。

顶点着色器(watercolor.vert)​

顶点着色器将顶点的 UV 坐标传递给片元着色器。在 PlayCanvas 中创建着色器资源,命名为 watercolor.vert,然后粘贴以下代码:

watercolor.vert
attribute vec2 aPosition;

varying vec2 vUv0;

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

片元着色器(watercolor.frag)​

片元着色器使用颜色缓冲区纹理和 UV 坐标实现水彩效果。创建另一个名为 watercolor.frag 的着色器资源,填入以下代码:

watercolor.frag
precision mediump float;

// The texture containing our rendered scene
uniform sampler2D uColorBuffer;

// The UV coordinates passed from the vertex shader
varying vec2 vUv0;

// Function to create a simple paper grain texture
float paperTexture(vec2 uv) {
// Create a pseudo-random pattern based on UV coordinates
float grain = fract(sin(dot(uv, vec2(12.9898, 78.233))) * 43758.5453);
// Modulate the grain intensity
grain = smoothstep(0.3, 0.7, grain);
return grain;
}

void main(void) {
// Sample the color from the scene texture at this fragment's UV coordinates
vec4 sceneColor = texture2D(uColorBuffer, vUv0);

// Apply a softening filter to mimic watercolor fluidity
// Blend with neighboring pixels (basic blur)
vec4 blurColor = vec4(0.0);
float offset = 0.003; // Offset for neighboring pixels; adjust for blur amount
for (int x = -1; x <= 1; x++) {
for (int y = -1; y <= 1; y++) {
blurColor += texture2D(uColorBuffer, vUv0 + vec2(x, y) * offset);
}
}
blurColor /= 9.0;

// Mix original color with blurred version
vec4 mixedColor = mix(sceneColor, blurColor, 0.5);

// Overlay the paper texture
float grain = paperTexture(vUv0 * 10.0); // Tiling of the grain texture
mixedColor.rgb += mixedColor.rgb * grain * 0.1; // Modulate to adjust intensity

// Output the final color
gl_FragColor = mixedColor;
}

第二步:创建水彩效果脚本​

接下来创建脚本,将着色器应用到场景。在 PlayCanvas 中新建 watercolor.js,粘贴以下代码:

//--------------- POST EFFECT DEFINITION------------------------//
class WatercolorEffect extends pc.PostEffect {
constructor(graphicsDevice, vs, fs) {
super(graphicsDevice);

this.shader = new pc.Shader(graphicsDevice, {
attributes: {
aPosition: pc.SEMANTIC_POSITION
},
vshader: vs,
fshader: fs
});
}

// Every post effect must implement the render method which
// sets any parameters that the shader might require and
// also renders the effect on the screen
render(inputTarget, outputTarget, rect) {
// Set the input render target to the shader. This is the image rendered from our camera
this.device.scope.resolve("uColorBuffer").setValue(inputTarget.colorBuffer);

// Draw a full screen quad on the output target. In this case the output target is the screen.
// Drawing a full screen quad will run the shader that we defined above
pc.drawFullscreenQuad(this.device, outputTarget, this.vertexBuffer, this.shader, rect);
}
}


//--------------- SCRIPT DEFINITION------------------------//
var Watercolor = pc.createScript('watercolor');

Watercolor.attributes.add('vs', {
type: 'asset',
assetType: 'shader',
title: 'Vertex Shader'
});

Watercolor.attributes.add('fs', {
type: 'asset',
assetType: 'shader',
title: 'Fragment Shader'
});

// initialize code called once per entity
Watercolor.prototype.initialize = function() {
const effect = new WatercolorEffect(this.app.graphicsDevice, this.vs.resource, this.fs.resource);

// add the effect to the camera's postEffects queue
const queue = this.entity.camera.postEffects;
queue.addEffect(effect);

// when the script is enabled add our effect to the camera's postEffects queue
this.on('enable', function () {
queue.addEffect(effect, false);
});

// when the script is disabled remove our effect from the camera's postEffects queue
this.on('disable', function () {
queue.removeEffect(effect);
});
};
备注

记得解析脚本,让编辑器识别脚本属性!

第三步:将效果应用到相机​

要看到水彩效果,需要将它应用到场景中的相机:

  • 如果尚未创建相机,请创建实体并添加相机组件。
  • 为相机实体添加脚本组件,并挂载 watercolor 脚本。
  • 将 watercolor.vert 和 watercolor.frag 着色器资源分配给脚本组件中的对应属性。

现在运行场景,就能看到水彩效果,使画面具有柔和的艺术风格。

自定义后处理效果项目见此处。