- Home /
Post effect shader similar to "Shannara Chronicles" intro's edge smoke effect
Hi there,
I'd like to create a post effect shader, similar to the "edge smoke" effect from the "Shannara Chronicles" intro (see screenshot: inner blue marked area visualizes the edge, the outer one the smoke). But in contrast to the video, the edge smoke should only be created from high contrast edge areas and only when there is movement, like dust blown of a moving edge or something like that.
My idea was, to detect the edges of high edge contrast areas in the rendered image and use these fragments in combination with the local camera motion vectors, to further "extend" these areas approximately along the motion, to create some sort of mask. This mask would then uncover a simple scrolling/animated texture, which just gets overlayed on top of everything.
I have the edge contrast detection working (see screenshot below) and I can access the motion vectors in those areas. But then I'm a bit lost, how to actually "extend" these areas and draw in them. In other words, how can I somehow draw outside the current fragment? Maybe writing into some buffer for neighboring fragments and in a second pass actually using them?
Also, I'm not sure what to use as a temporary mask (stencil buffer or some other buffer maybe?).
Do you have any ideas on how to accomplish a similar effect? I'm open to all routes :)
Cheers
This is the shader code so far:
Shader "Hidden/FT/MotionPatternsPostEffect"
{
HLSLINCLUDE
//#include "UnityCG.cginc"
#include "PostProcessing/Shaders/StdLib.hlsl"
#include "FTShaderUtils.cginc"
TEXTURE2D_SAMPLER2D(_MainTex, sampler_MainTex);
float4 _MainTex_TexelSize;
float _Blend;
float _ContrastThreshold;
// Camera motion vectors texture
sampler2D _CameraMotionVectorsTexture;
float4 _CameraMotionVectorsTexture_TexelSize;
struct Varyings
{
float4 vertex : SV_POSITION;
float2 texcoord : TEXCOORD0;
float2 texcoordStereo : TEXCOORD3;
};
Varyings Vert(AttributesDefault v)
{
Varyings o;
o.vertex = float4(v.vertex.xy, 0, 1);
o.texcoord = TransformTriangleVertexToUV(v.vertex.xy);
#if UNITY_UV_STARTS_AT_TOP
o.texcoord = o.texcoord * float2(1.0, -1.0) + float2(0.0, 1.0);
#endif
o.texcoordStereo = TransformStereoScreenSpaceTex(o.texcoord, 1.0);
return o;
}
float4 Frag(Varyings i) : SV_Target
{
float4 color = float4(1, 1, 1, 1);
// sample colors of main/middle fragment and 2 neighboring fragments
float4 color0 = SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, i.texcoord);
float4 color1 = SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, i.texcoord + float2(_MainTex_TexelSize.x, 0));
float4 color2 = SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, i.texcoord + float2(0, _MainTex_TexelSize.y));
// calculate luminance/greyscale
float luminance0 = luminance(color0.rgb);
float luminance1 = luminance(color1.rgb);
float luminance2 = luminance(color2.rgb);
// calculate differences to middle fragment
float luminanceDiff01 = abs(luminance0 - luminance1);
float luminanceDiff02 = abs(luminance0 - luminance2);
// if difference is greater than _ContrastThreshold, perform actions for high contrast areas
// high contrast
if(luminanceDiff01 > _ContrastThreshold || luminanceDiff02 > _ContrastThreshold)
{
// Sample the motion vector.
float4 motionVectors = tex2D(_CameraMotionVectorsTexture, i.texcoord);
////////////////
// TODO: do something here with the motion vectors to create an extended mask
////////////////
// debug show high contrast areas
color = float4(1,0,0,1);
// blend everything together
//color = float4(lerp(color.rgb, motionVectors.rgb, _Blend.xxx), 1);
}
// low contrast
else
{
color = color0;
}
return color;
}
ENDHLSL
SubShader
{
Cull Off
ZWrite Off
ZTest Always
Pass
{
HLSLPROGRAM
#pragma vertex Vert
#pragma fragment Frag
ENDHLSL
}
}
}