Click here to Skip to main content
15,613,250 members
Articles / Programming Languages / C#
Posted 6 Sep 2007


57 bookmarked

SamplePamper Series - Part 1: 3D-Light, Flat-shading and Rotations

Rate me:
Please Sign up or sign in to vote.
4.79/5 (17 votes)
12 Sep 2007CPOL8 min read
This article is one in a series for those who want to know about 3D when it comes to calculation of the light falling on a surface. It will explain in an easy manner for those who don't want to see all those graphical formulas in Greek, as they can be very confusing. Not just for use in C#


This article is Part One of the Sample Pamper series and will apply to those of you who want to know about 3D programming when it comes to shading and lightning of a surface. The most irritating thing for a beginner as I can recall is to see all formulas and signs as we might not know much about them and are only confused by them. Therefore I wanted to ease the pain and show everything more concrete with some complete examples without embedding it into nice objects with interfaces, matrices and the use of DirectX / OpenGL or Glut. Although, there will still be formulas, they will not be the graphical ones in Greek. So there is no fancy code in these examples, but just the important things for learning.

So... this article will describe:


You will need a computer and a calculator to try to find the results by yourself. You must have either Visual Studio .NET or Macromedia Flash 8 installed to try the downloadable source codes. Paper and a pen would be good to be able to do the matrix multiplications. Then you're ready to go!

The story

Screenshot - flatshade1.gif

When we're drawing a surface in a 3D-environment and want to calculate how the light should fall, we can then use the cross product of two vectors on that surface to get the Normal vector and the Dot product to compute the angle between the Normal and the light vector. What the Normal vector is will be explained soon, hold on.

Back to top?

The example

This example is a "Flat-shade" which means that the illumination stays constant across the surface. The Normal vector is not actually constant across the polygon surface as we assume when using the "Flat-shading", but it is the first step into the other level of knowledge. Now pretend that we are calculating the light for this surface:

Screenshot - coord1.gif

Here we must know the location of the points (P 0-2) of the surface and the direction and position (LP 0-1) of the light coordinates in space. We can imagine that P0 has the coordinates x=5, y=10 and z=-6 and the point P2 is x=8, y=1 and z=-3, and finally the third point P1 which has the coordinatesx=2.5, y=6 and z=4.

Back to top?

Calculating vector length

Screenshot - coord2.gif

We must now also calculate the length of U and V that will give us the following values:

Ux = (P2x – P1x) = (8 – 2.5) = 5.5
Uy = (P2y – P1y) = (1 - 6) = -5
Uz = (P2z – P1z) = ((-3) - 4) = -7

And now we will do the same for the V vector:

Vx = (P2x – P0x) = (8 - 5) = 3
Vy = (P2y – P0y) = (1 - 10) = -9
Vz = (P2z – P0z) = ((-3) - (-6)) = 3

The Normal vector

To be able to calculate the light against this surface, we must also have one more vector to know how much light will fall on it. This vector is called the Normal vector. The Normal vector is a vector that will stand out 90 degrees from the surface and to find this vector, we must use something called the Cross product, that is a mathematical way of handling already existing number coordinates on a surface to retrieve this Normal one. In this figure you can see the Normal vector sticking out from the surface front side and being lit on the backside by the light. U is the vector between P1 and P2 and V is the vector between point P2 and P0. The Cross product that follows will use these vectors U and V to build the N vector.

Back to top?

The cross product

U X V = |U| * |V| * sin * n

Where |U| denotes the length of vector U and |V| of vector V, and sin is the angle between them, and n is a unit vector.

Crossing the matrix

Screenshot - crossmatrix.gif
To get Nx, cross like the lines in the matrix, explained more below.

There are more things to add into this, but I'm going to exclude them because they are not needed, like the tree unit vectors parallel to the x, y, and z-axes. We compute the cross product of U and V and start by retrieving the Normal vectors x direction (Nx) by crossing in the matrix like this:

Nx = Uy * Vz – Uz * Vy =  (-5 * 3) – (-7 * -9) = -78

Then we are going to do the same for Ny, like this:

Ny = Uz * Vx – Ux * Vz = (-7 * 3) – (5.5 * 3) = -37.5

And finally to find the (Nz) which should be a negative value in our case (seen in the picture with the surface):

Nz = Ux * Vy – Uy * Vx = (5.5 * -9) – (-5 * 3) = -34,5

In these numbers, you can see that the normal vector will have a negative path in the x, y, and z–axes. This points down to the left and into the picture.

To normalize

Then you must also normalize it by dividing by the length to get the n (which is a normalized normal (N) vector).

|N| = sqrt(Nx^2 + Ny^2 + Nz^2) = sqrt(78^2 + 37.5^2 + 34.5^2) = 93.17

n = N / |N| = N / 93.17

nx = Nx / |N| = -78 / 93.17 = -0.84
ny = Ny / |N| = -37.5 / 93.17 = -0.4
nz = Nz / |N| = -34.5 / 93.17 = -0.37

Back to top?

The light vector

Now we must compute the vector of the light as we will set to the point (LP0) x=12, y=8, z=0 and (LP1) to x=5, y=6, z=0, and then calculate the vector parts like in the following:

Lx = (LP0 x – LP1x) = (12 - 5) = 7
Ly = (LP0y – LP1x) = (8 – 6) = 2
Lz = (LP0z – LP1z) = (0 - 0) = 0

And this must also be normalized to make sense to us.

lx = Lx / |L| = 7 / sqrt(7^2+2^2+0^2) = 0.96
ly = Ly / |L| = 2 / sqrt(7^2+2^2+0^2) = 0.28
lz = Lz / |L| = 0 / sqrt(7^2+2^2+0^2) = 0

Back to top?

The Dot product

There we have it, now you can plug this into another mathematical function, as is the Dot product. We will use this equation to find the angle between the normal and the light vector and it looks like this:

N . L = |N| * |L| = nx * lx + ny * ly + nz * lz

x = -0.84 * 0.96 + -0.4 * 0.28 + -0.37 * 0 = -0.92

A negative value means that the light is falling on the backside of the surface as you can see in the picture. Now if you take the inverted sine of the result of x, you'll get the angle between the light and the normal vector around –67 degrees, which means that the surface is being lit on its backside by a 67 degree angle. Now you can use this x variable to multiply it by the color of the surface to get the shading effect you want. This is called flat shading and is not used so much anymore because it gives the surface a false look when it is lit by a constant all over it. However, it is a good start to get to know about putting light into the picture.

Back to top?

The rotation matrices

I will also explain rotation around the x and y-axes here because I've implemented it in the .zip downloadable source code file. It will rotate the polygon surface around the x and y-axes. If you know how to do a multiplication of matrices then it will be easy for you to add the third one to also be able to rotate around the z-axis on your own. This will be briefly explained in this following part.

I guess you have heard about the rotation matrices before, they look like this.

Screenshot - crossmatrix.gif
Screenshot - yrot.gif

And yes, there is one more for the rotation round the z-axis, but I will exclude it from this article.

The matrix multiplication

Screenshot - matrixmulti.gif

The matrix to the left is rotation around the x-axis and the one to the right for rotation round the y-axis. Now as you can see in the GIF animation above, we are multiplying these matrices together which will result into the following matrix.

Screenshot - resultmatrix.gif

Now you have your combined matrix for rotation around both the x and the y-axis at the same time. We leave the fourth column and row out because those are only needed for translation. To start rotating your surface, you must plug the points P(0-2) and the new matrix into a new formula.

P0newX = (P0x * CoY) + (P0y * 0) + (P0z * -siY)
P0newY = (P0x * (siX * siY)) + (P0y * coX) + (P0z * (siX * coY))
P0newZ = (P0x * (coX * siY)) + (P0y * -siX) + (P0z * (coX * coY))

This will give you the new position of the point P0 and you'll have to do the same for all the remaining points to rotate.

P1newX = (P1x * CoY) + (P1y * 0) + (P1z * -siY)
P1newY = (P1x * (siX * siY)) + (P1y * coX) + (P1z * (siX * coY))
P1newZ = (P1x * (coX * siY)) + (P1y * -siX) + (P1z * (coX * coY))

And the point P2 is as follows:

P2newX = (P2x * CoY) + (P2y * 0) + (P2z * -siY)
P2newY = (P2x * (siX * siY)) + (P2y * coX) + (P2z * (siX * coY))
P2newZ = (P2x * (coX * siY)) + (P2y * -siX) + (P2z * (coX * coY))

That's it! To see the example, choose the zip that will suit you and download it to play around with it.

Back to top?

Points of Interest

There are two samples to play around with, one in C# and the other for Macromedia Flash 8. If you want to step even further and learn about the reflection vector, you can hold your eyes wide open for Part Two of this series.

Back to top?


  • Version 1.0, uploaded 6 September, 2007
    Two versions included for the article, one for C# and the other for Macromedia Flash 8

Back to top?



  • "Tricks of the Windows Game programming gurus – Fundamentals of 2D and 3D game programming". Vol1 - (SAMS) by André LaMothe ISBN: 0-672-31361-8


Back to top?


This software is provided 'as-is' without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose including commercial applications. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. And never could you claim that it is yours.

Back to top?


This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Written By
Sweden Sweden
Professional programmer, degree in Informatics and Applied Systems Science.

Comments and Discussions

GeneralThe best example code in my life Pin
dummy@pawelwoo.com16-Jun-11 13:51
dummy@pawelwoo.com16-Jun-11 13:51 
GeneralRe: The best example code in my life Pin
Windmiller20-Sep-11 10:16
Windmiller20-Sep-11 10:16 
GeneralExcellent Pin
andalmeida6-Sep-07 5:07
andalmeida6-Sep-07 5:07 
QuestionRe: Excellent Pin
Windmiller8-Sep-07 3:23
Windmiller8-Sep-07 3:23 
AnswerRe: Excellent Pin
andalmeida8-Sep-07 3:34
andalmeida8-Sep-07 3:34 
Hey, thanks Smile | :)

Anderson J. Almeida
Systems Analyst

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.