# Other types of light

## “Point me to the right direction…”

get it? point like point light, but direction like directional light? ....
.. ah forget it.

Other than point lights, we have two other types of lights in OGRE:

• Directional lights: have no position, only a direction.
• Spotlights: these lights have a ‘corridor cone’ in a direction. They have two cones:
• Inner cone: maximum illumination.

## Directional lights

These lights are probably the easiest thing in the world to calculate. All they have is a direction from which they are coming from (as if originating from infinity and traveling parallel).

All you have to do is ignore the position. That means we have one less calculation. There isn’t much more to tell, just show you the code (even that is redundant). OGRE sends directional lights' direction (xyz) instead of position, with the w component set to 0.

```vertexOut mainVS(vertexIn input)
{
vertexOut output = (vertexOut)0;

float4 worldPos = mul(input.pos, world_m);
//fill output
//------------------------------
output.pos 	= mul(input.pos, worldViewProj_m);
output.normal 	= mul(input.normal, world_m);
output.viewDir	= viewPos - worldPos;
//------------------------------
return output;
}

float4 mainPS(vertexOut input) : COLOR
{
//normalize per pixel
//------------------------------
float3 normal 	= normalize(input.normal);
float3 viewDir 	= normalize(input.viewDir);
//------------------------------
//diff and specular
//------------------------------
float 	dotNL 	= dot(lightPos.xyz, normal);
float4	diff    = saturate(dotNL);

float3 	halfAng = normalize(viewDir + lightPos.xyz);
float 	dotNH	= dot(normal, halfAng);
float 	spec	= pow(saturate(dotNH),50);
//------------------------------

return  diff*diffColor + spec*specColor + ambientColor ;
}```

## Spotlights

Spot lights are a bit more complicated, since they originate from a specific point, project to a specific direction, and have a cone which only in it the object is affected (like a flash light). These points make it somewhat trickier.

Let’s take a look at the spotlight:

To avoid confusion, we will rename lightDir to pixelToLight since the spotlight has a facing direction.

We will use two dot products:

• dotNL : dot between pixelToLight and the surface normal
• dotPLd: dot between the inverse pixelToLight and spotlight direction

We have a few more variables however; when you ask for spotlight_params from OGRE material script, OGRE will send a float4 organized as such:

• cos(innerConeAngle/2)
• cos(outerConeAngle/2)
• falloff value
• 1

The forth value (w component) is used to help differentiate spotlights from point lights.

The cosine uses the half of the angle because the angle defines the cone, and we need only the angle between the side of the cone and the axis (which is the spotlight facing vector).

### Calculating

There are two parts for determining the effect of the cone of light on the object depending on its direction to it:

1. The dotNL must be positive: because otherwise we are either behind the light or on the other side of the object.
2. The dotPLd value:
• The value of the dotPLd equals in value to the cosine between the spotlight facing vector and pixelToLight; since we have the cosine of the angle between the cones' side and their axis (which is the spotlight facing vector) comparing dotPLd with these number is essentially comparing its angle with the cones' (if you look at the drawing again, the angle of the dotPLd is smaller than both cones meaning it lies within the inner cone, while the farthest pixel on the sphere is in the outer cone)
• Diffuse:
• Larger than cos(innerConeAngle/2): the pixel is in the inner cone.
• Value: the diffuse will equal the dotNL.
• Larger than cos(outerConeAngle/2): the pixel is in the outer cone.
• Value: the diffuse will equal the dotNL, multiplied by the relative location of the pixel between the outer and inner cone (will be explained later)
• Otherwise: not in the spotlight’s AOE.
• Specular:
• As long as the dotPLd is positive, the specular is present; this is because the specular reflects the source of the light, which means that as long as pixel is not behind it, the specular will appear.

I ignore the falloff value, because I don’t really know what to do with it.

### Code

The vertex program remained almost unchanged, but take a look at it anyhow:

```vertexOut mainVS(vertexIn input)
{
vertexOut output = (vertexOut)0;

float4 worldPos 	= mul(input.pos, world_m);
//fill output
//------------------------------
output.pos 		= mul(input.pos, worldViewProj_m);
output.normal 		= mul(input.normal, world_m);
output.pixelToLight	= lightPos - worldPos;
output.viewDir		= viewPos - worldPos;
//------------------------------
return output;
}```

The main change is in the pixel program:
We begin the program by normalizing and preparing the dot products:

```float4 mainPS(vertexOut input) : COLOR
{
//prep
//------------------------------
float3 normal 		= normalize(input.normal);
float3 pixelToLight	= normalize(input.pixelToLight);
float3 viewDir 		= normalize(input.viewDir);

float 	dotPLd	= dot(-pixelToLight, spotLightDir);
float 	dotNL 	= dot(pixelToLight, normal);
//------------------------------```

Now, let’s add the diffuse and specular as we described earlier and return the value:

```// lights
//------
float4	diff = 0, spec = 0;
if(dotNL > 0)
{
//diffuse
//------------------------------
if	( dotPLd > spotLightParams.x )
diff 	= dotNL;
else
if	( dotPLd > spotLightParams.y )
diff 	= dotNL * (1-(spotLightParams.x - dotPLd)/(spotLightParams.x - spotLightParams.y));
//------------------------------
// specular
//------------------------------
if (dotPLd > 0)
{
float3 	halfAng = normalize(viewDir + pixelToLight);
float 	dotNH	= dot(normal, halfAng);
spec 	= pow(saturate(dotNH),100);
}
//------------------------------
}
//------
return  diff*diffColor + spec*specColor;
}```

### Outer cone diffuse explained

Why

`dotNL * (1-(spotLightParams.x - dotPLd)/(spotLightParams.x - spotLightParams.y));`

We need to know where the pixel is between the inner and outer cones, but in a relative value (range of 0 to 1).
Therefore, we must first do this:

`spotLightParams.x – dotPLd`

This will give us the absolute difference between the dotPLd (the orthogonal to pixelToLight) and the inner cone limit.

In order to receive a relative value, we divide it by the absolute difference between the inner cone limit and the outer cone limit:

`(spotLightParams.x - dotPLd)/(spotLightParams.x - spotLightParams.y)`

The problem is that the value is inversed (closest will equal 0) so we need to one minus it:

`(1-(spotLightParams.x - dotPLd)/(spotLightParams.x - spotLightParams.y))`

And finally, we need to address the angle towards the source of the light (dotNL):

`dotNL * (1-(spotLightParams.x - dotPLd)/(spotLightParams.x - spotLightParams.y))`

## Differentiating light types

When you ask for light data from OGRE material script, you will receive all lights depending on distance, therefore, you don’t know if it’s a point light, a directional light or a spotlight.

In order to address all three in one shader, we need to know how to differ them; we need three things from the material script:

• Light_position_array
• light_direction_array
• spotlight_params

Each type of light has a different signature (a different combination of values):
Point light:

• light_position : (pos.x, pos.y, pos.z, 1)
• light_direction : not relevant
• spotlight_params : (1, 0, 0, 1)

Directional light:

• light_position : (-dir.x, -dir.y,-dir.z, 0)
• light_direction : not relevant
• spotlight_params : (1, 0, 0, 1)

Spotlight:

• light_position : (pos.x, pos.y, pos.z, 1)
• light_direction : (dir.x, dir.y, dir.z )
• spotlight_params : (cos(innerAngle/2) , cos(outerAngle/2) , falloff, 1)

### “Why is light_direction not relevant in directional light?”

Because it is deprecated for greater flexibility. Instead, its inverse direction is sent in light_position, with the w component set to 0 to differ it from point or spot light.

The only reason it’s not deprecated in spotlights is that they have both direction and position.

## Completionist

As a closing note, lets write a shader that an handle multiple lights of any type!
Try and write it on your own, and don’t give up – it mihgt take a while.
Tips:

• efficiency is key : try and avoid unnecessary work
• Delegate: write sub programs to do the main bulk of the code separately, and leave the work flow in the main program.
• Shader model 3: if the program gets large, you might have to use ps_3_0 and vs_3_0 (otherwise you'll pass the instruction limit).
• Work slowly: do each task at a time; expand on need – make sure what you already wrote works.

### My solution

I wrote my own version for this; it’s a bit more mature than what we wrote up to this point, but all of its elements should be familiar to you. Just follow the work flow line by line (starting with the main programs) and you’ll get it.

• all positions and directions in object space, saving time (spent on transforming before calculating directional vectors) and also helps result accuracy.
• Use of shader model 3.
• Use of inout for the sub programs to make them as seamless with the main program as possible.
• Use of light_distance_object_space instead of manual calculation (trading attenuation accuracy for speed)

material:

```vertex_program lightMasterA_VS hlsl
{
source lightmaster.hlsl
target vs_3_0
entry_point multiTypeLightVS
preprocessor_defines lightCount=5
default_params
{
param_named_auto 	worldViewProj_m 	worldviewproj_matrix
param_named_auto	cameraPos		camera_position_object_space
param_named_auto	lightPoses		light_position_object_space_array	5
}
}

fragment_program lightMasterA_PS hlsl
{
source lightmaster.hlsl
target ps_3_0
entry_point multiTypeLightPS
preprocessor_defines lightCount=5
default_params
{
param_named_auto	lightDirs		light_direction_object_space_array 	5
param_named_auto	SLParamsArray		spotlight_params_array			5
param_named_auto	diffColors		light_diffuse_colour_array		5
param_named_auto	specColors		light_specular_colour_array		5
param_named_auto	lightAttens		light_attenuation_array			5
param_named_auto	lightDists		light_distance_object_space_array	5
param_named_auto	ambientColor		ambient_light_colour
param_named specShine float 55
}
}

material lightMasterA
{
technique
{
pass
{
vertex_program_ref lightMasterA_VS {}
fragment_program_ref lightMasterA_PS {}
}
}
}```

the HLSL file:

```struct vertexIn
{
float4 position : POSITION;
float3 normal	: NORMAL0;
};

struct vertexOut
{
float4 position  		: POSITION;
float3 normal			: TEXCOORD0;
float3 viewDir   		: TEXCOORD1;
float3 pixelToLight[lightCount]	: TEXCOORD2;
};

//lighting sub program, read multiTypeLightPS first(this one is called from it)
//--------------------------------------------------------------------------------
void light(	float3 normal,
float3 viewDir,
float3 pixelToLight,
float3 spotLightDir,
float4 spotLightParams,
float4 diffColor,
float4 specColor,
float4 lightAtten,
float  lightDist,
float  specShine,
inout	float4 diff,
inout	float4 spec
)
{
float 	dotNL	 	= dot(pixelToLight, normal);
float 	luminosity 	= 1 / ( lightAtten.y + lightAtten.z*lightDist + lightAtten.w*pow(lightDist,2));
float3 	halfAng 	= normalize(viewDir + pixelToLight);
float 	dotNH		= dot(normal, halfAng);

// if spotlight params are (1, 0, 0, 1) we have a point, directional or empty light; we handle them the same.
if(spotLightParams.x == 1 && spotLightParams.y == 0 && spotLightParams.z == 0 && spotLightParams.w == 1)
{
spec	+= pow(saturate(dotNH),specShine) * specColor * luminosity;
diff 	+= (saturate(dotNL)) * diffColor * luminosity;
}
else
if(dotNL > 0)
{
//if it was not either of the above, we have a spotlight
float 	dotPLd		= dot(-pixelToLight, spotLightDir);
//diffuse
//------------------------------
if	( dotPLd > spotLightParams.y )
diff += dotNL * (1-(spotLightParams.x - dotPLd)/(spotLightParams.x - spotLightParams.y)) * diffColor * luminosity;
else
if	( dotPLd > spotLightParams.x )
diff += dotNL * diffColor * luminosity;
//------------------------------

// specular
//------------------------------
if (dotPLd > 0)
spec 	+= pow(saturate(dotNH),specShine) * specColor * luminosity;
//------------------------------
}
}
//--------------------------------------------------------------------------------

// main vertex program
//--------------------------------------------------------------------------------
vertexOut multiTypeLightVS(	vertexIn 	input,
uniform	float4x4 	worldViewProj_m,
uniform	float4 		cameraPos,
uniform	float4 		lightPoses[lightCount]
)
{
vertexOut output= (vertexOut)0;

output.position = mul(worldViewProj_m, input.position);
output.normal 	= input.normal;
output.viewDir	= cameraPos - input.position;
int j = 2;

for(int i = 0; i < lightCount; i++)
{
if(lightPoses[i].w == 0)
output.pixelToLight[i] = lightPoses[i].xyz;
else
output.pixelToLight[i] = lightPoses[i] - input.position;
}
return output;
}
//--------------------------------------------------------------------------------

//main pixel program
//--------------------------------------------------------------------------------
float4 multiTypeLightPS(	vertexOut 	input,
uniform float4		SLParamsArray[lightCount],
uniform float3		lightDirs[lightCount],
uniform	float4		diffColors[lightCount],
uniform	float4 		specColors[lightCount],
uniform float4		lightAttens[lightCount],
uniform float4		lightDists[lightCount],
uniform	float4		ambientColor,
uniform float		specShine
) : COLOR
{
float4 diff 	= float4(0, 0, 0, 0);
float4 spec 	= float4(0, 0, 0, 0);

input.viewDir	= normalize( input.viewDir );
input.normal 	= normalize( input.normal );

for(int i = 0; i<lightCount ; i++)
{
input.pixelToLight[i]	= normalize( input.pixelToLight[i] );

light(	input.normal,
input.viewDir,
input.pixelToLight[i],
normalize(lightDirs[i]),
SLParamsArray[i],
diffColors[i],
specColors[i],
lightAttens[i],
lightDists[i],
specShine,
diff,
spec);
}
return ambientColor + diff + spec ;
}
//--------------------------------------------------------------------------------```

The shader in action; red directional, blue point light from below and white spotlight from the left.

<HR>

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.

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.
• 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.