Advertisement

Per pixel light attenuation and bump mapping

Started by June 21, 2004 05:48 PM
36 comments, last by vincoof 20 years, 5 months ago
hi everybody :) as is uual for me, i got another problem with per pixel lighting :D this time i've resolved all my problems with dot3 bump mapping and i would like to add some kind of "attenuation" for my light source. i have this situation: GL_TEXTURE0 is my cubemap GL_TEXTURE1 is the normal map (i compute the dot with the cube map as u know) GL_TEXTURE2 is my decal texture GL_TEXTURE3 is lightmap. i've searched the net for some good ppl tutorial and i've found the Ron Frazier's one... it's very good but he used only nVidia's extensions and i got an ATI :/ but the real problem is: how can i combine my dot3 bump mapping system with attenuation of light? should i do another rendering pass or there's a way to avoid this?
If you use the ATI_fragment_shader extension, you can easily compute the distance from a point to the light and attenuate the light contribution with this distance.

If you use the ARB_multitexture extension only you can either use a 3D attenuation texture, or combine a 2D attenuation with a 1D attenuation texture. The former (3D texture) uses one texture unit only, is more flexible, and more straightforward, but wastes much more texture memory. The latter (2D+1D) is harder to setup but runs on much more hardware and saves memory. Personnaly I'd go for the 3D texture solution with a low texture size (something like 16x16x16).
Advertisement
Sample source :
GLuint generateAttenuation3D(unsigned int size){  GLubyte* pixels;  GLuint texid;  int i, j, k;  pixels = (GLubyte*)malloc(size*size*size*sizeof(GLubyte));  if (pixels == NULL) return 0;  for (i = 0 ; i < size ; ++i) {    float iattn = 1.f-2.f*fabs(i/(float)(size-1)-0.5f);    for (j = 0 ; j < size ; ++j) {      float jattn = 1.f-2.f*fabs(j/(float)(size-1)-0.5f);      for (k = 0 ; k < size ; ++k) {        float kattn = 1.f-2.f*fabs(k/(float)(size-1)-0.5f);        float attn = iattn*jattn*kattn;        pixels[sizeof(GLubyte)*(k+size*(j+i*size))] = (GLubyte)(attn*255.f);      }    }  }  glGenTextures(1, &texid);  glBindTexture(GL_TEXTURE_3D_EXT, texid);  glTexParameteri(GL_TEXTURE_3D_EXT, GL_TEXTURE_MIN_FILTER, GL_LINEAR);  glTexParameteri(GL_TEXTURE_3D_EXT, GL_TEXTURE_MAG_FILTER, GL_LINEAR);  glTexParameteri(GL_TEXTURE_3D_EXT, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE_EXT);  glTexParameteri(GL_TEXTURE_3D_EXT, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE_EXT);  glTexParameteri(GL_TEXTURE_3D_EXT, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE_EXT);  glTexImage3DEXT(GL_TEXTURE_3D_EXT, 0, GL_LUMINANCE, size, size, size, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, pixels);  free(pixels);  return texid;}

void enableAttenuation3D(GLenum texturestage, GLuint textureid, GLfloat radius, GLfloat center[3]){  GLfloat eqS[4] = { 0, 0, 0, 0 };  GLfloat eqT[4] = { 0, 0, 0, 0 };  GLfloat eqR[4] = { 0, 0, 0, 0 };  glActiveTextureARB(texturestage);  glBindTexture(GL_TEXTURE_3D_EXT, textureid);  glEnable(GL_TEXTURE_3D_EXT);  glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);  eqS[0] = 0.5f/radius;  eqS[3] = 0.5f-0.5f*center[0]/radius;  eqT[1] = 0.5f/radius;  eqT[3] = 0.5f-0.5f*center[1]/radius;  eqR[2] = 0.5f/radius;  eqR[3] = 0.5f-0.5f*center[2]/radius;  glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR);  glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR);  glTexGeni(GL_R, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR);  glTexGenfv(GL_S, GL_EYE_PLANE, eqS);  glTexGenfv(GL_T, GL_EYE_PLANE, eqT);  glTexGenfv(GL_R, GL_EYE_PLANE, eqR);  glEnable(GL_TEXTURE_GEN_S);  glEnable(GL_TEXTURE_GEN_T);  glEnable(GL_TEXTURE_GEN_R);}

void disableAttenuation3D(GLenum texturestage){  glActiveTextureARB(texturestage);  glDisable(GL_TEXTURE_3D_EXT);  glDisable(GL_TEXTURE_GEN_S);  glDisable(GL_TEXTURE_GEN_T);  glDisable(GL_TEXTURE_GEN_R);}


Then, call this in your initialization part :
GLuint tex3Dattn = generateAttenuation3D(16);if (tex3Dattn==0) printf("error");


And for instance call this before rendering :
GLfloat light_center = { 10.f, 20.f, 10.f };GLfloat light_radius = 40.f;enableAttenuation3D(GL_TEXTURE4_ARB, tex3Dattn, light_radius, light_center);

And call this after rendering :
disableAttenuation3D(GL_TEXTURE4_ARB);


And don't forget to call glActiveTextureARB(GL_TEXTURE0_ARB) after enableAttenuation3D or disableAttenuation3D (or any other
GL_TEXTUREx_ARB that would be useful).

PS: I've just written it from scratch. Please excuse me if there is any mistake in it.
thank you! but tell me: i have to create ANOTHER cube map or i can use the same i use for bump mapping? and i should place your code before or after the dot3 code ( i mean in the render cycle) ?
You don't have to creat any other cube map, but you have to create a 3D texture.

You can just call the enableAttenuation3D function after you setup all the other texture units, or just before. It doesn't matter. The only thing that matters is that you call it before glBegin, and that the modelview matrix is in a good state to use the light's center coordinate correctly.

I've just forgot to note : since it uses a fifth texture unit, it won't ge able to run on GeForce1-4 (and probably not even on GeForceFX, I don't remeber how NVIDIA manages texture units higher than GL_TEXTURE3_ARB)
again: thank you :)

i've used your code, but appened that glTexImage3DEXT is not supported by mi ati9000 :/
Advertisement
@vincoof: tell me, if i want to make that attenuation light ..hmm.. red? i mean, with your code i have a spherical zone with light and out everything is black.
i've changed this line
pixels[sizeof(GLubyte)*(k+size*(j+i*size))] = (GLubyte)(attn*255+30);

adding "+30" that makes the light brighter, so now i can see almost everything...but it's a white light :D how can i change its color? Using
glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, mycolor); ?
The role of the attenuation map is not really to setup the light color, though it could be done. You would have to change the initializatio with something like this :

GLuint generateAttenuation3D(unsigned int size, GLfloat red, GLfloar green, GLfloat blue){  GLubyte* pixels;  GLuint texid;  int i, j, k;  pixels = (GLubyte*)malloc(3*size*size*size*sizeof(GLubyte));  if (pixels == NULL) return 0;  for (i = 0 ; i < size ; ++i) {    float iattn = 1.f-2.f*fabs(i/(float)(size-1)-0.5f);    for (j = 0 ; j < size ; ++j) {      float jattn = 1.f-2.f*fabs(j/(float)(size-1)-0.5f);      for (k = 0 ; k < size ; ++k) {        float kattn = 1.f-2.f*fabs(k/(float)(size-1)-0.5f);        float attn = iattn*jattn*kattn;        pixels[3*sizeof(GLubyte)*(k+size*(j+i*size))+0] = (GLubyte)(attn*255.f*red);        pixels[3*sizeof(GLubyte)*(k+size*(j+i*size))+1] = (GLubyte)(attn*255.f*green);        pixels[3*sizeof(GLubyte)*(k+size*(j+i*size))+2] = (GLubyte)(attn*255.f*blue);      }    }  }  glGenTextures(1, &texid);  glBindTexture(GL_TEXTURE_3D_EXT, texid);  glTexParameteri(GL_TEXTURE_3D_EXT, GL_TEXTURE_MIN_FILTER, GL_LINEAR);  glTexParameteri(GL_TEXTURE_3D_EXT, GL_TEXTURE_MAG_FILTER, GL_LINEAR);  glTexParameteri(GL_TEXTURE_3D_EXT, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE_EXT);  glTexParameteri(GL_TEXTURE_3D_EXT, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE_EXT);  glTexParameteri(GL_TEXTURE_3D_EXT, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE_EXT);  glTexImage3DEXT(GL_TEXTURE_3D_EXT, 0, GL_RGB, size, size, size, 0, GL_RGB, GL_UNSIGNED_BYTE, pixels);  free(pixels);  return texid;}


But that means you have to build as many 3D textures as different light colors exist. This is going to kill your memory and bandwidth if there are too many different lights.


The other method that works is using a new texture unit, using texture environment combine like that :
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_MODULATE);glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_PREVIOUS);glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_CONSTANT);/* use lines below only if you use blending */glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_REPLACE);glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_PREVIOUS);


As a side note, for this to work you will probably need to bind a "dumb" texture object (you need to bind a valid texture object otherwise the texture unit is ignored) even if no texels are sampled from it.
hmm... and so? how should i do to have a coloured light?
Quote: Original post by b3rs3rk
hmm... and so? how should i do to have a coloured light?

instead of calling
generateAttenuation3D(16)
, call for instance
generateAttenuation3D(16, 0.7f, 0, 0)

This topic is closed to new replies.

Advertisement