Sign Up

Sign Up to our social questions and Answers Engine to ask questions, answer people’s questions, and connect with other people.

Have an account? Sign In

Have an account? Sign In Now

Sign In

Login to our social questions & Answers Engine to ask questions answer people’s questions & connect with other people.

Sign Up Here

Forgot Password?

Don't have account, Sign Up Here

Forgot Password

Lost your password? Please enter your email address. You will receive a link and will create a new password via email.

Have an account? Sign In Now

You must login to ask a question.

Forgot Password?

Need An Account, Sign Up Here

Please briefly explain why you feel this question should be reported.

Please briefly explain why you feel this answer should be reported.

Please briefly explain why you feel this user should be reported.

Sign InSign Up

The Archive Base

The Archive Base Logo The Archive Base Logo

The Archive Base Navigation

  • Home
  • SEARCH
  • About Us
  • Blog
  • Contact Us
Search
Ask A Question

Mobile menu

Close
Ask a Question
  • Home
  • Add group
  • Groups page
  • Feed
  • User Profile
  • Communities
  • Questions
    • New Questions
    • Trending Questions
    • Must read Questions
    • Hot Questions
  • Polls
  • Tags
  • Badges
  • Buy Points
  • Users
  • Help
  • Buy Theme
  • SEARCH
Home/ Questions/Q 8935845
In Process

The Archive Base Latest Questions

Editorial Team
  • 0
Editorial Team
Asked: June 15, 20262026-06-15T10:05:07+00:00 2026-06-15T10:05:07+00:00

I am implementing Pre-Pass Lighting algorithm in OpenGL for my master dissertation project, after

  • 0

I am implementing Pre-Pass Lighting algorithm in OpenGL for my master dissertation project, after implementing a Deferred renderer as well. The Deferred renderer works perfectly and I based the implementation of PPL on it. I got a very weird artifact after the lighting pass of the algorithm: the data contained in the L-buffer, where I accumulate the contributions of the lights in the scene, is correct, but results to be slightly off in respect to the geometry so when I apply it to the scene in the material pass the result it’s clearly visible! (I can’t post the image here but here it’s a link to see it http://postimage.org/image/kxhlbnl9v/)

It looks like the light map cube is somehow computed with an offset (different in every axes) from the geometry. I checked the shaders and C++ code many times, I do not understand where this problem comes from. I am running out of ideas. Below there is the code for the 3 passes of the algorithm that are called in sequence. The code is experimental for now so I know it’s not well designed at this stage. I also add the shaders I use in every stage to write to G-buffer, L-buffer and framebuffer in order.

C++ CODE:

// Draw geometry to g buffer
void GLPrePassLightingRendererV2::GeometryStage()
{
  // Set GL states
  glFrontFace(GL_CCW);
  glCullFace(GL_BACK);
  glEnable(GL_CULL_FACE);
  glDepthFunc(GL_LEQUAL);
  glDisable(GL_BLEND);
  glEnable(GL_DEPTH_TEST);
  glDepthMask(GL_TRUE);

  // Bind G-Buffer for geometry pass
  mGBuffer->BindForWriting();

  // Bind geometry stage shaders
  mTargetRenderSystem->BindShader(mGeometryStageVS);
  mTargetRenderSystem->BindShader(mGeometryStageFS);

  // Clear the framebuffer
  mTargetRenderSystem->ClearFrameBuffer(FBT_COLOUR | FBT_DEPTH);

  // Iterate over all the Renderables in the previously built RenderQueue
  RenderableList* visibles = mSceneManager->GetRenderQueue()->GetRenderables();

  // Set shader params here
  //[...]

  // Get the transformation info from the node the renderable is attached to
  for (RenderableList::iterator it = visibles->begin(); it != visibles->end(); ++it)
  {
    Renderable* renderable = *it;
    Material* mat = renderable->GetMaterial();

    mGeometryStageVS->Update();
    mGeometryStageFS->Update();

    // Render the object
    RenderOperation rop;
    renderable->GetRenderOperation(rop);
    mTargetRenderSystem->Render(rop);
  }

  // Only the geometry pass will write to the depth buffer
  glDepthMask(GL_FALSE);
  glDisable(GL_DEPTH_TEST);
}

// Accumulate lights contribs in L-buffer using G-buffer
void GLPrePassLightingRendererV2::LightingStage()
{
  // Enable additive blending for lights
  glEnable(GL_BLEND);
  glBlendEquation(GL_FUNC_ADD);
  glBlendFunc(GL_ONE, GL_ONE);
  //glCullFace(GL_FRONT);

  // Bind shader for light stage
  mTargetRenderSystem->BindShader(mLightStageVS);
  mTargetRenderSystem->BindShader(mLightStageFS);

  // Bind G-Buffer for reading and L-Buffer for writing for lighting pass
  mGBuffer->BindForReading();
  mLBuffer->BindForWriting();

  mTargetRenderSystem->ClearFrameBuffer(FBT_COLOUR);

  // Set shader params
  // [...]

  // Get all the lights in frustum, not by renderable
  const LightList& lights = mSceneManager->GetLightsInFrustum();

  // For each light in the frustum
  LightList::const_iterator front_light_it;
  for (LightList::const_iterator lit = lights.begin(); lit != lights.end(); ++lit)
  {
    // Send per light parameters to the shader
    Light* l = (*lit);
    SetLight(*l);

    // Calculate bounding sphere for light and scale accordingly to instensity
    float lightSphereScale = GetPointLightSphereScale(l->GetColor(), l->GetDiffuseIntensity());

    // TODO: Render a sphere for each point light, a full screen quad for each directional
    worldMtx.Identity();
    worldMtx.SetScale(lightSphereScale, lightSphereScale, lightSphereScale);
    worldMtx.SetTranslation(l->GetPosition());

    mLightStageVS->SetParameterValue("gWorldMtx", (float*)&worldMtx);

    mLightStageVS->Update();
    mLightStageFS->Update();
    static MeshInstance* sphere = mSceneManager->CreateMeshInstance("LightSphere", MBT_LIGHT_SPHERE);

    RenderOperation rop;
    sphere->GetSubMeshInstance(0)->GetRenderOperation(rop);
    mTargetRenderSystem->Render(rop);
  }

  // Disable additive blending
  glDisable(GL_BLEND);
}

// Combine L-buffer and material information per object
void GLPrePassLightingRendererV2::MaterialStage()
{
  // Set some GL states
  glDepthMask(GL_TRUE);
  glEnable(GL_DEPTH_TEST);
  //glCullFace(GL_BACK);

  // Bind material stage shaders (TODO: actually every object will bind its own matarial, if not a default one is used)
  mTargetRenderSystem->BindShader(mMaterialStageVS);
  mTargetRenderSystem->BindShader(mMaterialStageFS);

  // Bind L-Buffer for reading
  mLBuffer->BindForReading();

  mTargetRenderSystem->ClearFrameBuffer(FBT_COLOUR | FBT_DEPTH, Math::ColourValue::WHITE);

  // Iterate over all the Renderables in the previously built RenderQueue
  RenderableList* visibles = mSceneManager->GetRenderQueue()->GetRenderables();

  // Set shader params here
  // [...]

  // Get the transformation info from the node the renderable is attached to
  for (RenderableList::iterator it = visibles->begin(); it != visibles->end(); ++it)
  {
    Renderable* renderable = *it;
    Material* mat = renderable->GetMaterial();


    // Set texture units
    if (mat)
    {
      for (unsigned short i = 0; i < mat->GetTextureUnitCount(); ++i)
      {
        const TextureUnit* unit = mat->GetTextureUnit(i);
        GLTexture* t = static_cast<GLTexture*>(unit->GetTexture());
        glActiveTexture(GL_TEXTURE1); // This is needed because the first texture map slot is hold by the LBuffer!
        glBindTexture(GL_TEXTURE_2D, t->GetGLId());
      }
    }

    mMaterialStageVS->Update();
    mMaterialStageFS->Update();

    // Render the object
    RenderOperation rop;
    renderable->GetRenderOperation(rop);
    mTargetRenderSystem->Render(rop);
  }
}

NVIDIA CG Shaders:

// Vertex shader for Deferred Rendering geometry stage.

float4x4 gWorldMtx;
float4x4 gViewMtx;
float4x4 gProjectionMtx;

struct a2v
{
    float3 position : POSITION;
    float3 normal   : NORMAL;
    float2 texCoord : TEXCOORD0;
};

struct v2f
{
    float4 position     : POSITION;
    float3 normal       : TEXCOORD0;
    float3 wPosition    : TEXCOORD1;
    float2 texCoord     : TEXCOORD2;
};


v2f PPL_geometry_stage_vs(a2v IN)
{
    v2f OUT;

    // Transform to world space
    OUT.wPosition = mul(gWorldMtx, float4(IN.position, 1.0f)).xyz;
    OUT.normal = mul(gWorldMtx, float4(IN.normal, 0.0f)).xyz;

    // Transform to homogeneous clip space
    OUT.position = mul(gViewMtx, float4(OUT.wPosition, 1.0f));
    OUT.position = mul(gProjectionMtx, OUT.position);

    OUT.texCoord = IN.texCoord;

    return OUT;
}

// Fragment shader for Pre-pass Lighing geometry stage.

struct f2a
{
    float4 position : COLOR0;
    float4 normal   : COLOR1;
};

f2a PPL_geometry_stage_fs(v2f IN)
{
    f2a OUT;

    OUT.position = float4(IN.wPosition, 1.0f);
    OUT.normal = float4(normalize(IN.normal), 1.0f);

    return OUT;
}

// Vertex shader for Pre-pass lighing light stage.

float4x4 gWorldMtx;
float4x4 gViewMtx;
float4x4 gProjectionMtx;

struct a2v
{
    float3 position : POSITION;
};

struct v2f
{
    float4 position : POSITION;
    float4 lightPos : TEXCOORD0;
};

v2f PPL_light_stage_vs(a2v IN)
{
    v2f OUT;

    float4x4 wv   = mul(gWorldMtx, gViewMtx);
    float4x4 wvp  = mul(gViewMtx, gProjectionMtx);
    wvp           = mul(wvp, gWorldMtx);

    // Only transforms position to world space
    OUT.position  = mul(wvp, float4(IN.position, 1.0f));

    // Copy light position to calculate fragment coordinate
    OUT.lightPos = OUT.position;

    return OUT;  
}

// Fragment shader for Pre-pass lighing light stage.

// Light structures
struct BaseLight
{
    float3 color;
    float ambientIntensity;
    float diffuseIntensity;
};

struct DirectionalLight
{
    struct BaseLight base;
    float3 direction;
};

struct Attenuation
{
    float constant;
    float linearr;
    float quadratic;
};

struct PointLight
{
    struct BaseLight base;
    float3 position;
    Attenuation atten;
};

struct SpotLight
{
    struct PointLight base;
    float3 direction;
    float cutoff;
};

// G-Buffer textures
sampler2D gPositionMap  : TEXUNIT0;
sampler2D gNormalMap    : TEXUNIT1;

// Light variables
float3 gEyePosition;
DirectionalLight gDirectionalLight;
PointLight gPointLight;
SpotLight gSpotLight;
int gLightType;
float gSpecularPower;

float4 PPL_light_stage_point_light_fs(v2f IN) : COLOR0
{
    // Get fragment coordinate, from NDC space [-1, 1] to [0, 1].
    float2 fragcoord = ((IN.lightPos.xy / IN.lightPos.w) + 1.0f) / 2.0f;

    // Calculate lighting with G-Buffer textures
    float3 position = tex2D(gPositionMap, fragcoord).xyz;
    float3 normal = tex2D(gNormalMap, fragcoord).xyz;
    normal = normalize(normal);

    // Attenuation
    float3 lightDirection = position - gPointLight.position;
    float dist = length(lightDirection);
    float att = gPointLight.atten.constant + gPointLight.atten.linearr * dist + gPointLight.atten.quadratic * dist * dist;

    // NL
    lightDirection = normalize(lightDirection);
    float NL = dot(normal, -lightDirection);

    // Specular (Blinn-Phong)
    float specular = 0.0f;
    //if (NL > 0)
    //{
    //  float3 vertexToEye = normalize(gEyePosition - position);
    //  float3 lightReflect = normalize(reflect(lightDirection, normal));
    //  specular = pow(saturate(dot(vertexToEye, lightReflect)), gSpecularPower);
    //}

    // Apply attenuation to NL
    NL = NL / min(1.0, att);

    float3 lightColor = gPointLight.base.color * gPointLight.base.diffuseIntensity;
    return float4(lightColor.r, lightColor.g, lightColor.b, 1.0f) * NL;
}

// Vertex shader for Pre-pass lighing material stage.

float4x4 gWorldMtx;
float4x4 gViewMtx;
float4x4 gProjectionMtx;

struct a2v
{
    float3 position : POSITION;
    float3 normal   : NORMAL;
    float2 texcoord : TEXCOORD0;
};

struct v2f
{
    float4 position : POSITION;
    float2 texcoord : TEXCOORD0;
    float3 normal   : TEXCOORD1;
    float4 projPos  : TEXCOORD2;
};

v2f PPL_material_stage_vs(a2v IN)
{
    v2f OUT;

    float4x4 wv   = mul(gWorldMtx, gViewMtx);
    float4x4 wvp  = mul(gViewMtx, gProjectionMtx);
    wvp           = mul(wvp, gWorldMtx);

    // Only transforms position to world space
    OUT.position  = mul(wvp, float4(IN.position, 1.0f));

    // Normal (It's not necessary, but i have to see if it influences the execution)
    OUT.normal    = mul(gWorldMtx, float4(IN.normal, 0.0f)).xyz;

    // Copy texture coordinates
    OUT.texcoord = IN.texcoord;

    // Copy projected position to get the fragment coordinate
    OUT.projPos = OUT.position;

    return OUT;
}

// Fragment shader for Pre-pass lighing material stage.

// L-buffer texture
sampler2D gLightMap : TEXUNIT0;
// Object's material specific textures
sampler2D gColorMap : TEXUNIT1;

float4 PPL_material_stage_fs(v2f IN) : COLOR0
{
    float2 fragcoord = ((IN.projPos.xy / IN.projPos.w) + 1.0f) / 2.0f;

    // Get all light contributions for this pixel
    float4 light = tex2D(gLightMap, fragcoord);
    float3 combined = saturate(light.rgb);// + light.aaa);

    // Get material albedo from texture map
    float4 diffuse = tex2D(gColorMap, IN.texcoord);

    return float4(combined, 1.0f) * diffuse;
}

Any suggestions?

  • 1 1 Answer
  • 0 Views
  • 0 Followers
  • 0
Share
  • Facebook
  • Report

Leave an answer
Cancel reply

You must login to add an answer.

Forgot Password?

Need An Account, Sign Up Here

1 Answer

  • Voted
  • Oldest
  • Recent
  • Random
  1. Editorial Team
    Editorial Team
    2026-06-15T10:05:08+00:00Added an answer on June 15, 2026 at 10:05 am

    You may want to use the WPOS register (VPOS in HLSL) instead of calculating the screen locations.

    • 0
    • Reply
    • Share
      Share
      • Share on Facebook
      • Share on Twitter
      • Share on LinkedIn
      • Share on WhatsApp
      • Report

Sidebar

Related Questions

I am implementing a Rails 3.1 application in combination with the sunspot_rails gem (2.0.0.pre)
I'm implementing a somewhat complex configuration section for a system monitoring project: <monitoring> <components>
When implementing jQuery UI Slider (http://jqueryui.com/demos/slider/) there is always a pre-selection (default value 0).
So I have been looking into implementing a lowest common ancestor algorithm. I looked
I saw an example about implementing pre-increment and post-increment, which claims that overloading pre-increment
I'm implementing namespaces in my existing project. I found that you can use the
I've been implementing boost::spirit into a project and one of my challenges is to
I'm implementing the 960 grid system on my pre-existing site. I'm using a jQuery
Implementing a custom Dependency Property on a Framework Element object causes my Visual Studio
Implementing the ScriptControlClass was extremely easy, unfortunately the side effects with the language implementation

Explore

  • Home
  • Add group
  • Groups page
  • Communities
  • Questions
    • New Questions
    • Trending Questions
    • Must read Questions
    • Hot Questions
  • Polls
  • Tags
  • Badges
  • Users
  • Help
  • SEARCH

Footer

© 2021 The Archive Base. All Rights Reserved
With Love by The Archive Base

Insert/edit link

Enter the destination URL

Or link to existing content

    No search term specified. Showing recent items. Search or use up and down arrow keys to select an item.