GL_CLAMP not working?

I saw my engine on a different system today and noticed a problem where the edges of my terrain meshes had a black line between them. This was also happening with my sky. I use GL_CLAMP texture mode to get a seamless attachment of the meshes. I also modify the textures of the two meshes when I attach them, so that the pixels along the adjacent edges are the same. On my system it looks fine, but on the system I was using today, there is a black line at the edges of the terrain and sky quads. I think this must be a problem with GL_CLAMP. Does anyone have any ideas on how to fix this?

This sounds like the behavior of the NVIDIA GL_CLAMP bug. By default, with NVIDIA drivers, GL_CLAMP behaves like GL_CLAMP_TO_EDGE. Other video cards use correct GL_CLAMP behavior which is often not desireable. So I’m guessing your good system was one with an NVIDIA card with default behavior. And the other was either a non-NVIDIA card, or an NVIDIA card with the correct behavior enabled. The answer is simple, change your application so that it explicitly uses GL_CLAMP_TO_EDGE.

I notice in the detonator 40.xx series of drivers, the option to revert back to proper functionality is a little check box in the OpenGL tab. Nice.

DFrey, thanks for your help, that is probably what the problem is. However, its NOT the NVIDIA card which works properly. I dont want to specify GL_CLAMP_TO_EDGE, because I want GL_CLAMP. So how do I get it to work properly on the NVIDIA card?

You probably really do need GL_CLAMP_TO_EDGE. At least if I understand clearly what you say you are doing, then GL_CLAMP_TO_EDGE is exactly what I would use.

There are three different “CLAMP” modes in OpenGL 1.3 and 1.4. Let’s take an example of each with a square with texture coordinates ranging from (-1,-1) to (+2, +2). In all three cases, roughly 1/9th of this square will show a copy of the texture image. The other 8/9ths would show what happens with clamping.

GL_CLAMP_TO_EDGE means that if your texture coordinates tell you to sample off the end of your texture map, you should sample the texel that is on the edge. In this example, you would see the texels at the edge of the map smeared outward.

GL_CLAMP_TO_BORDER means if your texture coordinates tell you to sample off the end of your texture map, you should sample the texture border. If you have a texture border in your image, you would see the texels at the corner of the map smeared outward. Otherwise, you get the constant texture border color (black by default).

GL_CLAMP tells you first to clamp the coordinates to [0,1]. If you point sample, this clamping will always end up sampling a valid texel in the map, and will look just like clamp to edge. If you are doing linear filtering and you’re clamped to the left edge of the texture, you’ll have two samples at the edge texels and two that fall off the edge. Those “off the edge” samples will hit the border texels if you have a border, or the constant border color. The filtering weights will be 50% edge, 50% border. (In the corners, it will be 25% edge, 75% border). What you will see is a blend of the edge and border texels if you have a border, or a blend of the edge texels and the constant border color otherwise. Additionally, if you are using the border colors, you will see a somewhat abrupt transition when you are near the corners of the texture where the border color weight jumps from 50% to 75%. This is rarely what you want.

A dark seam (like you are seeing) is usually evidence of an application using GL_CLAMP when it should be using GL_CLAMP_TO_EDGE. One way to test this is to change the border color to white, in which case you should see a light seam.

In practice, this example with out-of-range texture coordinates isn’t what you’d expect – if you want to line up multiple texture maps, you will generally tesselate your geometry so that all the coordinates line up with the texture map. Note that even if all your texture coordinates are in range, GL_CLAMP can STILL mess you up. A pixel center might fall right next to the edge of your texture, in which case some if its samples fall off the edge and give you the dark/light seam. Also note that multisample and anisotropic filtering can make these GL_CLAMP artifacts even worse.

NVIDIA drivers (at least Detonator 40) have a way to control the behavior of GL_CLAMP via the control panel. The reason we added behavior to control clamping behavior via the registry (thereby “breaking” our implementation) is to work around the fact that a significant number of applications use GL_CLAMP when they really should be using GL_CLAMP_TO_EDGE.

I am using OpenGL 1.1, so GL_CLAMP_TO_EDGE is not even available. The book that I have says that in GL_CLAMP mode, when texture coordinates go beyond 1.0, it continues to sample from the last row of pixels along the edge, which is the behavior I want. In fact, my texture coordinates for the meshes end at 1.0, but with linear filtering it will try to blend with the pixel on the opposite side of the texture in GL_REPEAT mode. I do believe that a properly functioning GL_CLAMP is what I need. So is there a way to make the NVIDIA card use the normal GL_CLAMP mode (or to do whatever the other cards I’ve tried it on do)?

[This message has been edited by ioquan (edited 09-08-2002).]

I’ve got a nvidia card and the 40.41 detonator drivers, there is an option in the control panel nammed “Enable conformant OpenGL texture clamp behavior” (btw i download a reg to get the overclock option in this menu may be whitout it you can’t see this option). But I didn’t try it coz my engine use the SGIS extension to clamp to edge.

Arath

ioquan,

No, that’s not what it does. When the coordinate goes beyond 1.0, it samples at position 1.0, which happens to lie right between the rightmost texel and the texture border (which is probably solid black).

GL_CLAMP_TO_EDGE is implemented on most cards (even old cards) under OpenGL 1.1, as an extension. Note that displaying black lines is the correct behavior, and displaying seamless terrain is the wrong behaviour (even though the wrong behaviour is desirable).

If you’re rendering a bunch of terrain blocks, each with texture coordinates between 0 and 1, you have three ways of making it look good:

  1. inset your terrain texture coordinates by 0.5/texturesize, i e for 256x256 texture, go from 1/512 to 511/512. This doesn’t work with MIP mapping

  2. turn on GL_CLAMP_TO_EDGE (_EXT)

  3. Target only cards that support texture borders (GF3 and up) and upload the texels of the surrounding terrain blocks into the border

The third option will look the best, followed by the second (which is compatible with almost everything) followed by the first (because it can’t use MIP mapping).

There’s another solution that works on all cards that support OpenGL1.0 but it’s a bit annoying : add a border in your texture, so that you copy the edge texels into the border.

That is, if your texture is 256x256 :

  • create a 258x258 texture instead,
  • copy your 256x256 data into the middle of your 258x258 texture (between line 1 and line 256 and between column 1 and column 256),
  • copy the left column of your data (column 1 of the 258x258 texture) into the left border (column 0 of the 258x258 texture),
  • copy the right column of your data (column 256 of the 258x258 texture) into the right border (column 257 of the 258x258 texture),
  • and extrapolate those copies to the lines (ie copy top line of data into top border etc).

Then, instead of calling :
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 256, 256, 0, GL_RGB, GL_UNSIGNED_BYTE, pixels256x256)
call :
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 258, 258, 1, GL_RGB, GL_UNSIGNED_BYTE, pixels258x258)

That works with mipmaps too.

Advantages :

  • works on every OpenGL1.0 compatible card,
  • easy to implement if you centralized your texture calls into a class.

Disadvantages :

  • uses a bit more memry and a bit more CPU,
  • long to implement if the GL texture functions are called in several places in your code.