0

Shading Toolbox #1 – Chromatic Split / Aberration

Welcome to Shading Toolbox!

The Shading Toolbox (ST) articles will each highlight a single aspect or technique that you can use when writing Shaders. Those are not neccesarily exclusive to the framework or engine I will work in (which will be unity or the unreal engine), as the majority of the shader code will be written in CgFX or in later installments HLSL, but most of these posts will be focused around shaders for Unity and  the Unreal Engine 4.

I will try to split those articles into two parts the basic CgFX implementation (which should be easily translatable to HLSL or GLSL or whatever other shading language you use.) and second, a Use-Case or the necessary computations within the engine or framework to get this shader up and running.

With this said, I hope you will enjoy this series. Feel free to contribute ideas for future ST posts and happy coding!

Chromatic Split

A chromatic split on a texture is a very underrated effect. If used with care we can give the whole image an air of surreality without attracting unneccessary attention to the splitting itself, an effect that will maybe look as if the lens we are looking through is slightly damaged. This works great in horror games btw. and is infact an effect we know as „Chromatic Aberration“. We can also go crazy and create glitch styled postprocessing that will make our users think their monitor is ready for a flight out of the nearest window by splitting the RGB channels really far apart.

Luckily, implementing a basic Chromatic Split or Aberration (as seen above) is really easy.

The Theory:

A Chromatic Split of an image or texture is nothing else than shifting the whole red, blue and green values by a few pixels.

If we were to displace the whole image, we would take a pixel and override it with a pixel that lies a bit farther to the right. If we do this for every pixel in the image, we essentially shift the whole image a bit to the left.

However, we don’t want to shift the whole image, we want to displace a single channel, so we sample the red value of the pixel which lies farther to the right and override our current pixel’s red value with the value we just sampled. Thus, we shift the whole red spectrum of our image to the left.

We repeat this process for the green and blue channels and we are done.

When shading, we inspect every single pixel (fragment) of our image seperately, so our fragment function in CgFX would look something like this:

fixed4 frag (v2f i) : SV_Target
{
    // Get the actual color for this pixel
    half4 actColor = tex2D(_MainTex, i.uv);

    // Sample R G and B of adjacent pixels
    half4 rightRed = tex2D(_MainTex, i.uv.xy + (0.01 * _SplitIntensity * _SplitFactorRed));
    half4 leftGreen = tex2D(_MainTex, i.uv.xy - (0.01 * _SplitIntensity * _SplitFactorGreen));
    half4 diagBlue = tex2D(_MainTex, i.uv.xy + (0.02 * _SplitIntensity * _SplitFactorBlue));

    actColor.r = rightRed.r;
    actColor.g = leftGreen.g;
    actColor.b = diagBlue.b;

    return actColor;
}

Note that I do the shift diagonally and that I use a different factor for the blue channel, so the red and blue shift don’t overlap and thus cancel each other out.

There is really not much more to that. I declared a few additional variables to help me manipulate the effect from the editor or on runtime, the default values are 1 for every varaible.

Chromatic Split Post-processing within Unity

In order to use a Chromatic Split as a Postprocessing effect within Unity we would have to create a Shader for it within Unity and create a corresponding C# script.
As for the shader, we can simply create a new Image Effect Shader from the context menu, and write our fragment function as seen above. (Don’t forget to declare the additional variables!)

Maker sure to remember the Unity shader Path of your new shader, as we will need that in a minute. This path is located at the very top of you .shader file. In my case:

// begin file

Shader "Hidden/ChromaSplit"
{

The basic procedure for post processing is to retrieve the image the camera is currently rendering and apply another shading step on top of it. Usually we provide a material for that, which has our shader applied.

So let us do that. Also, let’s make sure we can manipulate our effect on runtime by exposing a few public variables.

using UnityEngine;
using System.Collections;

[ExecuteInEditMode]
public class IE_ChromaSplit : MonoBehaviour
{
    private Shader m_shader;
    private Material m_material;

    [Range(-3, 3)]
    public float SplitIntensity = 0;
    public float SplitFactorRed = 1;
    public float SplitFactorGreen = 1;

    public float SplitFactorBlue = 1;

    void Awake()
    {
        // Provide your shader path!
        m_shader = Shader.Find("Hidden/ChromaSplit");
        m_material = new Material(m_shader);
    }
}

Now for the actual Post processing.
Unity has a message called „OnRenderImage“, which is called, you guessed it, whenever an image is rendered on screen. We can use this message to apply our image effect.

void OnRenderImage(RenderTexture source, RenderTexture dest)
{
    m_material.SetFloat("_SplitIntensity", SplitIntensity);
    m_material.SetFloat("_SplitFactorRed", SplitFactorRed);
    m_material.SetFloat("_SplitFactorGreen", SplitFactorGreen);
    m_material.SetFloat("_SplitFactorBlue", SplitFactorBlue);

   Graphics.Blit(source, dest, m_material);
}

In this instance, the source image is the image we get from the camera, the destination image is the one that the camera will render after we are done with this method.
We set all the properties of our shader, which is important of course, and then call Graphics.Blit with our shader.

Now what Graphics.Blit does is, it takes the source texture and copies it into the destination texture. If we provide a material, the image will be processed using said material while copying.

Now add that script to your camera and you’re good to go!

That’s all for today, thanks for reading!

 

Alexander

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.