SwitchingColor Shader

Have you already seen the Nissan Juke webspecial? I came across this microsite on the Away3D¬†showcase. Great artwork, neat programming. As I was playing a lot with shaders, I was immediately interested in the incredible shaderwork that was done here. Especially the “color switching” effect (when you click on a colored area of the car and select a new color), caught my attention. After some hours of tinkering I came up with a shader, which looks quite the same. Have a look! Just click on the block anywhere, to switch to a random color.

So whats the idea behind it? Starting from the origin of the change there are two things happening:

  • a color transition is expanding concentrically
  • and a wave-like bulge is expanding concentrically
As we have a change in color and shape, we have to do some vertex and fragment programming. The color is changed by the fragment shader, and the shape morphing is done from the vertex shader.
We start with a switchColor function inside the shaderclass, which initiates the whole process. It should be pretty self explanatory:
</div>
<div>

public function switchColor(newc:Vector3D, neworigin:Vector3D):void
 {
 // save starttime, to avoid subtraction to zero in renderEvent, decrease about 1
 startTime = getTimer() - 1;
 // set previous new color to the current one
 this.currentColor = this.newColor;
 // set new color and origin of transformation
 this.newColor = newc;
 this.origin = neworigin;
 // set colors and origin on the shader
 this.params.currentcolor.value[0] = currentColor.x;
 this.params.currentcolor.value[1] = currentColor.y;
 this.params.currentcolor.value[2] = currentColor.z;

 this.params.newcolor.value[0] = newc.x;
 this.params.newcolor.value[1] = newc.y;
 this.params.newcolor.value[2] = newc.z;

 this.params.origin.value[0] = neworigin.x;
 this.params.origin.value[1] = neworigin.y;
 this.params.origin.value[2] = neworigin.z;
 }

</div>
<div>

The concentric expansion is dependent from the start time (t0) of the transition. So the more time elapsed since t0, the larger the distance from the origin. This has to be calculated only once every frame, so we do the calculation on the actionscript side in our SwitchingColorShader class. We simply subtract the afore saved startTime from the currentTimer. And we divide through a timeStretch value, to have some control over the speed of the change.


params.timedelta.value[0] = (getTimer() - startTime) / timeStretch;

Thats about it from the actionscript side. The rest is done inside the actual shader (FLSL). Let’s have a look at it:


/**
 * Flare3D Layer Shader Language v1.0.
 * @author Jonas Volger
 */

< namespace:"flare", name:"SwitchColorFilter" >

// current and new target color of object
public float4 currentcolor = float4(1,0,0,1);
public float4 newcolor = float4(0,0,1,1);
// the origin of the color change
public float4 origin = float4(0,0,0,1);
// time since change started
public float1 timedelta = float1(0.5);
// the width of the transition
public float1 spread = float1(6);
// view projection
public WORLD_VIEW_PROJ worldViewProj;
// world projection (transform local vertex coordinate into world space)
public WORLD world;
// position ob currently rendered vertex
input POSITION position;
// the normal of the current vertex/surface
input NORMAL normal;
// position in world of the current fragment
interpolated float4 iwPosition;
// factors/intensity of the colors at the current fragment
interpolated float4 factors;
private float4 vertexProgram()
{
 // take position of current vertex and multiply it with the world
 float4 iwposition = float4 (position,1) * world;
 // calculate distance between current position and distance to color change origin
 float1 distance = length(iwposition.xyz - origin.xyz);
 distance /= 3; // adjust it slightly, just for aesthetic reasons
 factors = float4(0,0,0,1); // init factors

 // calculate factors of current colors
 float1 f1 = saturate((distance - timedelta) / spread);
 float1 f2 = saturate(-((distance - timedelta) - spread) / spread);
 // save in interpolation variable
 factors.x = f1;
 factors.y = f2;
 // calculate modified surface normal, if its on the color transition, it gets translated outside
 // Optional: inverted "-sin" to "sin" to make it a dent!
 // if we are outside the transition, so either f1 or f2 is zero, the length becomes 0!
 float4 mynormal = float4 (normal * world,1) * -sin(f1 * f2 / 2);
 // add normal to world position!
 iwposition = float4 (position,1) + mynormal;
 // return position of vertex for final rendering
 return iwposition * worldViewProj;
 // delete all temporal variables!
 delete iwposition;
 delete distance;
 delete mynormal;
}

private float4 fragmentProgram()
{
 // multiply current color with its factor
 float4 color = factors.x * currentcolor;
 // add second color, according to its strength factor
 color += factors.y * newcolor;
 // and return it!
 return color;
 // as always, free the registers
 delete color;
}

technique "perVertex"
{
 vertex vertexProgram();
 fragment fragmentProgram();
}

Lets start in the vertex program. First, we calculate the world position of the current vertex and the distance to the origin to the origin of the color change:

float4 iwposition = float4 (position,1) * world;
float1 distance = length(iwposition.xyz - origin.xyz);

Then, we calculate the factors of the colors. We calculate the color factors for each vertex just once and pass the values into an interpolated variable.

float1 f1 = saturate((distance - timedelta) / spread);
float1 f2 = saturate(-((distance - timedelta) - spread) / spread);

This calculates the smooth transition between them. We saturateeach result, to have it in the range of 0 to 1, which corrensponds with 0% and 100%. I created a little graphic to explain everything a little better:

Now we have the color factors, which are passed into the fragment part of the shader.

As the second step, we utilize the color factors to calculate the bulge.

float4 mynormal = float4 (normal * world,1) * -sin(f1 * f2 / 2);

If you look at the graphic, you see the light blue curve. Its what you approximately get, if you multiply f1 and f2 and calculate the SIN of that. Well, you won’t get exactly that result, but something like it. At least a bulge.
We now multiply our result with the normal of the surface and add the resulting vector to the world position.

This translates our surface position outside along the surface normal, so it forms a neat bulge at the transition area. Here is another graphic, to make this (hopefully) a little bit more clear:

The part in the fragment is quite unexcited. The interpolated color-factors from the vertex program are used to multiply each color with it and then the results are added up. Thats it. As all important calculations are done in the vertex program, I don’t get into this in detail.

For the result there a some things to keep in mind, before using it:

  • There can always be only one transition at a time. When a second transition is initiated, the current transition is immediatly ended. So some restriction on when a transition can be started should possibly be implemented.
  • With this shader applied to an object, the calculations of the normal offset and the mixing of the current and the new color is active all the time, even if no active transition is visible. This unnecessarily uses GPU power, while not transitioning. So in an real world application one should consider having a class, which switches between this shader and a regular color shader, when no transition is active.

You can get the full code in the Sample Code Project. You find it in the Files section. Its also available on Github. So if you like to contribute, you are more than welcome!

If you have questions, I didn’t explain anything well enough, made a mistake or explained something wrong, please let me know and leave a comment!

16 thoughts on “SwitchingColor Shader

  1. Hi, thanks for sharinh the awesome job. I am so impressed by this exellent shader programming.
    I have a few questions for this. Since I am a complete noob for shader, I am wondering that if there is a way to use a texture in this effect instead of color. Or may be we can use a mouse interaction with the vertex, something like dragging or changing the surface. I wil be glad that you would help
    me. I do want to learn much further in 3D programming.
    Best

  2. Hey Jason!

    Thanks :) I’m glad it is going to have some use.
    Of course it’s possible to use a texture. Instead of the color you could use a texture sample – do you have any examples?
    Interaction with the vertex could be done too, though most of the work must be done outside the shader, like getting the interaction vector from the mouse.
    What exactly are you looking for?

  3. Jonas, please do me a favour. I really need the same vertex effect within a texture filter. For example, there are a few bitmaps which are used for texture of a object. And I would like to change them in order to display each texture. The edges of each texture still blending with a color or the original texture pixels. Is it possible?
    Thanks in advance.

  4. Pingback: switching colour shader with alternativa3d 8 | David E Jones a web developer with a passion for problem solving

Leave a Reply to Gaurav Rane Cancel reply

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

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>