Skip to main content

I’m not fat, I’m inflated

Or

Inflation and deflation


(Fine, balloons)

What are inflation and deflation?

Literally, they mean expanding and contracting (respectively).

Up until now, we usually mainly manipulated things pixel creation. While it’s true that most of the output we desire is mainly falls on the pixel program to perform (since we usually want the object to retain its shape) we can also manipulate vertices.

So, what does this have to do with normals?
One of the easiest (and most amusing) examples you’ll usually encounter in shading languages teaching material in early stages (this guide included) is how to move vertices along their own normal.

In inflation, we move the vertex along its normal outwards, and in deflation - the opposite.
What difference is there between just scaling and inflation/deflation?

Consider the following example;
Source:
Image

If we were to scale it, the result will be the obvious:
Image

But it we inflate or deflate it, the result will be:

Image
Inflated and deflated (respectively)

Changing along its own normal, our teapot had changed as if someone blew air in or out of it. This is especially useful when writing multi-pass techniques which require a shell around an object.

A usual female diet

Let’s play around with this trick. First, we need to create the basic code. How do we inflate?

We need to add to each vertex position whatever value we desire, but in the direction of its normal. The simplest way to do so will be to multiply the normal by some factor and combine the new vector with the vertex position.

Actually, it’s quite simple:

Copy to clipboard
input.position.xyz += input.normal * [some factor]; output.position = mul(input.position, worldViewProj_m);


Notice I added the factor to XYZ. Since W is not meant to signify one of the axes (W is added to allow multiplication with matrices)

So, let’s complicate things a bit. Let’s use time to inflate and deflate over time. (Hence the female diet)

In order to use time we can use the TIME semantic in FXC. After we’re finished, we’ll learn how to send time from OGRE.

Problem with time is that it just keeps growing. To negate that, you can just use some trig function. I picked cos.
There isn’t much to tell, I’ll just show you a minimal code:

Copy to clipboard
float4x4 worldViewProj_m : WorldViewProjection; float time_v : time; struct vertexIn { float4 position : POSITION; float3 normal : NORMAL; }; struct vertexOut { float4 position : POSITION; }; vertexOut mainVS(vertexIn input) { vertexOut output = (vertexOut)0; input.position.xyz += input.normal * cos(time_v)/20; output.position = mul(input.position, worldViewProj_m); return output; } float4 mainPS() : COLOR { return float4(1.0, 1.0, 1.0, 1.0); }


It doesn’t get simpler than that. I also divided the cos(time) by 20 to make the change more subtle.

Remember to press the play button to “run” your shader and see how it changes.

Image
…These buttons (the two on the left..). I’ll bet you forgot all about it.

It’s about time

See what I did there? It’s a pun…

So, the real reason you’re here. Seeing something swell and shrink can be entertaining, but after you finished playing with the numbers and laughing like a dumbass it doesn’t scratch your brain much.

Let’s make it burn some of your IQ. Getting time from OGRE via material script is easy in theory, but it might be obscure if you don’t know what all those things mean.
I’ll make it shorter for you.

OGRE has a few methods to retrieve time via script, text taken strait from the OGRE manual:

the OGRE manual wrote:

time
The current time, factored by the optional parameter (or 1.0f if not supplied).
time_0_x
Single float time value, which repeats itself based on "cycle time" given as an 'extra_params' field
costime_0_x
Cosine of time_0_x
sintime_0_x
Sine of time_0_x
tantime_0_x
Tangent of time_0_x
time_0_x_packed
4-element vector of time0_x, sintime0_x, costime0_x, tantime0_x
time_0_1
As time0_x but scaled to 0..1
costime_0_1
As costime0_x but scaled to 0..1
sintime_0_1
As sintime0_x but scaled to 0..1
tantime_0_1
As tantime0_x but scaled to 0..1
time_0_1_packed
As time0_x_packed but all values scaled to 0..1
time_0_2pi
As time0_x but scaled to 0..2*Pi
costime_0_2pi
As costime0_x but scaled to 0..2*Pi
sintime_0_2pi
As sintime0_x but scaled to 0..2*Pi
tantime_0_2pi
As tantime0_x but scaled to 0..2*Pi
time_0_2pi_packed
As time0_x_packed but all values scaled to 0..2*Pi

FAQ

What’s that cycle thing there?
You supply a maximum value and time count from 0 till it reaches it, when it does, its back to 0 again.

What’s the 0_1?
I really hope you were not thinking of asking that …“As time0_x but scaled to 0..1 “ shouldn’t really leave any room for interpretations.

0_2PI? What is?
If you haven’t learned that yet (or forgot), 2 times PI is a complete circle in radians. This means that you can either get the original angle in radians or trig it.

Try

Try each one of these, see what happens. I used for this specific example costime_0_2pi – this way I didn’t have to produce the cosine from my code.
Remember to add the extra param to define the cycle (in seconds)

My code:
Hlsl file:

Copy to clipboard
struct vertexIn { float4 position : POSITION; float3 normal : NORMAL; }; struct vertexOut { float4 position : POSITION; }; vertexOut mainVS( vertexIn input, uniform float4x4 worldViewProj_m, uniform float time_v ) { vertexOut output = (vertexOut)0; input.position.xyz += input.normal * time_v; output.position = mul(worldViewProj_m, input.position); return output; } float4 mainPS() : COLOR { return float4(1.0, 1.0, 1.0, 1.0); }


Material file:

Copy to clipboard
vertex_program vv2 hlsl { source grow.hlsl entry_point mainVS target vs_2_0 default_params { param_named_auto worldViewProj_m worldviewproj_matrix param_named_auto time_v costime_0_2pi 10 } } fragment_program pp2 hlsl { source grow.hlsl entry_point mainPS target ps_2_0 } material growingm { technique { pass { vertex_program_ref vv2 {} fragment_program_ref pp2 {} } } }


My cycle is 10 seconds long. What’s yours???