majakthecoder
majakthecoder

Reputation: 177

Blending lightmap with diffuse texture

I'm using c++, opengl 4.0 and glsh shader language.

I'm wondering how to correctly blend diffuse texture with lightmap texture.

Let's assume that we have a room. Every object has diffuse texture and lightmap. In every forum like gamedev.net or stackoverflow people say, that those textures should be multiplied. And in most cases it gives good results, but sometimes some objects are very close to light source (for example white bulb). This light source for close objects generates white lightmap. But when we multiply diffuse texture with white lightmap, then we get original diffuse texture color.

But if light source is close to some object, then color of light should be dominant

It means, that if white, strong light is close to red wall, then some part of this wall should be white, not red!

I think I need something more than just one lightmap. Lightmap don't have information about light intensity. It means, that the most shiny color is just maximum diffuse color.

Maybe I should have 2 textures - shadowmap and lightmap? Then equations should looks like this:

vec3 color = shadowmapColor * diffuseTextureColor + lightmapColor;

Is it good approach?

Upvotes: 2

Views: 2814

Answers (2)

thokra
thokra

Reputation: 2904

What you need is the distance (or to save some sqrt-ing, the squared distance) of the light source to the fragment being illuminated. Then you can, in the simplest case, interpolate linearly between the light map and light source contributions:

The distance is a simple calculation which can be done per vertex in you vertex shader:

in      vec4  VertexPosition; // let's assume world space for simplicity
uniform vec4  LightPosisiton; // world-space - might also be part of a uniform block etc.
out     float LightDistance;  // pass the distance to the fragment shader

// other stuff you need here ....

void main()
{
  // do stuff 
  LightDistance = length(VertexPosition - LightPosisiton);
}

In your fragment shader, you use the distance to compute interpolation factors betweem light source and lightmap contributions:

in      float     LightDistance;
const   float     MAX_DISTANCE = 10.0;
uniform sampler2D LightMap;

// other stuff ...

out vec4 FragColor;

void main()
{
  vec4 LightContribution;
  // calculate illumination (including shadow map evaluation) here
  // store in LightContribution

  vec4 LightMapConstribution = texture(LightMap, /* tex coords here */);

  // The following DistanceFactor will map distances in the range [0, MAX_DISTANCE] to
  // [0,1]. The idea is that at LightDistance >= MAX_DISTANCE, the light source
  // doesn't contribute anymore.
  float DistanceFactor  = min(1.0, LightDistance / MAX_DISTANCE); 

  // linearly interpolat between LightContribution and LightMapConstribution 
  vec4 FinalContribution = mix(LightContribution, LightMapConstribution, DistanceFactor);

  FragColor = WhatEverColor * vec4(FinalContribution.xyz, 1.0);
}

HTH.

EDIT: To factor in Nicol Bolas' remarks, I assume that the LightMap stores the contribution encoded as an RGB color, storing the contributions for each channel. If you actually have a single channel lightmap which only store monochromatic contributions, you'll have to either use the surface color, use the color of the light source or reduce the light source contribution to a single channel.

EDIT2: Although this works mathematically, it's definitely not physically sound. You might need some correction of the final contribution to make it at least physically plausible. If your only aiming for effect, you can simply play around with correction factors until you're satisfied with the result.

Upvotes: 0

Nicol Bolas
Nicol Bolas

Reputation: 473447

Generally speaking, if you're still using lightmaps, you are probably also not using HDR rendering. And without that, what you want is not particularly reasonable. Unless your light map provides the light intensity as an HDR floating-point value (perhaps in a GL_R11F_G11F_B10F or GL_RGBA16F format), this is not going to work very well.

And of course, you'll have to do the usual stuff that you do with HDR, such as tone mapping and so forth.

Lastly, your additive equation makes no sense. If the light map color represents the diffuse interaction between the light and the surface, then simply adding the light map color doesn't mean anything. The standard diffuse lighting equation is C * (dot(N, L) * I * D), where I is the light intensity, D is the distance attenuation factor, and C is the diffuse color. The value from the lightmap is presumably the parenthesized quantity. So adding it doesn't make sense.

It still needs to multiply with the surfaces's diffuse color. Any over-brightening will be due to the effective intensity of the light as a function of D.

Upvotes: 4

Related Questions