Tips to create your custom image-effect in WaveEngine

Image Effects are very often used to enhance the final look of our games, in WaveEngine we have worked to create an Image Effect extension with the common image effects used in games and optimized for mobile devices. So the default values used for each of these effects are tested in iOS, Android and Windows Phone devices, but you can change these to get more quality results if you target other platforms like Windows, MacOS or Linux.

Here is the complete list of image effects developed by the WaveEngine Team:

  • Antialiasing
  • Bloom
  • ChromaticAberration
  • ColorCorrection
  • Convolution
  • DepthOfField
  • Distortion
  • FastBlur
  • FilmGrain
  • FishEye
  • Fog
  • GaussianBlur
  • Glow
  • GrayScale
  • Invert
  • LensFlare
  • LightShaft
  • MotionBlur
  • Pixelate
  • Posterize
  • RadialBlur
  • SSAO
  • Scanlines
  • ScreenOverlay
  • Sepia
  • Sobel
  • Tiling
  • TiltShift
  • ToneMapping
  • Vignette

And the complete code of all these image effects is open source and is published in our github account,

Sometimes to get better performance you need to mix some of these image effects in only one or to get a special look you need to create a custom image-effect, so in this article we will show you some tips and considerations that you need to keep in mind while you do so.

A starting point is to download the WaveEngine.ImageEffect github repository and study some of the image effects in there.

There are some very simple ones like Invert, Sepia or GrayScale.

We are going to analyze the last one. If you open the folder called GrayScale you will see two csharp files in there:

And the default values method:

Used to initialize all the parameters after the serialization.

Also GrayScaleLens contains the main method call render:

Where this lens sets the source rendertarget buffer as an input texture of the GrayScaleMaterial and uses the helper method RenderToImage to render a quad in screen and apply the shader to the source image. Each lens has two fields called Source and Destination that represent the input texture source and the output texture destination. This is useful because you can apply multiple lenses and the output of the first lens will be the input of the next lens. It makes it very easy for the final user to use lenses from the Wave Visual Editor.

The GrayScaleMaterial represents a class that holds the shader code and the parameters of this. In WaveEngine we use HLSL directly (DirectX platforms) and GLSL (OpenGL platforms) to write shaders, so if you want to write a new shader that will be supported in all platforms you will need to write the HLSL version and GLSL version of your shaders. Both languages are very similar and the most important thing is that you can apply specific optimization in each language.

At the beginning of GrayScaleMaterial you can see how the techniques array is initialized:

Here you specify where is the vertexshaders and pixelshaders code, parameters:

  • “GrayScale”: this is the name of this technique, this should be unique.
  • “ImageEffectMaterial”: In this case we will use the default vertexshader code located in the ImageEffectMaterial folder.
  • “ImageEffectvsImageEffect”: The name of the default vertex shader function.
  • Empty: The pixel shader code is located in the folder called like the GrayScaleMaterial class.
  • “GrayScalepsGrayScale”: the name of the pixel shader function.

This material doesn’t have parameters, in case that you need to pass some parameters you can see how to do that in the Sepia image effect. You need to create a struct where you hold all values, see the struct SepiaEffectParameters. This struct will be multiple of 16 bytes, in HLSL you need to create a constantbuffer with all parameters in the same order and in GLSL it is necessary that each of these parameters are represented like fields of this class because they will be passed as uniform to the shader after serializing the material.

Finally, you need to write the shader in HLSL and GLSL, the system will search for them in the current assembly, as an embedded resource inside the next hierarchy:

  • Shaders
    • GLSL
      • GrayScaleMaterial
    • HLSL
      • GrayScaleMaterial

If you navigate to the HLSL folder, you will see 3 files:

  • CompileShaders.cmd: is used to compile from command line using fxc the GrayScale.fx file.
  • GrayScale.fx: contains the HLSL code of the Sepia ImageEffect.

  • GrayScalepsGrayScale.fxo: Is the result of compile the GrayScale.fx with fxc and the file that we need to mark as Embedded resource.

Inside the GLSL folder you will find:

We only need the fragment shader because we are using the default vertex shader located in the ImageEffectMaterial folder.

  • GrayScalepsGrayScale.frag has the same name of the fxo HLSL code generated but with different extension, and contains the GLSL code of the sepia image effect. We need to mark as embedded resource as well.

If you follow this guide and conventions the material system will be able to find the source code of the correct shader for each platform.

Once you have finished your custom image effect, you can test how it works directly from the Wave Visual Editor as you can see in the next video.

Published by


Javier is a Computer Science Engineer who has always had a passion for 3D graphics and software architecture. He enjoys imparting talks about technology and has contributed in many important software and video game events. He has participated in multitude of software projects involving multitouch technologies, innovative user interfaces, augmented reality, and video games. His professional achievements include being MVP for Windows DirectX and DirectX XNA for the last eight years, Xbox Ambassador, as well as Microsoft Student Partner and Microsoft Most Valuable Student during his years at college. Currently he works at Plainconcepts and he is the development team lead at WaveEngine project.

Leave a Reply

Your email address will not be published. Required fields are marked *