How OpenGL handles the texture border when using GL_REPEAT? - opengl

I understand that GL_REPEAT parameter will ignore the integer portion of the texture coordinate, only the fractional part is used when doing texture mapping. The question is how OpenGL handles the texture border? For instance, if the texcoord is 2.0, the return value should be the same as the return value at texcoord 0.0 or at 1.0? I assume the "normal" texcoord is in [0.0,1.0] range, which is the most common texcoord range.
Thanks for your answer!

Your concrete example is interesting. Say you have a 1D texture of size 2 [a b]
What you actually get from a texture coordinate of 0.0 depends on filtering. It's not necessarily a. The only location where you have a and just a for LINEAR (ignoring mipmapping) is at texture coordinate 0.25. The same is true for b and 0.75.
At 0.0 and 1.0 (and 2.0, for that matter), you will get (a+b)/2 (again, LINEAR). It is not the case that 0 will give you a and 1 will give you b.
-0.25 0 0.25 .5 0.75 1 1.25 ...
_____________________________
| | |
B | A | B | A
_____________________________
In the case of NEAREST filtering, 0.0 and 1.0 are exactly on the edge of the texels, While I don't remember exactly what the specification says on that case, I would not rely on it.
All that said... All this is discussing the texture coordinates that are used by a fragment. They are not the ones you passed in, but the ones that were rasterized.
E.g. If you draw a quad that covers a 2 pixel region, with 1 texture coordinate from 0 to 1.
Here is a diagram of the pixels that get covered along with the interpolated coordinates:
0 0.25 .5 0.75 1
_________________
| | |
| P0 | P1 |
_________________
The texture coordinates that the fragments will use are indeed 0.25 and 0.75 (aka the rasterizer interpolates texture coordinates in the middle of the pixel), even though you passed in 0 and 1.

2.0 is clamped to 0.0, as it doesn't fall out of the [0, 1] range, it doesn't consider the texture border.

Related

Lines when attempting to color each pixel of the screen?

I'm trying to draw a point at every pixel on the screen but I'm getting a weird anomaly: two red lines, one vertical, one horizontal:
My setup is like this:
I've got an ortho projection matrix like this:
glm::mat4 projection=glm::ortho(0.f, screen_width-1.f, 0.f, screen_height-1.f, 1.f, -1.f);
It's just setting the opengl coordinate system to be the same size as the screen's pixel dimensions.
Then, I'm creating filling the data into a vector like this:
for (int i=0; i<screen_height; ++i){
for (int j=0; j<screen_width; ++j){
idata.push_back(j);
idata.push_back(i);
idata.push_back(1);
}
}
I'm uploading integers into a VBO with this:
glBufferData(GL_ARRAY_BUFFER, sizeof(GLint)*idata.size(), &idata[0], GL_DYNAMIC_DRAW);
Setting up the attributes like so:
glVertexAttribIPointer(0, 3, GL_INT, 3*sizeof(GLint), (void *)(0*sizeof(GLint)));
glEnableVertexAttribArray(0);
This is the layout of my shader:
layout (location=0) in ivec3 position;
And this is my draw call:
glDrawArrays(GL_POINTS, 0, idata.size()/3);
That's a rounding issue, because you did set up the projection in a very weird way:
glm::ortho(0.f,screen_width-1.f,0.f,screen_height-1.f,1.f,-1.f)
In OpenGL's window space, pixels are an actual area (of squares of size 1x1).
So, If your screen is for example 4 pixel wide, this is what you get in terms of pixels
+--+--+--+--+
|0 |1 |2 |3 | integer pixel coors
...
However, the space you work in is continuous (in principle, at least), adn OpenGL's window space will go from 0 to 4 (not 3), which makes perfect sense as there are four pixels, and each pixel being one unit wide:
0 1 2 3 4 OpenGL window space
+--+--+--+--+
|0 |1 |2 |3 | integer pixel coords
....
Using OpenGL's window space conventions, the pixel centers lie at half-integer coordinates.
Now you're not drawing in OpenGL window space directly, but you can set up transformation to basically undo the viewport transform (which goes from NDC to window space) to get a pixel-exact mapping. But you didn't do that. what you did instead is:
3/4 9/4
0 6/4 3 your coordinate system
+--+--+--+--+
|0 |1 |2 |3 | integer pixel coords
....
And you're draw at full integers in that weird system, which just does not map very well to the actual pixels.
So you should do
glm::ortho(0.f,screen_width,0,screen_height,1.f,-1.f)
However, since you seem to be drawing at integer positions, you end up drawing exactly at the pixel corners, and might still see some rounding issues (and the rounding direction will be up to the implementation, as per the spec).
So, you better shift it so that integer coordinates lie at pixel centers
glm::ortho(-0.5f, screen_width-0.5f, -0.5f, screen_height-0.5f, 1.f, -1.f)
0.5 2.5
-0.5 1.5 3.5 your coordinate system
+--+--+--+--+
|0 |1 |2 |3 | integer pixel coords
....

How do I half texel offset when not ortho rendering?

My rendering is bleeding pixels (the dots in red) along the edge of these two perpendicular quads.
I'm using:
GL_NEAREST min/max texture filtering.
texture function on fragment shader.
A sprite atlas of 1024x1024, with the desired sprite # (x=320,y=732,width=8,height=8)
s1 = 320.0f/1024.0f;
s2 = (320.0f+8.0f)/1024.0f;
t1 = 732.0f/1024.0f;
t2 = (732.0f+8.0f)/1024.0f;
Render #1 (with a dark texture to make it easier to see bleeding):
Render #2: (with bright texture for counting texels, but the red pixels are still present):
I understand that OpenGL's texture function will sample at the pixel center, but when I try to offset the "S,T" coordinates by half-a-pixel, it (fixes it but) has adverse effects.
For example, the texture coordinates below shrinks the width of the texels along the border of the sprite.
s1 = (0.5f+320.0f)/1024.0f;
s2 = (0.5f+320.0f+8.0f)/1024.0f;
t1 = (-0.5f+732.0f)/1024.0f;
t2 = (-0.5f+732.0f+8.0f)/1024.0f;
I tried a bunch of possible S&T offset combinations (adding 0.5 to both S&T, subtracting from both, adding to T but subtracting from S) and different values, none of it worked. I don't know what I'm missing; I don't think this is a T-junction issue because the bleeding pixel color is coming from the texture (not the background).
Only subtracting or adding a half texel offset will never solve the problem since you will always keep the problem on one side.
What you can do is to clamp your texel coordinates in the range x+0.5 and x+width-0.5 which will prevent bleeding on both sides. The code could looks like this:
s = clamp(texCoord.x, 320 + 0.5, 320 + 8 - 0.5) / 1024;
t = clamp(texCoord.y, 732 + 0.5, 732 + 8 - 0.5) / 1024;

OpenGL 3D Transformation: z-Aspect ratio

I'm doing 3D perspective projection in OpenGL (webgl), doing it myself with uniform-matrices.
Everthing is working fine, but I have an aspect ration of 3:2 (600px x 400px) and this distorts all geometry rendered.
In 2D I used to fix this in the model matrix by dividing x and y through 1 / width and 1 / height respectively.
Now I also have z to worry about and I am pretty clueless how / where to transform z to not distort on my 3:2 aspect ratio.
The model matrix does not seem to offer any opportunity to do this and I don't know where / what to do in the projection matrix.
Edit:
Projection Matrix:
#_pMatrix = [
1, 0.0, 0.0, 0.0,
0.0, 1, 0.0, 0.0,
0.0, 0.0, -(f + n) / (f - n), -1,
0.0, 0.0, -2.0 * n * f / (f - n), 0.0
]
Column major order
Edit 2:
Weird distortions on n < 1
You're missing the *left*, *right*, *top*, *bottom* in your projection matrix.
2*n
----- 0 0 0
r-l
2*n
0 ----- 0 0
t-b
r+l t+b -(f+n)
----- ----- ------ -1
r-l t-b f-n
-2*f*n 0
0 0 ------
f-n
If you define (r-l)/(t-b) = 3/2 such that the viewing volume is the appropriate size for your model, then you should be set.
Here are some slides describing the various projection matrices and how they're derived:
http://www.cs.unm.edu/~angel/CS433/LECTURES/CS433_17.pdf
They're by Edward Angel, the author of Interactive Computer Graphics, which is where I got this matrix. Unfortunately the OpenGL Red Book doesn't seem to work through the math at all.

opengl oblique projection

I want to create a oblique (cavalier) projection in OpenGL. I know this operation is not default supported and instead I need a Shear Matrix and then make an Orthogonal Projection.
Can you tell me what are the OpenGl steps / functions that I have to make?
I've not used a oblique/cavalier projection before, but the following should give you an idea of how to proceed:
Create a 4x4 shear matrix,
H(θ, Φ) = | 1, 0, -cot(θ), 0 |
| 0, 1, -cot(Φ), 0 |
| 0, 0, 1, 0 |
| 0, 0, 0, 1 |
θ being the shear in X, Φ being the shear in Y, and Z being left alone.
(ref: slide 11 of http://www.cs.unm.edu/~angel/CS433/LECTURES/CS433_17.pdf)
Multiply that by your orthographic projection,
| 2/(r-l), 0, 0, -(r+l)/(r-l) |
| 0, 2/(t-b), 0, -(t+b)/(t-b) |
| 0, 0, 2/(f-n), -(f+n)/(f-n) |
| 0, 0, 0, 1 |
(described by, left, right, bottom, top, near and far)
(ref: http://en.wikipedia.org/wiki/Orthographic_projection_%28geometry%29)
OpenGL then allows you to upload this matrix directly (as an array of 16 floats) via the function glLoadMatrixf():
GLfloat proj[16] = { ... };
glMatrixMode(GL_PROJECTION); // Make sure we're modifying the *projection* matrix
glLoadMatrixf(proj); // Load the projection
For a more in depth look at how viewing and transformations work in OpenGL, I'd refer you to Chapter 3 of the OpenGL "Red Book". There they use glOrtho() to create and apply an orthographic projection.
Edit:
As datenwolf points out, bear in mind that the matrix elements in OpenGL are specified in column major order.
OpenGL allows you to specify arbitrary projection matrices. Construct the desired projection matrix yourself to map the incoming vertices into the range -1 to 1 in each dimension, then load it using
GLfloat custrom_projection[16] = {
...
};
glMatrixMode(GL_PROJECTION);
glLoadMatrix(custom_projection);
OpenGL indexes the matrix elements in colum major order, i.e.
0 4 8 12
1 5 9 13
2 6 10 14
3 7 11 15
Since the so-called oblique projection is obtained by rotating the projection plain by a certain angle away from the right one, which produces nothing but a lengthened image along the rotation axis, I think it suffices to just scale the normal orthogonal projection along that axis, by a factor of \csc\theta. This claim can be proven by trigonometry equalities, e.g., \sin\theta+\cos\theta \cot\theta=\csc\theta. If your oblique projection is specified by the \theta and \phi like in luke's answer, the axis angle can be computed as a trigonometry exercise based on this two angles, say, \arctan(\tan\theta\sqrt(1+\cot^2\phi)).

Texture wrong value in fragment shader

I'm loading a custom data into 2D texture GL_RGBA16F:
glActiveTexture(GL_TEXTURE0);
int Gx = 128;
int Gy = 128;
GLuint grammar;
glGenTextures(1, &grammar);
glBindTexture(GL_TEXTURE_2D, grammar);
glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA16F, Gx, Gy);
float* grammardata = new float[Gx * Gy * 4](); // set default to zero
*(grammardata) = 1;
glTexSubImage2D(GL_TEXTURE_2D,0,0,0,Gx,Gy,GL_RGBA,GL_FLOAT,grammardata);
int grammarloc = glGetUniformLocation(p_myGLSL->getProgramID(), "grammar");
if (grammarloc < 0) {
printf("grammar missing!\n");
exit(0);
}
glUniform1i(grammarloc, 0);
When I read the value of uniform sampler2D grammar in GLSL, it returns 0.25 instead of 1. How do I fix the scaling problem?
if (texture(grammar, vec2(0,0) == 0.25) {
FragColor = vec4(0,1,0,1);
} else
{
FragColor = vec4(1,0,0,1);
}
By default texture interpolation is set to the following values:
GL_TEXTURE_MIN_FILTER = GL_NEAREST_MIPMAP_LINEAR,
GL_TEXTURE_MAG_FILTER = GL_LINEAR
GL_WRAP[R|S|T] = GL_REPEAT
This means, in cases where the mapping between texels of the texture and pixels on the screen does not fit, the hardware interpolates will interpolate for you. There can be two cases:
The texture is displayed smaller than it actually is: In this case interpolation is performed between two mipmap levels. If no mipmaps are generated, these are treated as beeing 0, which could lead to 0.25.
The texture is displayed larger than it actually is (and I think this will be the case here): Here, the hardware does not interpolate between mipmap levels, but between adjacent texels in the texture. The problem now comes from the fact, that (0,0) in texture coordinates is NOT the center of pixel [0,0], but the lower left corner of it.
Have a look at the following drawing, which illustrates how texture coordinates are defined (here with 4 texels)
tex-coord: 0 0.25 0.5 0.75 1
texels |-----0-----|-----1-----|-----2-----|-----3-----|
As you can see, 0 is on the boundary of a texel, while the first texels center is at (1/(2 * |texels|)).
This means for you, that with wrap mode set to GL_REPEAT, texture coordinate (0,0) will interpolate uniformly between the texels [0,0], [-1,0], [-1,-1], [0,-1]. Since -1 == 127 (due to repeat) and everything except [0,0] is 0, this results in
([0,0] + [-1,0] + [-1,-1] + [0,-1]) / 4 =
1 + 0 + 0 + 0 ) / 4 = 0.25