For those who want it, you can get a copy of the code from either the Wave Rendering Itch Page or from the Github.
- For reasons that I'll get into later in this post, this was made without the use of Shaders. So all of this runs on the CPU instead of the GPU. The effects made by this code would be much more efficient as a Shader and I'd recommend for anyone using Ren'py 7.4 and above, specifically the model-based rendering, that you use Shaders instead. You'll likely get far better performance out of those than with this.
- Given the above, be aware that this can cause a lot of lag on some systems. If it's only having to deal with a couple of strips, it should be fine. But when you get into the hundreds of strips to handle, it can become a performance bottleneck.
- Later into this I'll provide a rundown on ways to mitigate these issues should they arise for you.
So the effects to emulate were:
- Palette Cycling
- Horizontal Oscillation
- Interleaved Oscillation
- Vertical Oscillation
- Unlike the SNES, we're not limited to only horizontal strips. We can apply this principle with vertical strips as well! You can enable this style with the WaveImage class by providing horizontal = False as a parameter.
- Interleaved Oscillation usually creates holes in the image. While interesting when used on sprites or things shown above a background, this can create issues when used as your primary background. A good way to get around this though is to apply the same strip on both waves so they can mask each other. You can enable this by providing the double = True in WaveImage. If you just want Interleaved Oscillation however, just have to provide double = "interleaved" as a parameter instead.
- I've also included a way to reduce or increase the amount of the horizontal offset with each strip with the damp parameter for WaveImage. This works by multiplying the offset by a variable called extreme each strip. Extreme starts out at 1.0 but will approach damp with each strip.
- Extreme can come in and out as well. Making it so the wavey effect starts and stops on an interval. You can enable this by including extreme_sine = True. However, to simplify things, if damp is set to around 1, the default, then the starting value of extreme is used throughout instead of extreme trying to target damp when it's at 0. If damp is set to anything else, extreme will continue to target it, regardless of the starting value. If you'd prefer different behaviour, you may want to modify this.
- Since extreme changes with each strip as the process proceeds, I've made it so you can specify a direction boolean, which will dictate which side the stripping process begins on.
- We can also do the stripping at arbitrary angles by making use of Transforms. By rotating the initial render, then applying our strip offsets, and then undoing our original rotation, all the strips will have been applied at an angle. I've included the WaveImageUnrotate and WaveImageRotate classes to demonstrate this.
It should be noted though that the use of Transforms adds some overhead to the rendering process. So one may want to set strip_height to > 1 for this. There can also be some artifacts as well. Mostly in the form of slight holes between the strips. If I find a better fix for this in the future I'll update the project.
- Increasing the "height" of each strip will cut down substantially on the amount of time it takes to render, at the cost of some smoothness. But even increasing the strip height from 1 to 2 will half the amount of time it takes. For the WaveImage class, just include strip_height = [int] as a parameter to make use of this. It's also recommended that if you're targeting mobile, to maybe even automatically do this within the class.
- Make sure the strips are going across the smallest side. If an image is wider than it is tall, horizontal strips are recommended. And vertically if it's taller than it is wide.
- Only strip as much as you need. If you only want to have the effect cover part of an image, then provide a start and end number so it only covers that range.
- Applying this to a smaller image and then scaling it up to the size you want can help a lot. Especially if you're doing pixel art for your game since using nearest_neighbor during the zoom will keep each pixel crisp. This is what I did with the Earthbound backgrounds I used in the demo.
However, in order to be feature-complete with the video, I've decided to include it as well. It's pretty basic. Just need to have your image broken down into several layers of pixels that will take on each color in the palette. And then provide the list of colors to cycle through. You can check out the example in the project if you'd like to give it a try.
- Thanks to Minoh Workshop for being cool with me using their sprite for demo purposes. Check out their VN at https://minoh.itch.io/minotaur-hotel
- Sunset Image borrowed from https://www.flickr.com/photos/rcweir/11074856536/ and modified to fit screen resolution and demonstrate melt effect.
- Earthbound backgrounds copyrighted to Nintendo. Please don't sue.