I've been testing a variety of shockwave or ripple effect shaders, and spent time on this cleaned up and improved version on shadertoy.

I'm not entirely satisfied with that because it produces an ugly "bubbling" effect in the center. I'm trying to add a stronger "compression" effect to the shockwave itself but everything I do results in a stronger bubbling effect in the center.

I've found an example of what I want which uses an image for the displacement map: example

enter image description here

The author of that example only provided a sample of the shader code which I've been able to port. However, I don't understand how to animate this (to expand outward) over time.

Here's my current port into hlsl for Unity:

Shader "Hidden/Shockwave2"
        _MainTex ("Texture", 2D) = "white" {}
        _GradientTex ("Texture", 2D) = "white" {}
        // No culling or depth
        Cull Off ZWrite Off ZTest Always

            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"

            struct appdata
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;

            struct v2f
                float2 uv : TEXCOORD0;
                float4 vertex : SV_POSITION;

            v2f vert (appdata v)
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv = v.uv;
                return o;

            sampler2D _MainTex;
            sampler2D _GradientTex;

            fixed4 frag (v2f i) : SV_Target
                fixed2 center = fixed2(.5, .5);

                fixed2 dir = normalize(i.uv - center);

                // Get the pixels off of the maps.
                fixed4 gradC = tex2D(_GradientTex, i.uv);

                // Read the pixel from the displaced position.
                fixed2 pos = i.uv;
                pos.x += (gradC.r * 2.0 - 1.0) * 0.025;
                pos.y -= (gradC.g * 2.0 - 1.0) * 0.025;

                return tex2D(_MainTex, pos * dir);

This produces the correct visual, but I need to animate its expansion outward.

  • \$\begingroup\$ how is this not already what you want? it appears to be compression in your example. \$\endgroup\$
    – Krupip
    Dec 21, 2017 at 18:08
  • \$\begingroup\$ The compression effect is much more pronounced. Another aspect I didn't mention is that it doesn't suffer from "bubbling" at the center which mine very much does. I've toyed around with how much the effect reduces over time, and that might be sufficient, but the bubbling effect gets even worse. I should probably rewrite this to focus more on animating that distortion image over time. \$\endgroup\$
    – BotskoNet
    Dec 21, 2017 at 19:14
  • \$\begingroup\$ I've rephrased and clarified the question. \$\endgroup\$
    – BotskoNet
    Dec 21, 2017 at 19:37
  • \$\begingroup\$ I submitted one answer for doing this with out the displacement map, I'll see about how to do it with later. \$\endgroup\$
    – Krupip
    Dec 21, 2017 at 19:49
  • \$\begingroup\$ You get a +1 just for having found a JSFiddle for shaders. \$\endgroup\$ Dec 21, 2017 at 21:09

1 Answer 1


You could avoid the displacement map altogether using something like this:


Basically, i create a radius that represents the wave, and make the sample the uv + direction * (coef/(coef + (signed_difference_from coord to wave)). You shouldn't have what you call bubbling here and it should in effect compress. You could layer multiple of these on top of one another as well to get multi waves. I also have a factor that is a function of the radius of the wave that reduces the power of it as it goes out.

You can get bubbling back by doing the negative of dir, in order to pull textures from the opposite direction, and you can remove part of the effect on either side by only distorting uv if the radius of the current uv is < or > than the wave radius.

If you are really set on the displacment map route, you can accomplish this by dividing the values used for the sampler over time then add that value to your current uv position. Something like this:

void mainImage( out vec4 fragColor, in vec2 fragCoord ) {
    float slowiTime = iTime/2.0;
    // Sawtooth calc of time
    float offset = (slowiTime - floor(slowiTime)) / slowiTime;
    float time = slowiTime * offset;

    // Find coordinate, flexible to different resolutions
    float maxSize = max(iResolution.x, iResolution.y);
    vec2 uv = fragCoord.xy / maxSize;

    // assuming getting in as vec4, and assuming the values are not signed, 
    //we need to offset values to get signed values out, assuming only one 
    //value in texture actually used, make sure not to tile the texture, so that all 
    // value outside the map itself turn to zero.
    // may need to translate if source of wave not in center of screen
    vec2 displacment_map_value = texture([your_displacement_texture], uv/time).xy;
    displacement_map_value -= halfway_offset;
    // should be zero if not in the bubble, or outside the texture itself
    distorted_uv += displacment_map_value;
    texture_color = texture([your_actual_texture], distorted_uv);

    fragColor = texture_color;

Note you will probably have to write the entire scene to some frame buffer and use that as a source texture for your actual texture to get this effect to appear on top of everything you want it to.

What this does is at the start, make sure only the center fragments are actually grabbing from the bubble (as uv/time will be large) then as time goes on uv/time will be smaller and all coordinates will be chosen closer and closer to the center of the image (creating the expanding effect). The displacement map should already have the direction encoded into it from the center, if you need it moved use a translation, but regardless all you should have to do is add the displacement value to your uv to get a proper sample. magnitude should be encoded into the xy pair retrieved from the displacement map as well.


You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .