OpenGL VRML W3D            
  Home Tutorials Books Applications Tools Docs Models Textures  

 

              Bugs

GLSL Tutorial   

  GLSL Tutorial

Index
Introduction

The Graphics Pipeline
Pipeline Overview
Vertex Processor
Fragment Processor

OpenGL Setup for GLSL
Overview
Creating a Shader
Creating a Program
Source Code
Trouble Shooting: the InfoLog
Cleaning Up

Comm. OpenGL=> GLSL
Comm. Introduction
Uniform Variables
Attribute Variables

Shader Basics
Data Types and Variables
Statments and Functions
Varying Variables

Shader Examples
Shader Examples List

GLSL Hello World

Color Shader

Flatten Shader

Toon Shader
Toon Shader - Version I
Toon Shader - Version II
Toon Shader - Version III

Lighting
OpenGL Directional Light I
OpenGL Directional Light II
Directional Light per Pixel
Point Light Per Pixel
Spot Light Per Pixel

Simple Texture
Combine Texture + Fragment
Multitexturing

Notes
The gl_NormalMatrix
Normalization Issues


Google

OpenGLTutorials @ Lighthouse3d.com

Led Shader
View Frustum Culling
GLSL Tutorial
Maths Tutorial
Billboarding Tutorial
Picking Tutorial
Terrain Tutorial
Display Lists Tutorial
GLUT Tutorial



   
[Previous: Directional Light per Pixel] [Next: Spot Light Per Pixel]

GLSL Tutorial


Point Light Per Pixel


This tutorial is based on the directional lights tutorial as most (99%) of the code comes from there. The tutorial is based on the difference between a directional light and a point light. A directional light is assumed to be infinitely far away, so that the light rays are parallel when they reach the object. In contrast, a point light has a position, and sends rays in all directions. Furthermore, in a point light, the intensity decays with the distance to the vertex.

From an OpenGL application point of view there are two differences between the two:

  • the w component of the light position field: in a directional light it is zero to indicate that the position is in fact a direction (or vector), where as in a point light the w component of the light position field is 1.
  • The attenuation is specified based on three coefficients: a constant term, a linear term, and a quadratic term

From a computational point of view these differences must be taken care of. For a directional light, the direction of the light rays is constant for every vertex, whereas for a point light it is the vector from the vertex to the lights position. Hence, all that needs to change in the vertex shader is the computation of the lights direction.

The attenuation is computed based on the following formula in OpenGL:

where k0 is the constant attenuation, k1 is the linear attenuation, k2 is the quadratic attenuation and d is the distance from the light's position to the vertex.

Note that the attenuation does not vary linearly with distance, hence we can't compute the attenuation per vertex and use the interpolated value in the fragment shader. We can however compute the distance in the vertex shader and use the interpolated distance in the fragment shader to compute the attenuation.

The equation for the color using a point light is:

As shown in the above equation, the ambient term must be spitted in two: one global ambient term using the lighting model ambient setting and a light specific ambient term. The vertex shader must separate the computation of the ambient term accordingly. The new vertex shader is:


	varying vec4 diffuse,ambientGlobal,ambient;
	varying vec3 normal,lightDir,halfVector;
	varying float dist;
	
	void main()
	{	
		vec4 ecPos;
		vec3 aux;
		
		normal = normalize(gl_NormalMatrix * gl_Normal);
		
		/* these are the new lines of code to compute the light's direction */
		ecPos = gl_ModelViewMatrix * gl_Vertex;
		aux = vec3(gl_LightSource[0].position-ecPos);
		lightDir = normalize(aux);
		dist = length(aux);
	
		halfVector = normalize(gl_LightSource[0].halfVector.xyz);
		
		/* Compute the diffuse, ambient and globalAmbient terms */
		diffuse = gl_FrontMaterial.diffuse * gl_LightSource[0].diffuse;
		
		/* The ambient terms have been separated since one of them */
		/* suffers attenuation */
		ambient = gl_FrontMaterial.ambient * gl_LightSource[0].ambient;
		ambientGlobal = gl_LightModel.ambient * gl_FrontMaterial.ambient;
		
			
		gl_Position = ftransform();
	} 

The fragment shader needs to compute the attenuation. It also needs to normalize the interpolated light direction, since the direction is potentially different for every vertex.


	varying vec4 diffuse,ambientGlobal, ambient;
	varying vec3 normal,lightDir,halfVector;
	varying float dist;
	
	
	void main()
	{
		vec3 n,halfV,viewV,ldir;
		float NdotL,NdotHV;
		vec4 color = ambientGlobal;
		float att;
		
		/* a fragment shader can't write a varying variable, hence we need
		a new variable to store the normalized interpolated normal */
		n = normalize(normal);
		
		/* compute the dot product between normal and normalized lightdir */
		NdotL = max(dot(n,normalize(lightDir)),0.0);
	
		if (NdotL > 0.0) {
		
			att = 1.0 / (gl_LightSource[0].constantAttenuation +
					gl_LightSource[0].linearAttenuation * dist +
					gl_LightSource[0].quadraticAttenuation * dist * dist);
			color += att * (diffuse * NdotL + ambient);
		
			
			halfV = normalize(halfVector);
			NdotHV = max(dot(n,halfV),0.0);
			color += att * gl_FrontMaterial.specular * gl_LightSource[0].specular * 
							pow(NdotHV,gl_FrontMaterial.shininess);
		}
	
		gl_FragColor = color;
	}


The following images show the difference between a point light as computed by the fixed functionality, i.e. per vertex, and using the shader in this tutorial, i.e. per pixel.

Fixed FunctionalityPer Pixel

The full source of the shaders, in a Shader Designer project can be found in here.

[Previous: Directional Light per Pixel] [Next: Spot Light Per Pixel]

       


Site designed and maintained by António Ramires Fernandes
Your comments, suggestions and references to further material are welcome!

Lighthouse 3D privacy statement