カスタムポストエフェクト
In this tutorial, you'll learn how to create a custom watercolor post effect in PlayCanvas that applies a softening filter and a paper grain texture to your scene. By the end of this guide, you'll have a visually appealing watercolor effect that you can apply to any 3D scene.
Step 1: Setting Up Your Shaders
First, we need to create the shaders that will define our watercolor effect. You'll create two shader assets: a vertex shader and a fragment shader.
Vertex Shader (watercolor.vert)
The vertex shader will pass the UV coordinates from the vertices to the fragment shader. Create a new shader asset in PlayCanvas and name it watercolor.vert. Then, copy and paste the following code:
attributevec2 aPosition;
varyingvec2 vUv0;
voidmain(void)
{
gl_Position =vec4(aPosition,0.0,1.0);
vUv0 =(aPosition.xy +1.0)*0.5;
}
Fragment Shader (watercolor.frag)
The fragment shader will apply the watercolor effect using the color buffer texture and UV coordinates. Create another shader asset named watercolor.frag and insert the following code:
precisionmediumpfloat;
// The texture containing our rendered scene
uniformsampler2D uColorBuffer;
// The UV coordinates passed from the vertex shader
varyingvec2 vUv0;
// Function to create a simple paper grain texture
floatpaperTexture(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;
}
voidmain(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;
}
Step 2: Creating the Watercolor Effect Script
Now, you'll create a script to apply the shaders to your scene. Create a new script in PlayCanvas and name it watercolor.js. Paste in the code provided:
//--------------- POST EFFECT DEFINITION------------------------//
classWatercolorEffectextendspc.PostEffect{
constructor(graphicsDevice, vs, fs){
super(graphicsDevice);
this.shader=newpc.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------------------------//
varWatercolor= 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 =newWatercolorEffect(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);
});
};
Remember to parse the script so that the Editor knows about the script's attributes!
Step 3: Applying the Effect to a Camera
To see your watercolor effect in action, you need to apply it to a camera in your scene:
- Create a new entity with a camera component if you haven't already.
- Add a script component to the camera entity and assign the watercolor script to it.
- Assign the
watercolor.vertandwatercolor.fragshader assets to the corresponding attributes in the watercolor script component.
Now, when you play your scene, you should see the watercolor effect applied, giving your scene a soft, artistic look.
See the Custom Post Effects project here.