He hit me!

Or

Basic lighting


You know what joke they always do when they start teaching lights right? There’s no need for me to say “lights on!” or “let there be light!” ….if I had to choose I’d pick “can has light? Kthxbi”

Before we begin

The three basic lighting channels


Ambient is the base color added to all surfaces. In deep space for instance, this color is complete blackness. An analogy on earth would be all light hitting an object which doesn’t originate directly from a light source, and thus is weakened and close to even on all spots. (light reflected from other objects)

Diffuse is light hitting an object directly from a light source and spreads evenly through the surfaces, depending on the surface angle towards the source.

The most common algorithm for diffuse is Lambertian reflectance, which we will use in our basic lighting shaders. Lambertian uses the dot product between the surface normal and the view direction vector to calculate diffuse.
It produces a smooth and gradual (and somewhat rough, like uncoated wood) lighting texture.

Specular is light reflected in a specular angle, creating the “shiny spot” (or strip) that is seen in reflective surfaces. The size and distribution of the specular depending on the “shininess factor” we provide, and the algorithm we use. This highlight is dynamic, and changes location and spread according to the angle from which the user views the object from.

The two most commonly used methods for specular are Phong and Blinn-Phong, we will learn them both.

What are normals?

A normal is a vector that points perfectly upwards from the surface (orthogonal). It indicates the direction to which the surface (or a specific point across it) is facing.

A normalized vector

Is a vector which all of his components run between 0 and 1 in value keeping their original proportions compared to the vector's magnitude. Essentially, they are clamped so that the length of the vector will be 1 (a unit vector).
The magnitude of a 2d vector will be: √( X2 + Y2) (Pythagoras)
A vector (2, 1) when normalized will become (2/√5, 1/√5)

The dot product

The dot product equals the multiplication of two vector’s magnitudes by each other and the cos(α) between them (|A|*|B|*cos(α)) as if they had originated from the same spot.

This function helps us determine the directional relationship between two vectors.

On unit vectors, the result is always between 1 and -1 (since it uses a cos function):
1. If the two vectors are orthogonal to each other, the result is 0
Image
2. If two vectors are in opposite directions the result is -1 (Note, opposite can be facing each other or from each other)
Image
3. If vectors are facing the same directions the result is 1
Image
4. If the angle between two vectors is sharp (acute), the result is between 0 and 1;
5. If the angle between two vectors is blunt (obtuse), the result is between 0 and -1;

A quick look


Image
Ambient, lambertian diffuse and Phong specular (per vertex) separately

and combined:
Image
(Note, this isn’t a combination of the 3 above, because they combine ugly)

Gouraud shading (per vertex)

Gouraud shading is the simplest, fastest lighting method that combines ambient, diffuse and specular channels to light an object depending on the angle between the direction of the light source and the vertex’s normal.

For this shader, we will use Lambertian model for diffuse, and Phong reflection model for specular.

Adding ambient… or the new RED RING OF DEATH

… It can also be square ring of death… Or a tea pot…

As for ambient, it’s not very difficult. Just declare a COLOR and call it ambientColor. Give it whatever color you wish, but you might not want to make it very intense (0.125 is enough).

Diffuse

Now things will start getting a bit more complicated.
We need to take into consideration a few parameters:

  1. A desired diffuse color
  2. The position of the light
  3. The angle in which the light hits the surface


1 and 2 can be done swiftly:

float4		diffColor 	: COLOR;
float4 		lightPos;		// something like (200 150 0 0) will suffice


Since we will be setting these variables by hand in FXC, remember to either insert values in the declaration or in the material properties.

The plan:

In order to calculate the diffuse color of the vertex, we need to:

  1. Receive the vertex normal
  2. Calculate the direction in which the light hits the vertex
  3. Calculate their dot product
  4. Use the dot product as a factor for the diffuse color
  5. Pass the combined color (diffuse + ambient) to the pixel shader


1: adding normal to vertex input
we need to add a normal to the vertex shader input:

struct vertexIn
{
	float4 position : POSITION;
	float3 normal	: NORMAL;
};


2: calculate the light direction:
In order to do so, we need to subtract vertex’s world position from the light’s world position.
We also normalize the light direction, because its true magnitude doesn’t matter, and that way we can use it as a direction.

//vertex world position
float3 worldpos = mul(input.position, world_m);

//normalized direction vector
float3 lightDir	= normalize( lightPos - worldpos );


3: dot product
HLSL provides a function to calculate the dot product of two vectors:

//vertex world position
float dotNL 	= dot ( lightDir, input.normal );


4: make a diffuse color factor

//this value will later be multiplied by diffCollor to create a color vector
Float 	diff 	= saturate(dotNL);


Note that I used a function called saturate. This function clamps the value to between 0 and 1 (this is not equivalent to abs()! Values below 0 will become 0 and above 1 will become 1)

I did that because I don’t want my values to turn negative or exceed 1, because I’m accumulating color values. A negative dot product will sabotage ambient color by negating it where it doesn’t have any effect (such the opposite side of the light)

5: The state of things
At the moment, our program should look something like this:

float4x4 	worldViewProj_m : WorldViewProjection;
float4x4 	world_m		: World;

// make sure you fill these three values
float4 	lightPos;
float4	ambientColor	: COLOR;
float4	diffColor	: COLOR;

struct vertexIn
{
	float4 position : POSITION;
	float3 normal	: NORMAL;
};

struct vertexOut
{
	float4 position : POSITION;
	float4 finColor	: COLOR;
};

struct pixelIn
{
	float4 finColor	: COLOR;
};

vertexOut mainVS(vertexIn input)
{
	vertexOut output = (vertexOut)0;
	
	output.position = mul(input.position, worldViewProj_m);
	float3 worldpos = mul(input.position, world_m);
	
	float3 lightDir	= normalize( lightPos - worldpos );
	
	float 	dotNL 	= dot ( lightDir, input.normal );
	float 	diff 	= saturate(dotNL);
	
// combine the two lights
	output. finColor = diff * diffColor + ambientColor;
	return output;
}

float4 mainPS( pixelIn input ) : COLOR
{
	return input.lightColor;
}


Although you should be seeing sharp edged vertices, FXC seem to soften these. But you can see a somewhat murky line of vertices along the transition line to the dark side.

Specular

How do we calculate specular according to phong? We need to take into consideration these few things:

  1. The specular reflection angle of the light along the vertex.
  2. The angle of the camera

Specular reflection

Phong model provides the simplest, most strait forward algorithm for specular reflection.
It goes like this:
(Theory only, actual code will come later):

R = (2 * dotNL * N) – L

R : specular reflection vector;
dotNL : dot product of vertex normal and light direction vectors;
N : vertex normal vector;
L : light direction vector;

Line by line

Let’s see why and how, and don’t worry – it’s all pictures! Even a hamster can catch up!

Step 1: dot
Let's use two simple 2d vectors, separated by 45 degrees
Image
As mentioned, the dot product is cos(45)

Step 2: midway vector
Image
Our midway vector value is now: (0, 2*cos(45) )
(Do not confuse this with half-way vector used in Blinn)

Step 3: the reflection vector
To achieve a reflection vector, we will have to add a negative L vector (or subtract L, however you see it)
Image
The R vector is our reflection vector, and it equals ( -cos(45), cos(45) )
Its values are opposite to L along X.

Calculating the view

The second part of creating specular is to calculate the factor of the view angle (after we calculated the directional vector from the vertex to the camera) on the specular distribution depending on the angle between the reflection ray and the direction to the view, and according to a predefined factor we choose in advance.

In order to do that, we take the dot product of the direction of the view and the reflection vector, and pow() it by desired “shininess”.
Image

V – The view’s direction
dotVR – Dot product of V and R

The actual code

So, now we know what to do, let’s do it!

First thing we have to do, is to add a global variable that will automatically track the camera position (when we export to ogre we will have one provided from ogre, don’t worry)

float4  cameraPos  :  CameraPosition;


Secondly, we need to calculate the direction from the vertex to the camera:
(again, since it’s a directional vector, we normalize it)

float3 viewDir	= normalize( cameraPos - worldpos );


Now we calculate the reflection vector:

float3 	ref 	=  (input.normal * 2 * dotNL) - lightDir;


( dotNL and lightDir had already been calculated, you didn’t forget right? It's not like you fell asleep by now or something)

Lastly, we calculate the dotVR (view and reflection dot), calculate the specular light, and add the product to the output color:

float	dotRV	= dot(ref, viewDir);
float 	spec 	= pow(saturate(dotRV), [your specular factor here] );
	
output.finColor = diff * diffColor + spec * specColor  + ambientColor;


Note that I used saturate on dotRV as well. If I wouldn’t have, we would have a dark spot that mirrors the specular in position and behavior swallowing ambient light in the back of the object.

It’s kind of cool though (the ambient devouring blackness), try it.

The state of things

By now, two program looks something like this:

float4	specColor	: COLOR;
…
vertexOut mainVS(vertexIn input)
{
	vertexOut  output = (vertexOut )0;
	
	output.position = mul(input.position, worldViewProj_m);
	float3 worldpos = mul(input.position, world_m);
	
	float3 lightDir	= normalize( lightPos - worldpos );
	float3 viewDir	= normalize( cameraPos - worldpos );
	
	float 	dotNL 	= dot ( lightDir, input.normal );
	float 	diff 	= saturate(dotNL);
	
	float3 	ref 	=  (input.normal * 2 * dotNL) - lightDir;
	float	dotRV	= dot(ref, viewDir);
	float 	spec 	= pow(saturate(dotRV),15);
	
	output.finColor = diff  * diffColor + spec*specColor  + ambientColor;
	return output;
}

float4 mainPS( pixelIn input ) : COLOR
{
	return input.finColor;
}


Our first per vertex shader is now complete!
You can try and add some other factors, such as light intensity or whatever…
Image
The result you should be getting. I divided the diffuse by 3 to make the specular show how ugly it is when using per vertex.

Image
With higher shiny number you can see the effect of per vertex on specular better.

I know, it’s ugly, its supposed to be.

‘Exporting’ to OGRE

Unlike our earlier shaders, this shader actually has substance. This means you have to be more acute with your exporting skillz.
Make sure that:

  1. You provide all needed externals to each program that need them (globals in FXC)
  2. Remember to swap mul() components.

About lights

When retrieving lights from OGRE via the scripts, you need to add an index – this tells ogre which light to choose. The index run from 0 to n, when 0 is the closest to the object and n is the farthest.
Such as:

param_named_auto lightPos light_position 0


When you finish, your files should look something like these:

The HLSL file:

struct vertexIn
{
	float4 position : POSITION;
	float3 normal	: NORMAL;
};

struct vertexOut
{
	float4 position : POSITION;
	float4 finColor	: COLOR;
};

struct pixelIn
{
	float4 finColor	: COLOR;
};

vertexOut mainVS( 		vertexIn input,
			uniform float4x4  worldViewProj_m,
			uniform float4x4  world_m,
			uniform float4 	 cameraPos,
			uniform float4 	 lightPos,
			uniform float4	 ambientColor,
			uniform float4 	 diffColor,
			uniform float4 	 specColor,
			uniform float	 shiny	
		)
{
	vertexOut output = (vertexOut)0;
	
	output.position = mul(worldViewProj_m, input.position);
	float3 worldpos = mul(world_m, input.position);
	
	float3 	lightDir= normalize( lightPos - worldpos );
	float3 	viewDir	= normalize( cameraPos - worldpos );
	
	float 	dotNL 	= dot ( lightDir, input.normal );
	float 	diff 	= saturate(dotNL);
	
	float3 	ref 	=  (input.normal * 2 * dotNL) - lightDir;
	float	dotRV	= dot(ref, viewDir);
	float 	spec 	= pow(saturate(dotRV), shiny);
	
	output.finColor = diff* diffColor + spec*specColor + ambientColor;
	return output;
}

float4 mainPS( pixelIn input ) : COLOR
{
	return input.finColor;
}


The material script

vertex_program vv hlsl
{
	source balrg.hlsl
	entry_point mainVS
	target vs_1_1
	default_params
	{
		param_named_auto 	worldViewProj_m 	worldViewProj_matrix
		param_named_auto 	world_m 		world_matrix
		param_named_auto 	cameraPos		camera_position
		param_named_auto 	lightPos		light_position 0
		param_named_auto 	ambientColor		ambient_light_colour 0 
		param_named_auto 	diffColor		light_diffuse_colour 0
		param_named_auto 	specColor		light_specular_colour 0
		param_named	shiny float 25.0
	}
}

fragment_program pp hlsl
{
	source balrg.hlsl
	entry_point mainPS
	target ps_2_0
}

material textry
{
	technique
	{
		pass
		{
			vertex_program_ref vv
			{
			}
			fragment_program_ref pp
			{
			}
		}
	}
}

<HR>
Creative Commons Copyright -- Some rights reserved.


THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS CREATIVE COMMONS PUBLIC LICENSE ("CCPL" OR "LICENSE"). THE WORK IS PROTECTED BY COPYRIGHT AND/OR OTHER APPLICABLE LAW. ANY USE OF THE WORK OTHER THAN AS AUTHORIZED UNDER THIS LICENSE OR COPYRIGHT LAW IS PROHIBITED.

BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND AGREE TO BE BOUND BY THE TERMS OF THIS LICENSE. THE LICENSOR GRANTS YOU THE RIGHTS CONTAINED HERE IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS AND CONDITIONS.

1. Definitions

  • "Collective Work" means a work, such as a periodical issue, anthology or encyclopedia, in which the Work in its entirety in unmodified form, along with a number of other contributions, constituting separate and independent works in themselves, are assembled into a collective whole. A work that constitutes a Collective Work will not be considered a Derivative Work (as defined below) for the purposes of this License.
  • "Derivative Work" means a work based upon the Work or upon the Work and other pre-existing works, such as a translation, musical arrangement, dramatization, fictionalization, motion picture version, sound recording, art reproduction, abridgment, condensation, or any other form in which the Work may be recast, transformed, or adapted, except that a work that constitutes a Collective Work will not be considered a Derivative Work for the purpose of this License. For the avoidance of doubt, where the Work is a musical composition or sound recording, the synchronization of the Work in timed-relation with a moving image ("synching") will be considered a Derivative Work for the purpose of this License.
  • "Licensor" means the individual or entity that offers the Work under the terms of this License.
  • "Original Author" means the individual or entity who created the Work.
  • "Work" means the copyrightable work of authorship offered under the terms of this License.
  • "You" means an individual or entity exercising rights under this License who has not previously violated the terms of this License with respect to the Work, or who has received express permission from the Licensor to exercise rights under this License despite a previous violation.
  • "License Elements" means the following high-level license attributes as selected by Licensor and indicated in the title of this License: Attribution, ShareAlike.

2. Fair Use Rights

Nothing in this license is intended to reduce, limit, or restrict any rights arising from fair use, first sale or other limitations on the exclusive rights of the copyright owner under copyright law or other applicable laws.

3. License Grant

Subject to the terms and conditions of this License, Licensor hereby grants You a worldwide, royalty-free, non-exclusive, perpetual (for the duration of the applicable copyright) license to exercise the rights in the Work as stated below:

  • to reproduce the Work, to incorporate the Work into one or more Collective Works, and to reproduce the Work as incorporated in the Collective Works;
  • to create and reproduce Derivative Works;
  • to distribute copies or phonorecords of, display publicly, perform publicly, and perform publicly by means of a digital audio transmission the Work including as incorporated in Collective Works;
  • to distribute copies or phonorecords of, display publicly, perform publicly, and perform publicly by means of a digital audio transmission Derivative Works.
  • For the avoidance of doubt, where the work is a musical composition:
    • Performance Royalties Under Blanket Licenses. Licensor waives the exclusive right to collect, whether individually or via a performance rights society (e.g. ASCAP, BMI, SESAC), royalties for the public performance or public digital performance (e.g. webcast) of the Work.
    • Mechanical Rights and Statutory Royalties. Licensor waives the exclusive right to collect, whether individually or via a music rights society or designated agent (e.g. Harry Fox Agency), royalties for any phonorecord You create from the Work ("cover version") and distribute, subject to the compulsory license created by 17 USC Section 115 of the US Copyright Act (or the equivalent in other jurisdictions).
    • Webcasting Rights and Statutory Royalties. For the avoidance of doubt, where the Work is a sound recording, Licensor waives the exclusive right to collect, whether individually or via a performance-rights society (e.g. SoundExchange), royalties for the public digital performance (e.g. webcast) of the Work, subject to the compulsory license created by 17 USC Section 114 of the US Copyright Act (or the equivalent in other jurisdictions).


The above rights may be exercised in all media and formats whether now known or hereafter devised. The above rights include the right to make such modifications as are technically necessary to exercise the rights in other media and formats. All rights not expressly granted by Licensor are hereby reserved.

4. Restrictions

The license granted in Section 3 above is expressly made subject to and limited by the following restrictions:

  • You may distribute, publicly display, publicly perform, or publicly digitally perform the Work only under the terms of this License, and You must include a copy of, or the Uniform Resource Identifier for, this License with every copy or phonorecord of the Work You distribute, publicly display, publicly perform, or publicly digitally perform. You may not offer or impose any terms on the Work that alter or restrict the terms of this License or the recipients' exercise of the rights granted hereunder. You may not sublicense the Work. You must keep intact all notices that refer to this License and to the disclaimer of warranties. You may not distribute, publicly display, publicly perform, or publicly digitally perform the Work with any technological measures that control access or use of the Work in a manner inconsistent with the terms of this License Agreement. The above applies to the Work as incorporated in a Collective Work, but this does not require the Collective Work apart from the Work itself to be made subject to the terms of this License. If You create a Collective Work, upon notice from any Licensor You must, to the extent practicable, remove from the Collective Work any credit as required by clause 4(c), as requested. If You create a Derivative Work, upon notice from any Licensor You must, to the extent practicable, remove from the Derivative Work any credit as required by clause 4(c), as requested.
  • You may distribute, publicly display, publicly perform, or publicly digitally perform a Derivative Work only under the terms of this License, a later version of this License with the same License Elements as this License, or a Creative Commons iCommons license that contains the same License Elements as this License (e.g. Attribution-ShareAlike 2.5 Japan). You must include a copy of, or the Uniform Resource Identifier for, this License or other license specified in the previous sentence with every copy or phonorecord of each Derivative Work You distribute, publicly display, publicly perform, or publicly digitally perform. You may not offer or impose any terms on the Derivative Works that alter or restrict the terms of this License or the recipients' exercise of the rights granted hereunder, and You must keep intact all notices that refer to this License and to the disclaimer of warranties. You may not distribute, publicly display, publicly perform, or publicly digitally perform the Derivative Work with any technological measures that control access or use of the Work in a manner inconsistent with the terms of this License Agreement. The above applies to the Derivative Work as incorporated in a Collective Work, but this does not require the Collective Work apart from the Derivative Work itself to be made subject to the terms of this License.
  • If you distribute, publicly display, publicly perform, or publicly digitally perform the Work or any Derivative Works or Collective Works, You must keep intact all copyright notices for the Work and provide, reasonable to the medium or means You are utilizing: (i) the name of the Original Author (or pseudonym, if applicable) if supplied, and/or (ii) if the Original Author and/or Licensor designate another party or parties (e.g. a sponsor institute, publishing entity, journal) for attribution in Licensor's copyright notice, terms of service or by other reasonable means, the name of such party or parties; the title of the Work if supplied; to the extent reasonably practicable, the Uniform Resource Identifier, if any, that Licensor specifies to be associated with the Work, unless such URI does not refer to the copyright notice or licensing information for the Work; and in the case of a Derivative Work, a credit identifying the use of the Work in the Derivative Work (e.g., "French translation of the Work by Original Author," or "Screenplay based on original Work by Original Author"). Such credit may be implemented in any reasonable manner; provided, however, that in the case of a Derivative Work or Collective Work, at a minimum such credit will appear where any other comparable authorship credit appears and in a manner at least as prominent as such other comparable authorship credit.

5. Representations, Warranties and Disclaimer

UNLESS OTHERWISE AGREED TO BY THE PARTIES IN WRITING, LICENSOR OFFERS THE WORK AS-IS AND MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY KIND CONCERNING THE MATERIALS, EXPRESS, IMPLIED, STATUTORY OR OTHERWISE, INCLUDING, WITHOUT LIMITATION, WARRANTIES OF TITLE, MERCHANTIBILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT OR OTHER DEFECTS, ACCURACY, OR THE PRESENCE OF ABSENCE OF ERRORS, WHETHER OR NOT DISCOVERABLE. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OF IMPLIED WARRANTIES, SO SUCH EXCLUSION MAY NOT APPLY TO YOU.

6. Limitation on Liability.

EXCEPT TO THE EXTENT REQUIRED BY APPLICABLE LAW, IN NO EVENT WILL LICENSOR BE LIABLE TO YOU ON ANY LEGAL THEORY FOR ANY SPECIAL, INCIDENTAL, CONSEQUENTIAL, PUNITIVE OR EXEMPLARY DAMAGES ARISING OUT OF THIS LICENSE OR THE USE OF THE WORK, EVEN IF LICENSOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.

7. Termination

  • This License and the rights granted hereunder will terminate automatically upon any breach by You of the terms of this License. Individuals or entities who have received Derivative Works or Collective Works from You under this License, however, will not have their licenses terminated provided such individuals or entities remain in full compliance with those licenses. Sections 1, 2, 5, 6, 7, and 8 will survive any termination of this License.
  • Subject to the above terms and conditions, the license granted here is perpetual (for the duration of the applicable copyright in the Work). Notwithstanding the above, Licensor reserves the right to release the Work under different license terms or to stop distributing the Work at any time; provided, however that any such election will not serve to withdraw this License (or any other license that has been, or is required to be, granted under the terms of this License), and this License will continue in full force and effect unless terminated as stated above.

8. Miscellaneous

  • Each time You distribute or publicly digitally perform the Work or a Collective Work, the Licensor offers to the recipient a license to the Work on the same terms and conditions as the license granted to You under this License.
  • Each time You distribute or publicly digitally perform a Derivative Work, Licensor offers to the recipient a license to the original Work on the same terms and conditions as the license granted to You under this License.
  • If any provision of this License is invalid or unenforceable under applicable law, it shall not affect the validity or enforceability of the remainder of the terms of this License, and without further action by the parties to this agreement, such provision shall be reformed to the minimum extent necessary to make such provision valid and enforceable.
  • No term or provision of this License shall be deemed waived and no breach consented to unless such waiver or consent shall be in writing and signed by the party to be charged with such waiver or consent.
  • This License constitutes the entire agreement between the parties with respect to the Work licensed here. There are no understandings, agreements or representations with respect to the Work not specified here. Licensor shall not be bound by any additional provisions that may appear in any communication from You. This License may not be modified without the mutual written agreement of the Licensor and You.