» Tutorial Effects - Normals
This site relies heavily on Javascript. You should enable it if you want the full experience. Learn more.

Tutorial Effects - Normals

Italian | Mandarin | Japanese

TOC: Of Effects and Shaders
Back: Vertex Data
Next: Geometry Morphing


Besides the position and the texture coodinates, the surface normals are the third important and most commonly used data field of the vertex. The normal is a 3d vector which is perpendicular to the surface curvature at the vertex position.
The normals are very important for light calculations, when the light vector is reflected on the surface to get the shading of the surface.

Displaying normals

VVVV has a helper module to display the surfave normals of a mesh: Normals (EX9)

We can modify the help patch of it to get a nice view of the mesh with its normals:

Calculating normals

If your mesh does not have normals stored with it you can use the node Normals (EX9.Geometry Mesh) to calculate them in the patch before you pass the mesh to the shader. Use the Smoothing Angle to average the normals at the vertex postion.

As most shading models (like phong) need the normal in view space, one has to transform the normal along with the vertex position. But you have to take care that the normal is a float3 or if its a float4, the w component of the normal is 0 (instead of 1 for positions), that it only gets rotated, but not translated. A typical vertex shader does the following transformations:

...
    //normal in view space
    Out.NormV = normalize(mul(NormO, tWV));
 
    //position (projected)
    Out.PosWVP  = mul(PosO, tWVP);
    ...

Recalculating normals

It gets more complicated if you change the vertex positions in the vertex shader like we did in the mr. wiggle or the function printing tutorial. Because the translation of the vertex positions changes the surface curvature, the normals stored previously in the mesh are not valid anymore. And therefore shading calculations are wrong.

So, if you need lightning calculations in the pixelshader you have to find a way to recalculate the normals. But this is a very specific problem, which depends on the method that you use to translate the positions.

The most general, but not so precise way to get the new vertex normal is to calculate 2 additional positions which are close by and get two vectors which are in the tangent plane at this vertex and are not parallel. From the two tangent vectors you can calculate the normal with the cross product:

  • calc current pos p
  • calc neighbour pos n1
  • calc neighbour pos n2
  • get tangent vector t1 = p - n1
  • get tangent vector t2 = p - n2
  • get normal n = t1 x t2

Using the cone example from the function printing tutorial it looks like:

float NeighbourOffset = 0.001;
 
vs2ps VS(
    float4 PosO  : POSITION,
    float4 TexCd : TEXCOORD0)
{
    //declare output struct
    vs2ps Out;
 
    //calc new position
    float3 p = Cone(PosO.xy);
 
    //calc neighbour pos in u direction
    float3 n1 = Cone(PosO.xy + float2(NeighbourOffset, 0));
 
    //calc neighbour pos in v direction
    float3 n2 = Cone(PosO.xy + float2(0, NeighbourOffset));
 
    //get tangent vector 1
    float3 t1 = p - n1;
 
    //get tangent vector 2
    float3 t2 = p - n2;
 
    //get normal
    float3 NormO = cross(t1, t2);
 
    //set new pos
    PosO.xyz = p;
    ...

Now, to actually use the normals we could for example use the Phong function declared in the PhongDirectional.fxh file from vvvv's effects folder. For that, copy the file besides your shader and then have a look into the PhongDirectional.fx shader to get the necessary lines to get the phong shading up and running.
We need to put the normal, the light and view dir into the view space, and then pass all that in the vs2ps struct to the pixel shader. Let's also copy the code for the texture to get a nice shading setup, where you can just replace the 'Cone' function with all uv-parametric surfaces you can find on the web:

// ----------------------------------------------------------------------------
// PARAMETERS:
// ----------------------------------------------------------------------------
 
//transforms
float4x4 tW: WORLD;        //the models world matrix as via the shader
float4x4 tV: VIEW;         //view matrix as set via Renderer (EX9)
float4x4 tP: PROJECTION;   //projection matrix as set via Renderer (EX9)
float4x4 tWV : WORLDVIEW;
float4x4 tWVP: WORLDVIEWPROJECTION; //all 3 premultiplied 
 
//texture transformation marked with semantic TEXTUREMATRIX
//to achieve symmetric transformations
float4x4 tTex: TEXTUREMATRIX <string uiname="Texture Transform";>;
 
//texture
texture Tex <string uiname="Texture";>;
sampler Samp = sampler_state    //sampler for doing the texture-lookup
{
    Texture   = (Tex);          //apply a texture to the sampler
    MipFilter = LINEAR;         //sampler states
    MinFilter = LINEAR;
    MagFilter = LINEAR;
};
 
//the data structure: "vertexshader to pixelshader"
//used as output data of the VS function
//and as input data of the PS function
struct vs2ps
{
    float4 Pos  : POSITION;
    float2 TexCd : TEXCOORD0;
    float3 LightDirV: TEXCOORD1;
    float3 NormV: TEXCOORD2;
    float3 ViewDirV: TEXCOORD3;
};
 
#include "PhongDirectional.fxh"
 
// ----------------------------------------------------------------------------
// VERTEXSHADERS
// ----------------------------------------------------------------------------
 
 
#define twopi 6.28318531
 
float2 Scale = 1;
float2 Offset = 0;
 
float3 Cone(float2 uv)
{
 
     uv *= Scale;
     uv += Offset;
 
    float u = uv.x * twopi;
    float v = uv.y;
 
    float3 newPos;
    newPos.x = v * cos(u);
    newPos.y = v * sin(u);
    newPos.z = v;
 
    return newPos;
}
 
float NeighbourOffset = 0.001;
 
vs2ps VS(
    float4 PosO  : POSITION,
    float4 TexCd : TEXCOORD0)
{
    //declare output struct
    vs2ps Out;
 
    //calc new position
    float3 p = Cone(PosO.xy);
 
    //calc neighbour pos in u direction
    float3 n1 = Cone(PosO.xy + float2(NeighbourOffset, 0));
 
    //calc neighbour pos in v direction
    float3 n2 = Cone(PosO.xy + float2(0, NeighbourOffset));
 
    //get tangent vector 1
    float3 t1 = p - n1;
 
    //get tangent vector 2
    float3 t2 = p - n2;
 
    //get normal
    float3 NormO = cross(t1, t2);
 
    //set new pos
    PosO.xyz = p;
 
    //put normal in view space and normalize it
    Out.NormV = normalize(mul(NormO, tWV));
 
    //inverse light direction in view space
    Out.LightDirV = normalize(-mul(lDir, tV));
 
    //inverse view dir in view space
    Out.ViewDirV = -normalize(mul(PosO, tWV));
 
    //transform position (projected)
    Out.Pos = mul(PosO, tWVP);
 
    //transform texturecoordinates
    Out.TexCd = mul(TexCd, tTex);
 
    return Out;
}
 
// ----------------------------------------------------------------------------
// PIXELSHADERS
// ----------------------------------------------------------------------------
 
float Alpha <float uimin=0.0; float uimax=1.0;> = 1;
 
float4 PS(vs2ps In) : COLOR
{
    float4 col = tex2D(Samp, In.TexCd);
 
    col.rgb *= PhongDirectional(In.NormV, In.ViewDirV, In.LightDirV);
    col.a *= Alpha;
 
    return col;
}
 
// ----------------------------------------------------------------------------
// TECHNIQUES:
// ----------------------------------------------------------------------------
 
technique TFunctionWithPhong
{
    pass P0
    {
        VertexShader = compile vs_1_1 VS();
        PixelShader  = compile ps_2_0 PS();
    }
}

TOC: Of Effects and Shaders
Back: Vertex Data
Next: Geometry Morphing

anonymous user login

Shoutbox

~5d ago

joreg: Postponed: Next vvvv beginner course starting April 29: https://thenodeinstitute.org/courses/vvvv-beginner-class-summer-2024/

~1mth ago

~1mth ago

joreg: The Winter Season of vvvv workshops is now over but all recordings are still available for purchase: https://thenodeinstitute.org/ws23-vvvv-intermediates/

~1mth ago

schlonzo: Love the new drag and drop functionality for links in latest previews!

~2mth ago

joreg: Workshop on 29 02: Create Sequencers and Precise Clock Based Tools. Signup here: https://thenodeinstitute.org/courses/ws23-vvvv-08-create-sequencers-and-precise-clock-based-tools-in-vvvv-gamma/

~2mth ago

joreg: Workshop on 22 02: Unlocking Shader Artistry: A Journey through ‘The Book of Shaders’ with FUSE. Signup here: https://thenodeinstitute.org/courses/ws23-vvvv-12-book-of-shaders/