OpenGL 4.4 texture atlas artifacts - opengl

I am writing a small OpenGL tile map renderer in C# with OpenTK.
To render the tiles I first create a vertex array and then a vertex buffer.
At next I iterate over all tiles and dependent from the tile type I add two triangles to the vertex buffer with the corresponding vertices(texture coords, color, position).
To avoid texture expensive switching I use one big texture with all tile textures named texture atlas.
But I have a problem while working with a texture atlas.
The problem is that I get some artifacts at the edge of some tiles if I zooming out and moving the camera around.
The solutions I found on the internet were...
...to calculate texture coords with half pixel correction.
...to use a texture array
The first one I tried but it didn't worked how expected (More explained below).
The second one I don't know how to work with texture arrays and I couldn't find much about texture arrays on the internet.
I used the following texture filters:
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Nearest);
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Nearest);
Texture coords calculation with half pixel correction:
text_coord_x = (0.5f + pixel_x) / texture_width;
text_coord_y = (0.5f + pixel_x) / texture_height;
Texture coords calculation without half pixel correction:
text_coord_x = pixel_x / texture_width;
text_coord_y = pixel_y / texture_height;
One tile with half pixel correction:
One tile without half pixel correction:
Multiple tiles with half pixel correction:
Multiple tiles without half pixel correction:

For solve your problem with border at atlas you should left N pixel border around each sprite. This border should contain pixel information depends of tiling what you need. This border may be generated by lot 3d programs.
For example Blender can bake several textures into one with margin options:
Also N pixels width depends from mip levels of future atlas. So if you will add one pixel border then you can to use only single mip level (zero mip level). For two - you can use 0 mip and 1 mip level etc. Make sure that your use mip levels not more them border width was.

Related

How to wrap texture coordinates manually?

I am using C++ and HLSL and need to have my texture coordinates wrap so that the texture is tiled across a triangle.
After the coordinates are "wrapped" into 0-1 range they will be rotated, so I can't simply use the texture sampler AddressU and AddressV properties set to wrap, because they need to be wrapped and THEN rotated, so it can't be done inside the sampler.
The solution here is simple, just use the fractional part of the texture coordinate and it will wrap.
Here is an example of a pixel shader that will tile the texture 36 times (6 * 6):
input.tex *= 6.0f; //number of times to tile ^ 2
input.tex = frac(input.tex); //wraps texCoords to 0-1 range
return shaderTexture.Sample(SampleType, input.tex);
This does tile the texture, but can create a problem at the boarder where the texture wraps. The tiles have to divide evenly into the space they are being displayed on or it creates a seam where the boarders meet. My square that the texture is drawn to is 800x600 pixels, so tiling by 5 will divide evenly but 6 will not and will cause seams along the Y axis.
I have tried using the modulus operator input.tex = input.tex % 1 to wrap the coordinates but I get the exact same results. I have also tried changing the texture filtering method and the AddressU and AddressV properties along with countless different methods of debugging.
I had some luck using this code. If the x coordinate is too high it gets set to 0, and if it is too low it gets set to 1.
input.tex *= 6.0f;
input.tex = frac(input.tex);
if (input.tex.x > 0.999f) input.tex.x = 0;
if (input.tex.x < 0.001f) input.tex.x = 1;
return shaderTexture.Sample(SampleType, input.tex);
This only fixes the problem in certain spots though, so it is definitely not a solution.
Here is a picture that shows a texture (left) and what it looks like when wrapped manually (right). You can see that not everywhere that the boarders touch has this error.
I have also tried not changing the texture coordinates to 0-1 range and rotating them around the center of each tile instead of (0.5, 0.5) but I get identical results. Also my texture coordinates are completely independent of the vertices and are calculated inside the pixel shader.
Anything I have seen relating to this issue has to do with having a high value at one pixel and then a low value at the next, for example u = 0.95 and the next pixel u = 0.03, which causes it to interpolate backwards across the texture. But when I rotate my texture coordinates nothing changes at all. Even when each tile has a random rotation applied to it. In this case the edges have all sorts of different values bordering each other, not just a high value on the left side and a low value on the right side, but the area where the seam occurs never changes.
As MuertoExcobito said the main problem is that at the borders the texture coordinate jumps from 1 to 0 in a single pixel. Semantically it is right to say that the entire texture gets averaged in this pixel, but it is not caused by interpolating the texture from 1 to 0 in this pixel. The real reason is the mipmapping.
For your texture there are mipmaps generated as you are loading it. This means the texture get multiple mipmap levels which are all half sized in respect to the before.
If a texture becomes distorted sampling of the highest level would lead to oversampling (pointfiltering like artifacts). To fight oversampling a texture lookup chooses the appropriate mipmaplevel dependent on the changes of the texture coordinate in screen space. In your case the borders are a very high change in a small place, which is leading to use the lowest mipmap possible (which is as you see a small red dot, which is the reason for the red border).
Returning to your problem, you should take control over the mipmapping by using the texture lookup method SampleGrad (Docs). To get the current changes of the texture coordinate of your pixel you can use the intrinsic methods ddx and ddy. They return for a arbitary variable in your shader, how it changes locally to adjacent pixels (the correct explaination would go to deep for this topic). So using following code shouldn't change anything, because it should be semantically identical:
input.tex *= 6.0f; //number of times to tile ^ 2
input.tex = frac(input.tex); //wraps texCoords to 0-1 range
float2 xchange = ddx(input.tex);
float2 ychange = ddy(input.tex);
return shaderTexture.SampleGrad(SampleType, input.tex, xchange, ychange);
Now you can apply your code which prevents big changes in xchange and ychange to force the graphic device to use a higher mipmap. This should remove you artifacts.
If your texture doesn't need mipmapping, because you are rendering the texture screen aligned and the texture size doesn't lead to oversampling, you can use the simpler alternative sampleLevel (Docs) There you can pass a parameter, which picks a specific mipmap, which you can determine by yourself.
The code here is causing the sampling the entire texture in the span of a single pixel. For example, for two adjacent pixels at the seam, one 'u' sample could be 1.0-eps, the next sample would be 0.0+eps, where eps is a number smaller than the width of a texel. When the output pixels are interpolated, you will interpolate from 1.0 .. 0.0, sampling the entire texture between those two samples. The averaging of the entire texture causes the 'greyness', even though your input texture doesn't actually contain any pixels that are exactly grey.
If you require to rotate the texture coordinates within each range (eg. 0..1, 1..2 are rotated independently), there are a few ways this could be solved. First, you could change the interpolation from linear to point, which will avoid the interpolation between texels. However, if you require bilinear interpolation, this might not be acceptable. In that case, you could construct a 'grid' mesh, and map the input texture 0..1 across each tile, with the tiles texture coordinates rotated independently in the shader.
Another possible solution, would be to transform the coordinates to 0..1 space, perform the rotation, and then translate them back into their original space. For example, you would do:
// pseudo-code:
int2 whole;
input.tex = modf(input.tex, whole);
input.tex = Rotate(input.tex); // do whatever rotation is needed
input.tex += whole;
This would ensure that the wrapping does not have any discontinuities. Alternatively, you could have your rotation code take into account non-unity space texture coordinates.
Gnietschow posted the answer to this question but I am going to add an answer that shows exactly how I used the answer.
I'm actually not even sure why this works I just know it does even with other multiples of tiling, various textures, and random rotation.
input.tex *= 6.0f; //number of times to tile ^ 2
input.tex = frac(input.tex); //wraps texCoords to 0-1 range
float2 xchange = ddx(input.tex);
float2 ychange = ddy(input.tex);
if ((xchange.x < 0)) xchange = float2(0, 0);
if ((ychange.y < 0)) ychange = float2(0, 0);
return shaderTexture.SampleGrad(SampleType, input.tex, xchange, ychange);
if you don't need mipmapping at all, this other method Gniet mentioned works fine too
input.tex *= 6.0f; //number of times to tile ^ 2
input.tex = frac(input.tex); //wraps texCoords to 0-1 range
return shaderTexture.SampleLevel(SampleType, input.tex, 0);

Terrain rendering from bitmap in OpenGL

I have an assignment to render a terrain from a greyscale 8bit bmp and get colors to the terrain from a texture 24bit bmp. I managed to get a proper landscape with heights and so on, and also I get the colors from the texture bitmap. The problem is that the full color rendered terrain is very "blocky", it shows right colors and height but it's so blocky. I use glShadeModel(GL_SMOOTH) but it still looks so blocky, almost like I can see the pixels from the bitmap. So any hints are appreciated.
Do you use the bitmap as texture, or do you set vertex colours from the bitmap? I suggest you use a texture, using the planar vertex position as texture coordinate.
One thing you have to take into consideration is when you are rendering are you using GL_TRIANGLES or GL_TRIANGLESTRIPS this makes a difference on performance, second if you are using lighting you have to define your normals and each triangle or each vertex of each triangle, the problem then becomes tricky because almost every triangle is on a different plane. Not having proper normals would make it look blocky. The third thing that makes a difference is how big or small the triangles are; the smaller the triangles or the more divisions in your [x,z] plane increases you resolution thus increases the visual quality, but also slows down your frame rate. You have to find a good balance between the two.

Off-screen multiple render targets using Frame Buffer Object (FBO) or?

Situation: Generating N samples of a shape and corresponding edges (using Sobel filter or my own) with different transformations and rotations, while viewport (size=600*600) and camera remain constants. i.e. there will be N samples + N corresponding edges.
I am thinking to do like this,
Use One FBO with 2 renderbuffers [i.e. size of each buffer will be= (N *600) * 600]- 1st for N shapes and 2nd for edges of the corresponding shapes
Questions:
Which is the best way to achieve above things?
Though viewport size is 600*600pixels but shape will only occupy around 50*50pixels. So is there any efficient way to apply edge detection on bounding box/AABB region only on 2nd buffer? Also only reading 2N bounding box (N sample + N corresponding edges) in efficient way?
1 : I'm not sure what you call "best way". Use Multiple Render Targets : you create two 600*N textures, bind them both to the FBO with glDrawArrays, and in your fragment shader, so something like that :
layout(location = 0) out vec3 color;
layout(location = 1) out vec3 edges;
When writing to "color" and "edges", you'll effectively write in your textures.
2 : You shouldn't do this. Compute your bounding boxes on the CPU, and project them (i.e. multiply each corner by your ModelViewProjection matrix) to get the bounding boxes in 2D
By the way : Compute your bounding boxes first, so that you won't need 600*600 textures but 50*50...
EDIT : You usually restrict the drawn zone with glViewPort. But ther is only one viewport, and you need several. You can try the Viewport array extension and live on the bleeding edge, or pass the AABB in a texture, or don't worry about that until performance matters...
Oh, and you can't use Sobel just like that... Sobel requires that you can read all texels around, which is not the case since you're currently rendering said texels. Either make a two-pass algorithm without MRTs (first color, then edges) or don't use Sobel and guess you edges in the shader ( I don't really see how )
Like Calvin said, you have to first render your object into the the first framebuffer and then bind this as texture (use texture attachment rather than a renderbuffer) for the second pass to find the edges, as the edge detection usually needs access to a pixel's surrounding pixels.
Regarding your second question, you could probably use the stencil buffer. Just draw your shapes in the first pass and let them write a reference value into the stencil buffer. Then do the edge detection (usually by rendering a screen sized quad with the corrseponding fragment shader) and configure the stencil test to only pass where the stencil buffer contains the reference value. This way (assuming early-z hardware, which is quite common now) the fragment shader will only be executed on the pixels the shape has actually been drawn onto.

Manual GL_REPEAT in GLSL

Currently I have a texture atlas that is 2048 x 2048 pixels set up with three 512 x 512 textures stored, and I am only applying one texture to the object. So I used the following code to position the texture coordinates (from zero to 1) to the correct position on the texture atlas for that texture:
color = texture2D(tex_0, vec2(0.0, 1024.0/2048.0) + mod(texture_coordinate*vec2(40.0), vec2(1.0))*vec2(512.0/2048.0));
The problem is that when I apply this, there is a black border around the texture. I presume that this is because OpenGL can't blend the two pixels at the place of that border.
So how do I get rid of the border?
Edit*
I have already tried to move the starting and ending boundaries in toward the center of the texture and that didn't work.
Edit*
I found the source of the problem, the automatic mipmap generation is blending the textures in the texture atlas together. This means I have to write my own mipmapping function. (As far as I can tell)
If anyone has any better ideas, please do post.
Instead of using a normal 2D texture as the texture atlas with a grid of textures, I used the GL_2D_TEXTURE_ARRAY functionality to create a 3D texture that mipmapped correctly and repeated correctly. That way the textures did not blend together at higher mipmap levels.

How to use GL_REPEAT to repeat only a selection of a texture atlas? (OpenGL)

How can I repeat a selection of a texture atlas?
For example, my sprite (selection) is within the texture coordinates:
GLfloat textureCoords[]=
{
.1f, .1f,
.3f, .1f,
.1f, .3f,
.3f, .3f
};
Then I want to repeat that sprite N times to a triangle strip (or quad) defined by:
GLfloat vertices[]=
{
-100.f, -100.f,
100.f, -100.f,
-100.f, 100.f,
100.f, 100.f
};
I know it has something to do with GL_REPEAT and textureCoords going passed the range [0,1]. This however, doesn't work: (trying to repeat N = 10)
GLfloat textureCoords[]=
{
10.1f, 10.1f,
10.3f, 10.1f,
10.1f, 10.3f,
10.3f, 10.3f
};
We're seeing our full texture atlas repeated...
How would I do this the right way?
It can't be done the way it's described in the question. OpenGL's texture coordinate modes only apply for the entire texture.
Normally, to repeat a texture, you'd draw a polygon that is "larger" than your texture implies. For instance, if you had a square texture that you wanted to repeat a number of times (say six) over a bigger area, you'd draw a rectangle that's six times as wide as it is tall. Then you'd set the texture coordinates to (0,0)-(6,1), and the texture mode to "repeat". When interpolating across the polygon, the texture coordinate that goes beyond 1 will, due to repeat being enabled, "wrap around" in the texture, causing the texture to be mapped six times across the rectangle.
None of the texture wrap modes support the kind of operation as described in the question, i.e. they all map to the full [0,1] range, not some arbitrary subset. when you're texturing using just a part of the texture, there's no way to specify that larger texture coordinate in a way that makes OpenGL repeat it inside only the sub-rectangle.
You basically have two choices: Either create a new texture that only has the sprite you need from the existing texture or write a GLSL vertex program to map the texture coordinates appropriately.
I'm not sure you can do that. I think OpenGL's texture coordinate modes only apply for the entire texture. When using an atlas, you're using "sub-textures", so that your texture coordinates never come close to 0 and 1, the normal limits where wrapping and clamping occurs.
There might be extensions to deal with this, I haven't checked.
EDIT: Normally, to repeat a texture, you'd draw a polygon that is "larger" than your texture implies. For instance, if you had a square texture that you wanted to repeat a number of times (say six) over a bigger area, you'd draw a rectangle that's six times as wide as it is tall. Then you'd set the texture coordinates to (0,0)-(6,1), and the texture mode to "repeat". When interpolating across the polygon, the texture coordinate that goes beyond 1 will, due to repeat being enabled, "wrap around" in the texture, causing the texture to be mapped six times across the rectangle.
This is a bit crude to explain without images.
Anyway, when you're texturing using just a part of the texture, there's no way to specify that larger texture coordinate in a way that makes OpenGL repeat it inside only the sub-rectangle.
None of the texture wrap modes support the kind of operation you are looking for, i.e. they all map to the full [0,1] range, not some arbitrary subset. You basically have two choices: Either create a new texture that only has the sprite you need from the existing texture or write a GLSL pixel program to map the texture coordinates appropriately.
While this may be an old topic; here's how I ended up doing it:
A workaround would be to create multiple meshes, glued together containing the subset of the Texture UV's.
E.g.:
I have a laser texture contained within a larger texture atlas, at U[0.05 - 0.1] & V[0.05-0.1].
I would then construct N meshes, each having U[0.05-0.1] & V[0.05-0.1] coordinates.
(N = length / texture.height; height being the dimension of the texture I would like to repeat. Or easier: the amount of times I want to repeat the texture.)
This solution would be more cost effective than having to reload texture after texture.
Especially if you batch all render calls (as you should).
(OpenGL ES 1.0,1.1,2.0 - Mobile Hardware 2011)
Can be done with modulo of your tex-coords in shader. The mod will repeat your sub range coords.
I was running into your question while working on the same issue - although in HLSL and DirectX. I also needed mip mapping and solve the related texture bleeding too.
I solved it this way:
min16float4 sample_atlas(Texture2D<min16float4> atlasTexture, SamplerState samplerState, float2 uv, AtlasComponent atlasComponent)
{
//Get LOD
//Never wrap these as that will cause the LOD value to jump on wrap
//xy is left-top, zw is width-height of the atlas texture component
float2 lodCoords = atlasComponent.Extent.xy + uv * atlasComponent.Extent.zw;
uint lod = ceil(atlasTexture.CalculateLevelOfDetail(samplerState, lodCoords));
//Get texture size
float2 textureSize;
uint levels;
atlasTexture.GetDimensions(lod, textureSize.x, textureSize.y, levels);
//Calculate component size and calculate edge thickness - this is to avoid bleeding
//Note my atlas components are well behaved, that is they are all power of 2 and mostly similar size, they are tightly packed, no gaps
float2 componentSize = textureSize * atlasComponent.Extent.zw;
float2 edgeThickness = 0.5 / componentSize;
//Calculate texture coordinates
//We only support wrap for now
float2 wrapCoords = clamp(wrap(uv), edgeThickness, 1 - edgeThickness);
float2 texCoords = atlasComponent.Extent.xy + wrapCoords * atlasComponent.Extent.zw;
return atlasTexture.SampleLevel(samplerState, texCoords, lod);
}
Note the limitation is that the mip levels are blended this way, but in our use-case that is completely fine.
Can't be done...