Project Description

A small collection of shaders that showcase a variety of the more common techniques related to lighting. The shaders were made as part of an introduction to shader programming both in terms of how to make the shaders and the different techniques as well as how to apply them to a model and how to manage applying parameters and such.

The shaders are made in Nvidia’s CgFX language and the implementation of the framework is done in a self made OpenGL framework with FreeGlut. The framework is set up in a way to efficiently create and apply a shader and apply it to a model and to easily apply and adjust the needed parameters.

Model credit goes to Dmitri Parkin.

 

Code Snippits

#ifndef _EFFECT_H_
#define _EFFECT_H_

#include "Cg/cgGL.h"
#include <string>
#include <vector>

class Effect
{
public:

  virtual ~Effect();

  static Effect* Create(const std::string& a_Name, const std::string& a_File);

  const std::string& GetName() const {return m_Name;}

  bool Load(const std::string& a_File);
  bool Reload();

  CGpass				GetFirstPass();
  CGpass				GetNextPass();
  CGpass				GetCurrentPass() const { return m_CurrentPass; }
  CGtechnique			GetTechnique() const { return m_Technique; }
  CGtechnique			GetTechnique(const char* a_Name);
  CGeffect			GetCGEffect() const { return m_Effect; }
  void				UpdatePassParameters();
  void				SetCurrentTechnique(CGtechnique a_Technique) {m_Technique = a_Technique;}

  CGparameter			GetParameter(const char* param);
  void				SetParameter1f(const char* param, float val);
  void				SetParameter2f(const char* param, const float* val);
  void				SetParameter3f(const char* param, const float* val);
  void				SetParameter4f(const char* param, const float* val);
  void				SetParameter1i(const char* param, int val);
  void				SetParameter2i(const char* param, const int* val);
  void				SetParameter3i(const char* param, const int* val);
  void				SetParameter4i(const char* param, const int* val);
  void				SetParameter1d(const char* param, double val);
  void				SetParameter2d(const char* param, const double* val);
  void				SetParameter3d(const char* param, const double* val);
  void				SetParameter4d(const char* param, const double* val);
  void				SetParameterMatrix(const char* param, const float* val);
  void				SetParameterTexture(const char* param, unsigned int texture);

  void				Bind();
  void				Unbind();

protected:

  Effect(const std::string& a_Name, const std::string& a_File);

  std::string m_Name;
  std::string m_File;
  CGeffect m_Effect;
  CGtechnique m_Technique;
  CGpass m_CurrentPass;

  std::vector<CGtechnique> m_TechniqueList;

  static CGcontext m_Context;
};
#endif

 

#ifdef _WIN32
#define WIN32_LEAN_AND_MEAN
#include <windows.h> // For OutputDebugString
#endif
#include <sstream>
#include "Effect.h"
#include <string>

CGcontext Effect::m_Context = NULL;

// Error handler for Cg
void CgErrorHandler( CGcontext context, CGerror error, void* appdata )
{
  if ( error != CG_NO_ERROR )
  {
    std::stringstream ss;
    const char* pStr = cgGetErrorString(error);
    std::string strError = ( pStr == NULL ) ? "" : pStr;
    ss << "Cg ERROR: " << strError << std::endl;

    std::string strListing;
    if ( error == CG_COMPILER_ERROR )
    {
      pStr = cgGetLastListing( context );
      strListing = ( pStr == NULL ) ? "" : pStr;

      ss << strListing << std::endl;
    }
#ifdef _WIN32
    OutputDebugStringA( ss.str().c_str() );
#else
    std::cerr << ss;
#endif
  }
}

Effect::Effect( const std::string& a_Name, const std::string& a_File )
{
  m_Name = a_Name;
  m_File = a_File;
  m_CurrentPass = NULL;

  if(!m_Context)
  {
    m_Context = cgCreateContext();
    cgSetErrorHandler(&CgErrorHandler, NULL);
    cgGLRegisterStates(m_Context);
    cgGLSetManageTextureParameters(m_Context, CG_TRUE);
  }

  Load(a_File);
}

Effect::~Effect()
{
  if(m_Effect != NULL)
  {
   	cgDestroyEffect(m_Effect);
   	m_Effect = NULL;
  }

  if(m_Context != NULL)
  {
   	cgDestroyContext(m_Context);
   	m_Context = NULL;
  }

  cgSetErrorHandler(NULL, NULL);
}

Effect* Effect::Create( const std::string& a_Name, const std::string& a_File )
{
  Effect* effect = new Effect(a_Name, a_File);

  return effect;
}

bool Effect::Load( const std::string& a_File )
{
  m_Effect = cgCreateEffectFromFile(m_Context, a_File.c_str(), NULL);

  if(m_Effect == NULL)
  {
    exit(-1);
  }

  CGtechnique technique = cgGetFirstTechnique(m_Effect);
  while(technique != NULL)
  {
    m_Technique = technique;
    m_TechniqueList.push_back(technique);
    technique = cgGetNextTechnique(technique);		
  }

  if(!m_Technique)
  {
    exit(-1);
  }

  return true;
}

bool Effect::Reload()
{
  cgDestroyEffect(m_Effect);
  m_TechniqueList.clear();
  Load(m_File);

  return true;
}

CGpass Effect::GetFirstPass()
{
  m_CurrentPass = cgGetFirstPass(m_Technique);
  return m_CurrentPass;
}

CGpass Effect::GetNextPass()
{
  m_CurrentPass = cgGetNextPass(m_CurrentPass);
  return m_CurrentPass;
}

void Effect::UpdatePassParameters()
{
  cgUpdatePassParameters(m_CurrentPass);
}

CGparameter Effect::GetParameter( const char* param )
{
  CGparameter p = cgGetEffectParameterBySemantic(m_Effect, param);
  if(p) return p;

  p = cgGetNamedEffectParameter(m_Effect, param);
  if(p) return p;

  return 0;
}

void Effect::SetParameter1f( const char* param, float val )
{
  CGparameter parameter = GetParameter(param);
  if(parameter) cgSetParameter1f(parameter, val);
}

void Effect::SetParameter2f( const char* param, const float* val )
{
  CGparameter parameter = GetParameter(param);
  if(parameter) cgSetParameter2f(parameter, val[0], val[1]);
}

void Effect::SetParameter3f( const char* param, const float* val )
{
  CGparameter parameter = GetParameter(param);
  if(parameter) cgSetParameter3f(parameter, val[0], val[1], val[2]);
}

void Effect::SetParameter4f( const char* param, const float* val )
{
  CGparameter parameter = GetParameter(param);
  if(parameter) cgSetParameter4f(parameter, val[0], val[1], val[2], val[3]);
}

void Effect::SetParameter1i( const char* param, int val )
{
  CGparameter parameter = GetParameter(param);
  if(parameter) cgSetParameter1i(parameter, val);
}

void Effect::SetParameter2i( const char* param, const int* val )
{
  CGparameter parameter = GetParameter(param);
  if(parameter) cgSetParameter2i(parameter, val[0], val[1]);
}

void Effect::SetParameter3i( const char* param, const int* val )
{
  CGparameter parameter = GetParameter(param);
  if(parameter) cgSetParameter3i(parameter, val[0], val[1], val[2]);
}

void Effect::SetParameter4i( const char* param, const int* val )
{
  CGparameter parameter = GetParameter(param);
  if(parameter) cgSetParameter4i(parameter, val[0], val[1], val[3], val[4]);
}

void Effect::SetParameter1d( const char* param, double val )
{
  CGparameter parameter = GetParameter(param);
  if(parameter) cgSetParameter1d(parameter, val);
}

void Effect::SetParameter2d( const char* param, const double* val )
{
  CGparameter parameter = GetParameter(param);
  if(parameter) cgSetParameter2d(parameter, val[0], val[1]);
}

void Effect::SetParameter3d( const char* param, const double* val )
{
  CGparameter parameter = GetParameter(param);
  if(parameter) cgSetParameter3d(parameter, val[0], val[1], val[2]);
}

void Effect::SetParameter4d( const char* param, const double* val )
{
  CGparameter parameter = GetParameter(param);
  if(parameter) cgSetParameter4d(parameter, val[0], val[1], val[3], val[4]);
}

void Effect::SetParameterMatrix( const char* param, const float* val )
{
  CGparameter parameter = GetParameter(param);
  if(parameter) cgSetMatrixParameterfc(parameter, val);
}

void Effect::SetParameterTexture( const char* param, unsigned int texture )
{
  CGparameter parameter = GetParameter(param);
  if(parameter) cgGLSetTextureParameter(parameter, texture);
}

void Effect::Bind()
{
  cgSetPassState(m_CurrentPass);
}

void Effect::Unbind()
{
  cgResetPassState(m_CurrentPass);
}

CGtechnique Effect::GetTechnique( const char* a_Name )
{
  for(int i = 0; i < m_TechniqueList.size(); ++i)
  {
    const char* name = cgGetTechniqueName(m_TechniqueList[i]);
    if(strcmp(name, a_Name) != 1)
      return m_TechniqueList[i];
  }
}

 

float Attenuation(float4 p, _Light light)
{
  float d = distance(p, light.Position);
  return 1 / (light.ConstantAttenuation + light.LinearAttenuation * d + light.QuadraticAttenuation * d * d);
}

float4 expand(float4 v) 
{
  return (v-0.5)*2;
}

float4 CalcLambert(float3 position, float3 normal, _Light light)
{
  float3 L = normalize(light.Position - position);
  float A = Attenuation(float4(position, 0), light);
  float D = saturate(dot(normal, L));

  return light.Color * D * A;
}

float4 CalcSpecularity(float3 position, float3 normal, float shine, _Light light)
{
  float3 L = normalize(light.Position - position);
  float3 V = normalize(EyePosition - position);
  float3 H = normalize(L + V);
  float A = Attenuation(float4(position, 0), light);
  float S = pow(max(dot(normal, H), 0), shine);

  return light.Color * S * A;
}

float tex2Dshadow( sampler2D shadowMap, float4 texCoord )
{
    float4 projTexColor = tex2Dproj( shadowMap, texCoord );
    float test = texCoord.z / texCoord.w;
    float val = ( test < ( projTexColor.r  ) ) ? 1.0f : 0.0f;

    // Don't shade pixels that are behind the light.
    return ( texCoord.w < 0 ) ? 1.0f : val;
}

float dualConeSpotlight(float3 P, _Light  light)
{
    float3 V = normalize(P - light.Position);
    float cosOuterCone = light.OuterCosAngle;
    float cosInnerCone = light.InnerCosAngle;
    float cosDirection = dot(V, light.Direction);

    return smoothstep(cosOuterCone, cosInnerCone, cosDirection);
}

void SpotAttenLighting(	_Light  light,
                  float3 P,
                  float3 N,
                  float3 eyePosition,
                  float  shininess,

                  out float4 diffuseResult,
                  out float4 specularResult)
{
    // Compute spotlight effect
    float spotEffect = dualConeSpotlight(P, light);

  float4 lambert = CalcLambert(P, N, light);
  float4 phong = CalcSpecularity(P, N, shininess, light);

  diffuseResult = lambert * spotEffect;
  specularResult = phong * spotEffect;
}

 

#include "General.header"
#include "Functions.header"

void mainVP(in Vertex IN, out Fragment OUT)
{
  OUT.pos = mul(ModelViewProjection, float4(IN.pos, 1));
  OUT.wpos = mul(ModelMatrix, float4(IN.pos, 1));

  float3 worldnormal = normalize(mul(ModelMatrixIT, IN.normal));
  float3 worldbinormal = normalize(mul(ModelMatrixIT, IN.binormal));
  float3 worldtangent = normalize(mul(ModelMatrixIT, IN.tangent));

  OUT.normal = float4(worldnormal, 1.0);
  OUT.binormal = float4(worldbinormal, 1.0);
  OUT.tangent = float4(worldtangent, 1.0);

  OUT.uv = IN.uv;
  OUT.diffuse = IN.diffuse;
}

void mainFP(  in Fragment IN, out float4 color : COLOR)
{
  float4 normalTex = tex2D(g_normalmap, IN.uv);
  float4 diffuse = Material.Diffuse;;
  float4 specular = Material.Specular;
  float4 emissive = Material.Emissive;

  if(Material.DiffuseMap != 0)
  {
    diffuse = tex2D(g_DiffuseMap, IN.uv);
  }
  if(Material.SpecularMap != 0)
  {
    specular = tex2D(g_specularmap, IN.uv);
  }
  if(Material.EmissiveMap != 0)
  {
    emissive = tex2D(g_emissivemap, IN.uv);
  }

  float4x4 tbnmatrix;
  tbnmatrix[0] = IN.tangent; 
  tbnmatrix[1] = IN.binormal;
  tbnmatrix[2] = IN.normal;

  float4 normalSample = expand(normalTex);
  normalSample = mul(normalSample, tbnmatrix);

  float4 lambert;
  float4 phong;

  if(Material.NormalMap != 0)
  {
    lambert = CalcLambert(IN.wpos.xyz, normalSample.xyz, Light);
    phong = CalcSpecularity(IN.wpos.xyz, normalSample.xyz, Material.Shininess, Light);
  }
  else
  {
    lambert = CalcLambert(IN.wpos.xyz, IN.normal, Light);
    phong = CalcSpecularity(IN.wpos.xyz, IN.normal, Material.Shininess, Light);
  }

  float4 ambientfinal = GlobalAmbient * Material.Ambient;
  float4 diffusefinal = lambert * diffuse;
  float4 specularfinal = phong * specular;
  float4 emissivefinal = emissive;

  color = ambientfinal + emissivefinal + (diffusefinal + specularfinal);
}

technique t0
{
    pass p0
    {
        VertexProgram = compile arbvp1 mainVP();
        FragmentProgram = compile arbfp1 mainFP();
    }
}