Distortion Effect

| | August 8, 2015

I’m trying to make a distortion effect for a space game. The idea is that the effect has a central point, and all nearby objects appear to swirl into that point. I’ve made a preliminary version and it looks like this:

Screenshot of the effect

I’m pretty much satisfied with this effect, but the problem is that it takes far too long — about 150 ms — and I need to render it every second or third frame, at least. How can I achieve a similar effect efficiently in Java2D? (If you’re interested, my source code is here: http://pastebin.com/1W9RrA44)

2 Responses to “Distortion Effect”

  1. Kevin gave you a good list of what you really need to do first to improve the performance of the code. However I would just throw out the code and implement the effect on higher level so that Java can utilize native code and GPU.

    Radial blur

    Based on the screenshot it looks like the effect you are looking for is radial blur with rotation. These can be implemented by first rendering the scene to an offscreen image instead of directly to the screen and then repeatedly rendering the image to the screen with scaling and rotation. These are operations that Java can accelerate on the GPU automatically and you might get even better performance by creating the offscreen image as VolatileImage. Make sure you are using bilinear filtering for better quality: g.setRenderingHints(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);

    In pseudo-code the algorithm would look something like:

    render scene to offscreen_image
    for i in 1 to N
        scale = any function that returns 1 for the first image and approaches zero when i grows.
        rotation = i * constant
        render offscreen_image to screen with scale and rotation
    endfor
    

    When rendering the offscreen_image to the screen, you also probably want to fade it to background color a little. In Java you can do this with AlphaComposite.
    This should be enough to make your effect fast enough. However there is still room for improvement.

    Feedback loop

    The rendering time of the implementation above is proportional to N. In some cases, mainly if your frame rate is constant, you can further accelerate the rendering by keeping the previous rendering result in the offscreen image and rendering only one copy of the offscreen image. This makes the effect very fast, but the dependency of previous rendering results might be undesirable. This also limits the shape of the scaling and rotation functions. The pseudo-code for that would be:

    render scene on top of offscreen_image_1 without clearing
    scale = constant_1
    rotation = constant_2
    render offscreen_image_1 to offscreen_image_2 with scale and rotation
    render offscreen_image_2 to screen
    swap offscreen_image_1 and offscreen_image_2
    

    Scale and rotation constants can still vary based on time / framecounter in this version.

    You might have seen this effect in action in some music player visualization (Winamp, Media Player etc.). Here is an example video: https://www.youtube.com/watch?v=jasdVfbAFVw#t=237s.

  2. Per-pixel image processing on the CPU is something where microƶptimization is worth thinking about from the start. You’re doing memory allocation and object initialization on a per-pixel basis; this is probably most of your problem.

    The first thing you should do is figure out how to use only arrays of primitives (byte[] etc.) to perform your computation.

    • Do not use ArrayList, but only arrays.
    • Do not use Integer, but only int, byte, etc.
    • Do not use arrays of arrays, but use a flat array and compute coordinates like arr[x + width * y].
    • Do not allocate any arrays (new byte[]) inside a loop.

    Once you have this taken care of (and it’ll probably help a lot), the next step is to get rid of your single-pixel image access operations (getRGB and setRGB). Instead, get an entire array of pixels from the image and write back the same way; I’m sure BufferedImage has operations to retrieve and store the image data as arrays, but I’m not familiar enough with it to recommend specific methods.

Leave a Reply