OGRE Wiki
Support and community documentation for Ogre3D
Ogre Forums
ogre3d.org
Log in
Username:
Password:
CapsLock is on.
Remember me (for 1 year)
Log in
Home
Tutorials
Tutorials Home
Basic Tutorials
Intermediate Tutorials
Mad Marx Tutorials
In Depth Tutorials
Older Tutorials
External Tutorials
Cookbook
Cookbook Home
CodeBank
Snippets
Experiences
Ogre Articles
Libraries
Libraries Home
Alternative Languages
Assembling A Toolset
Development Tools
OGRE Libraries
List of Libraries
Tools
Tools Home
DCC Tools
DCC Tutorials
DCC Articles
DCC Resources
Assembling a production pipeline
Development
Development Home
Roadmap
Building Ogre
Installing the Ogre SDK
Setting Up An Application
Ogre Wiki Tutorial Framework
Frequently Asked Questions
Google Summer Of Code
Help Requested
Ogre Core Articles
Community
Community Home
Projects Using Ogre
Recommended Reading
Contractors
Wiki
Immediate Wiki Tasklist
Wiki Ideas
Wiki Guidelines
Article Writing Guidelines
Wiki Styles
Wiki Page Tracker
Ogre Wiki Help
Ogre Wiki Help Overview
Help - Basic Syntax
Help - Images
Help - Pages and Structures
Help - Wiki Plugins
Toolbox
Freetags
Categories
List Pages
Structures
Trackers
Statistics
Rankings
List Galleries
Ogre Lexicon
Comments
History: White Phong part 3 - JaJDoo Shader Guide - Basics
View page
Source of version: 45
(current)
!:::I’M BLINDED!::: !:::Or::: !:::Other types of light::: {maketoc} !!“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. ** __Outer cone__: gradually fading 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. {CODE(wrap="1", colors="c++")} 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 ; }{CODE} !!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: {IMG(src="display1867")}{IMG} 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: # The __dotNL__ must be positive: because otherwise we are either behind the light or on the other side of the object. # 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: {CODE(wrap="1", colors="c++")} 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; } {CODE} The main change is in the pixel program: We begin the program by normalizing and preparing the dot products: {CODE(wrap="1", colors="c++")} 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); //------------------------------{CODE} Now, let’s add the diffuse and specular as we described earlier and return the value: {CODE(wrap="1", colors="c++")} // 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; }{CODE} !!!Outer cone diffuse explained Why {CODE(wrap="1", colors="c++")}dotNL * (1-(spotLightParams.x - dotPLd)/(spotLightParams.x - spotLightParams.y));{CODE} You ask? 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: {CODE(wrap="1", colors="c++")}spotLightParams.x – dotPLd{CODE} 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: {CODE(wrap="1", colors="c++")}(spotLightParams.x - dotPLd)/(spotLightParams.x - spotLightParams.y){CODE} The problem is that the value is inversed (closest will equal 0) so we need to one minus it: {CODE(wrap="1", colors="c++")}(1-(spotLightParams.x - dotPLd)/(spotLightParams.x - spotLightParams.y)){CODE} And finally, we need to address the angle towards the source of the light (dotNL): {CODE(wrap="1", colors="c++")}dotNL * (1-(spotLightParams.x - dotPLd)/(spotLightParams.x - spotLightParams.y)){CODE} !!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. A few words about 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:__ {CODE(wrap="1", colors="c++")} 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 {} } } } {CODE} __the HLSL file:__ {CODE(wrap="1", colors="c++")} 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 ; } //-------------------------------------------------------------------------------- {CODE} The shader in action; red directional, blue point light from below and white spotlight from the left. {IMG(src="display1868", height="461", width="649" )}{IMG}
Search by Tags
Search Wiki by Freetags
Latest Changes
IDE Eclipse
FMOD SoundManager
HDRlib
Building Ogre V2 with CMake
Ogre 2.1 FAQ
Minimal Ogre Collision
Artifex Terra
OpenMB
Advanced Mogre Framework
MogreSocks
...more
Search
Find
Advanced
Search Help
Online Users
113 online users