Lately, while getting used to C++ (already knowing OpenGL * fairly * well), I've gotten tired of the visual artifacts I see with textures at a distance, especially with large flat surfaces such as terrain. When coding in Java and OpenGL, I saw the same issues as well. Here's what I'm talking about:
I load my textures using the GL_COMPRESSED_RGBA_S3TC_DXT1_EXT (with DXT3 and DXT5 as well) extensions in OpenGL loading, obviously, .DDS images. Each image has 11 mipmaps (1 full-res texture and 11 lower-res textures), each of which are generated with bilinear in Paint.NET. The grass texture in this (where you see most of the artifacts) is 2048x2048. I AM using OpenGL 3.3, with shaders powering almost everything, so I can edit stuff in GLSL.
Image Parameters are as follows
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 11);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 16);
In the GLSL Shader, I call texture2D(grassTexture, terrainTexcoord * 512.0f); The '* 512.0f' is so that the grass isn't stretched all over the entire chunk (I'm using chunks w/ heightmap). The sampler is just a generic sampler2D.
And if anyone's wondering, I got the code for the DDS loader from http://www.opengl-tutorial.org/ (best site ever for OpenGL 3.3+ BTW)
I've probably given more can than what's needed but I hate hearing the "Can I see some more code?" Here's what I'd like to know, what is this effect called? What is a good way to fix it? Is there an post processing effect that takes care of this? Am I doing something wrong? (probably the latter, but oh well xD) Thanks!
Related
I'm currently working on a 3d project with Opengl but i have a blurry effect problem with distant block.
Like you can see, the nearby blocks are fine but the the blocks become blurry very quickly with the distance. (ps: ignore red block)
I tried differents resolutions of image (1024x1024 and 2048x2048) but with same result.
I tried to modify GL_TEXTURE_2D options but not fixed my problem.
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
if someone have an idea how to deal with this problem, it will be good. thx in advance
Posted as comment, now as proper answer so it can get marked as answered.
When looking at textured surfaces, especially distant ones, the frequency of texture features may exceed the necessary sampling frequency (pixels of the framebuffer/screen; see Nyquist limit). To combat this, MIP-mapping is employed, which reduces the feature frequency (minification). However, this does not account for perspective distortion. Anisotropic filtering places samples on the texture in a trapeze to correct for angled textures.
In OpenGL, you need either version 4.6 or EXT_texture_filter_anisotropic. You can then enable anisoptropic filtering per texture with glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, num_samples) - this sets the maximum number of samples the hardware may use, any given texture fetch may use less though if it deems more to be excessive. Be aware that the upper limit you can set is implementation-defined and must be queried with glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &max_samples). More samples mean higher quality (reasonably up until 16 or so), but also higher performance cost (not usually a deal-breaker).
I am rendering a huge 3D cube array, that sometimes counts thousands of cubes aligned right next to one another. I am rendering a jpg texture to the cubes, which is just a simple color with a black border around the frame.
The problem:
The array is huge, and the distant parts of the array get kind of mixed into one another, so to say. In other words, the borders in the distant cubes sometimes completely disappear, sometimes they form an arbitrary wavey line together with other neighboring borders. All in all, the scene looks kind of messy because all the fine details (hard borders between the neighboring cubes) are lost/melted together. After searching for the solution online, I understand that the problem might be in my choice of texture filtering options.
This is how the problem actually looks like in OpenGL:
This is how the current code for loading texture and setting texture parameters looks like:
glGenTextures(1, &texture3);
glBindTexture(GL_TEXTURE_2D, texture3);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
// set texture filtering parameters
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
//load image:
data = stbi_load("resources/textures/gray_border.jpg", &width, &height, &nrChannels, 0);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
glGenerateMipmap(GL_TEXTURE_2D);
By now, I have tried playing with changing different parameters to the function glGenerateMipmap() and altering between the parameters in the glTexParameteri() function, but none did work by now.
If you want to enable Mip Mapping, then you have to use one of the minifying functions like GL_NEAREST_MIPMAP_NEAREST, GL_LINEAR_MIPMAP_NEAREST, GL_NEAREST_MIPMAP_LINEAR or GL_LINEAR_MIPMAP_LINEAR, see glTexParameter and Texture - Mip Maps:
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
A further improvement can be gained by Anisotropic filtering, which is provides by the extension ARB_texture_filter_anisotropic and is a core feature since OpenGL 4.6.
e.g.
glTextureParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY, 16);
See Sampler Object - Anisotropic filtering
I'm trying to make an old game look a bit better, it uses opengl version 1.1, as you can see, this in a comparison between how the image looks on photoshop vs how it looks ingame.
As you can see on the right (ingame) the borders are blurry, not as sharp as the original image. (Please ignore all the text).
Is this expected because of the old version of opengl? Or is it something that could be fixed?
It looks like you have some sort of filtering enabled on your texture. Look for GL_LINEAR in your code and replace it with GL_NEAREST
glBindTexture(GL_TEXTURE_2D, texBuff);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
If I use the fixed pipeline, I can use
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
to make an image 'pixelated' as opposed to fragments in between pixels in the image being interpolated. How would I do the same thing in GLSL program? I'm using the texture2D function. I ask because I am using a shader program for my skybox, and you can see the edges because the edge pixels get blurred with grey. This problem gets fixed if I were to use the fixed pipeline and the above function calls.
You can use the same texture minification and magnification filters with the programmable pipeline. It sounds like the issue is not the min/mag filter, but with how you're handling texture clamping/wrapping. Either that or your textures have gray in them, which you probably don't want.
To set up texture clamping, you can do the following:
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
This will cause any pixels sampled from outside the texture to return the same color as the nearest pixel within the texture to that sample location.
As the other answers and comments alread pointed out, the texture sampling states will effect both the fixed function pipeline and the programmable pipeline in the same ways. I'd just like to add that in shaders, you can also completely bypass the sampling and use the GLSL texelFetch() functions where you can directly access the unfiltered texels - which will basically look like GL_NEAREST filtering. You will also lose the wrapping functionality and hve to use unnormalized integer texture coords, so this is probably not what you want in that scenario, though.
I am loading images into OpenGL app.Usually I am using Linear filtering but now testing nearest I found the resulting image is significantly darker than the original one.Btw,it also seems to me that the linear filtering causes some brightness loose too.Here are examples:
Linear filtering :
Nearest filtering :
Original image:
Now, I am setting mipmaps levels (to 4 ).I found that when not using mipmaps the original brightness is intact.What can be the problem?Is it related to gamma correction?
Here is the code for image load and mipmap generation:
ILinfo imageInfo;
iluGetImageInfo(&imageInfo);
iluFlipImage();
if (imageInfo.Format == IL_RGB)
{
ilConvertImage(IL_BGRA, IL_UNSIGNED_BYTE);
}
else if (imageInfo.Format == IL_RGBA)
{
ilConvertImage(IL_BGRA, IL_UNSIGNED_BYTE);
}
iluGetImageInfo(&imageInfo);
glPixelStorei(GL_PACK_ALIGNMENT, 1);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glGenTextures(1, &textureName);
glBindTexture(GL_TEXTURE_2D, textureName);
glTexStorage2D(GL_TEXTURE_2D,numMipMapLevels,GL_RGBA8,imageInfo.Width,imageInfo.Height);
glTexSubImage2D(GL_TEXTURE_2D,0,0,0,imageInfo.Width,imageInfo.Height,GL_BGRA,GL_UNSIGNED_BYTE,imageInfo.Data);
/* ==================================== */
// Trilinear filtering by default
if(smooth){
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glGenerateMipmap(GL_TEXTURE_2D);
}else{
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
}
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
I am also running MSAA pass in a custom FBO but it looks to be irrelevant to the issue as I tested also with MSAA turned off and the same problem persists.
From your code it looks like you create a mipmapped texture (with 4 mipmap levels as you say) but then only set the image for the first level. This means all the other level's images are undefined. When then using GL_NEAREST_MIPMAP_LINEAR, it will access the two mipmap levels that best fit the pixel-texel-ratio (the MIPMAP_LINEAR-part) and then pick a single nearest texel from each level (the NEAREST-part) and interpolate those.
From your image it looks like the unspecified mipmap levels are just black, so you get an interpolation between the texture color and black, thus a darkened texture (well, they could actually contain anything and the texturing shouldn't even work since the texture is incomplete, but maybe immutable storage behaves different in this regard). When not using mipmaps (thus only creating a single level with glTexStorage), there will only be a single level used in the filtering (even if using a mipmapped filter), which of course has a valid image.
If you intend to use some kind of mipmapping, then you should actually set the texture image for each and every mipmap level (or set the top-level image and do a glGenerateMipmap call afterwards). If you just wanted to use real nearest neighbour filtering, then just use GL_NEAREST (I've never actually seen much practical use for all the other mipmap filters except for the real trilinear filter GL_LINEAR_MIPMAP_LINEAR).