Problems with uv when using half pixel translations - opengl

I am rendering 2 triangles to make a square, using a single draw call with GL_TRIANGLE_STRIP.
I calculate position and uv in the shader with:
vec2 uv = vec2(gl_VertexID >> 1, gl_VertexID & 1);
vec2 position = uv * 333.0f;
float offset = 150.0f;
mat4 model = mat4(1.0f);
model[3][1] = offset;
gl_Position = projection * model * position;
projection is a regular orthographic projection that matches screen size.
In the fragment shader I want to draw each first line of pixels with blue and each second line of pixels with red color.
int v = int(uv.y * 333.0f);
if (v % 2 == 0) {
color = vec4(1.0f, 0.0f, 0.0f, 1.0f);
} else {
color = vec4(0.0f, 0.0f, 1.0f, 1.0f);
}
This works ok, however if I use offset that will give me a subpixel translation:
offset = 150.5f;
The 2 triangles don't get matching uvs as seen in this picture:
What am I doing wrong?

Attribute interpolation is done only with a finite precision. That means that due to round-off errors, even a difference in 1 ulp (unit-last-place, i.e. least significant digit) can cause the result rounded to the other direction. Since the order of operations in the hardware interpolation unit can be different between the two triangles, the values prior to rounding can be slightly different. OpenGL does not provide any guarantees about that.
For example you might be getting 1.499999 in the upper triangle and 1.50000 in the lower triangle. Consequently when you add an offset of 0.5 then 1.99999 will be rounded down to 1.00000 but 2.000000 will be rounded to 2.00000.
If pixel perfect results are important to you I suggest you calculate uv coordinates manually from the gl_FragCoord.xy. In a case of an axis-aligned rectangle, as in your example, it is straightforward to do.

Related

How the vertex shader is passing to the fragment shader

In the following vertex shader I am not able to understand what output does the lines 1 and 2 give out.
#version 450
out vec2 texcoord;
void main()
{
texcoord = vec2((gl_VertexID << 1) & 2, gl_VertexID & 2); // Line 1
gl_Position = vec4(texcoord * 2.0f - 1.0f, 0.0f, 1.0f); // Line 2
}
This is a method to define a full-screen quad using a single triangle.
A full-screen quad can be drawn as two triangles, but also as one large triangle which contains the quad. See here for more information.
In your example, the glDrawArrays function will be instructed to draw 3 vertices, causing the vertex shader to run 3 times for gl_VertexID 0, 1, and 2. This produces the following data:
gl_VertexID 0: texcoords(0.0F, 0.0F), gl_Position(-1.0F, -1.0F)
gl_VertexID 1: texcoords(2.0F, 0.0F), gl_Position( 3.0F, -1.0F)
gl_VertexID 2: texcoords(0.0F, 2.0F), gl_Position(-1.0F, 3.0F)
This defines a triangle in screenspace coordinates, going from the bottom left of the screen, then to the right ("beyond the screen"), and finally going to the top left ("beyond the screen"). The top-right corner of the quad sits on the hypotenuse of the triangle . The UV-coordinates are adjusted accordingly. The excess parts of the triangle will be clipped before rendering the quad's fragments.
The exact computations happen as follows:
We may assume this code is designed to only work for gl_VertexID 0, 1 and 2.
The bitwise AND in gl_VertexID & 2 clears all bits except for the second one, so the value 2 is kept at 2, any other value becomes 0. With a << 1 shift first, only gl_VertexID == 1 becomes 2 and any other value becomes 0.
For the gl_Position, the texcoords are mapped from the range [0, 2] to [-1, 3] to obtain the triangle vertex' positions.
Line 1 calculate texture coordinates (U,V in the range of 0..1 in the texture image).
It fill's the texcoord variable, defined as an output from the shader in this line:
out vec2 texcoord;
Line 2 calculates the vertex position, and updates the predefined global variable gl_Position. This variable is then availavle as an outout from the shader.
More about opengl predefined global variables: Built-in Variable (GLSL).
More about gl_position: documentation.
Regarding the content of the output:
Looks like the U texture coordinate ((gl_VertexID << 1) & 2) is either 0 or 1 depending of the least significant bit in gl_VertexID, and the V texture coordinate (gl_VertexID & 2) is either 0 or 1 depending on the 2nd bit of gl_VertexID.
gl_Position x is either 1 if U is 1 and -1 otherwise. Similarly y is dependant on V.
I cannot say why it is required by the application to be that way.

How to keep Opengl Scatter Instances size unchanged?

Here is my question, i will list them to make it clear:
I am writing a program drawing squares in 2D using instancing.
My camera direction is (0,0,-1), camera up is (0,1,0), camera position is (0,0,3), and the camera position changes when i press some keys.
What I want is that, when I zoom in (the camera moves closer to the square), the square's size(in the screen) won't change. So in my shader:
#version 330 core
layout(location = 0) in vec2 squareVertices;
layout(location = 1) in vec4 xysc;
out vec4 particlecolor;
uniform mat4 VP;
void main()
{
float particleSize = xysc.z;
float color = xysc.w;
gl_Position = VP* vec4(xysc.x, xysc.y, 2.0, 1.0) + vec4(squareVertices.x*particleSize,squareVertices.y*particleSize,0,0);
particlecolor = vec4(1.0f * color , 1.0f * (1-color), 0.0f, 0.5f);
}
Please notice that, inorder to keep the squares' size unchanged, what I do is:
1. transform the center of the square first
VP * vec4(xysc.x, xysc.y, 2.0, 1.0)
2. then compute one of the four corners (x,y,z,1) of the square
+ vec4(squareVertices.x*particleSize,squareVertices.y*particleSize,0,0);
instead of:
gl_Position = VP* (vec4(xysc.x, xysc.y, 2.0, 1.0) + vec4(squareVertices.x*particleSize,squareVertices.y*particleSize,0,0));
However when I move the camera closer to z=0 plane. The squares' size grows unexpectedly. Where is the problem? I can provide a demo code if necessary.
Sounds like you use a perspective projection, and the formula you use in steps 1 and 2 won't work because VP * vec4 will in the general case result in a vec4(x,y,z,w) with the w value != 1, and adding a vec4(a,b,0,0) to that will just get you vec3( (x+a)/w, (y+b)/w, z) after the perspective divide, while you seem to want vec3(x/w + a, y/w +b, z). So the correct approach is to scale a and b by w and add that before the divde: vec4(x+a*w, y+b*w, z, w).
Note that when you move your camera closer to the geometry, the effective w value will approach towards zero, so (x+a)/w will be a greater than x/w + a, resulting in your geometry getting bigger.

Artifacts with rendering texture with horizontal and vertical lines with OpenGL

I created 8x8 pixel bitmap letters to render them with OpenGL, but sometimes, depending on scaling I get weird artifacts as shown below in the image. Texture filtering is set to nearest pixel. It looks like rounding issue, but how could there be some if the line is perfectly horizontal.
Left original 8x8, middle scaled to 18x18, right scaled to 54x54.
Vertex data are unsigned bytes in format (x-offset, y-offset, letter). Here is full code:
vertex shader:
#version 330 core
layout(location = 0) in uvec3 Data;
uniform float ratio;
uniform float font_size;
out float letter;
void main()
{
letter = Data.z;
vec2 position = vec2(float(Data.x) / ratio, Data.y) * font_size - 1.0f;
position.y = -position.y;
gl_Position = vec4(position, 0.0f, 1.0f);
}
geometry shader:
#version 330 core
layout (points) in;
layout (triangle_strip, max_vertices = 4) out;
uniform float ratio;
uniform float font_size;
out vec3 texture_coord;
in float letter[];
void main()
{
// TODO: pre-calculate
float width = font_size / ratio;
float height = -font_size;
texture_coord = vec3(0.0f, 0.0f, letter[0]);
gl_Position = gl_in[0].gl_Position + vec4(0.0f, height, 0.0f, 0.0f);
EmitVertex();
texture_coord = vec3(1.0f, 0.0f, letter[0]);
gl_Position = gl_in[0].gl_Position + vec4(width, height, 0.0f, 0.0f);
EmitVertex();
texture_coord = vec3(0.0f, 1.0f, letter[0]);
gl_Position = gl_in[0].gl_Position + vec4(0.0f, 0.0f, 0.0f, 0.0f);
EmitVertex();
texture_coord = vec3(1.0f, 1.0f, letter[0]);
gl_Position = gl_in[0].gl_Position + vec4(width, 0.0f, 0.0f, 0.0f);
EmitVertex();
EndPrimitive();
}
fragment shader:
#version 330 core
in vec3 texture_coord;
uniform sampler2DArray font_texture_array;
out vec4 output_color;
void main()
{
output_color = texture(font_texture_array, texture_coord);
}
I had the same problem developing with Freetype and OpenGL. And after days of researching and scratching my head, I found the solution. In my case, I had to explicitly call the function 'glBlendColor'. Once, I did that, I did not observe any more artifacts.
Here is a snippet:
//Set Viewport
glViewport(0, 0, FIXED_WIDTH, FIXED_HEIGHT);
//Enable Blending
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glBlendColor(1.0f, 1.0f, 1.0f, 1.0f); //Without this I was having artifacts: IMPORTANT TO EXPLICITLY CALLED
//Set Alignment requirement to 1 byte
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
I figured out the solution after reviewing the source code of this OpenGL-Freetype library on github: opengl-freetype library
Well, when using nearest filtering, you will see such issues if your sample location is very close to the boundary between two texels. And since the tex coords are to be interpolated separately for each fragment you are drawing, slight numerical inaccuracies will result in jumping between those two texels.
When you draw an 8x8 texture to an 18x18 pixel big rectangle, and your rectangle is perfectly aligned to the putput pixel raster, you are almost guaranteed to trigger that behavior:
Looking at the texel coodinates will then reveal that for the very bottom output pixel, the texture coords would be interpolated to 1/(2*18) = 1/36. Going one pixel up will add 1/18 = 2/36 to the t coordinate. So for the fifth row from the bottom, it would be 9/36.
So for the 8x8 texel big texture you are sampling from, you are actually sampling at unnormalized texel coordinates (9/36)*8 == 2.0. This is exactly the boundary between the second and third row of your texture. Since the texture coordinates for each fragment are interpolated by a barycentric interpolation between the tex coords assigned to the three vertices froming the triangle, there can be slight inaccuracies. And even the slightest possible inaccuracy representable in floating point format would result in flipping between two texels in this case.
I think your approach is just not good. Scaling bitmap fonts is always problematic (maybe besides integral scale factors). If you want nicely looking scalable texture fonts, I recommend you to look into signed distance fields. It is quite a simple and powerful technique, and there are tools available to generate the necessary distance field textures.
If you are looking for a quick hack, you coud also just offset your output rectangle slightly. You basically must make sure to keep the offset in [-0.5,0.5] pixels (so that never different fragments are generated during rasterization, and you must make sure that all the potential sample locations will never lie close to an integer, so the offset will depend on the actual scale factor.

GLSL 1.2 floor() issues in Vertex Shader

I'm trying to calculate texture coordinates based on the coordinates of an incoming vertex in the Vertex Shader. This is a stripped down version of my attempt:
#version 120
varying vec4 color;
uniform sampler2D heightmap;
uniform ivec2 heightmapSize;
void main(void)
{
vec2 fHeightmapSize = vec2(heightmapSize);
vec2 pos = gl_Vertex.zx + vec2(0.5f, 0.5f);
vec2 offset = floor(fHeightmapSize * pos) + vec2(0.5f, 0.5f);
if (fract(offset.x) > 0.45f && fract(offset.x) < 0.55f
&& fract(offset.y) > 0.45f && fract(offset.y) < 0.55f)
color = vec4(0.0f, 1.0f, 0.0f, 1.0f);
else
color = vec4(1.0f, 0.0f, 0.0f, 1.0f);
// gl_Position = ...
// ...
}
gl_Vertex is in [-0.5, 0.5]^2, on the XZ-plane. So what I am basically trying to do, is to
first create a float vec2 from the ivec2 heightmapSize, which holds the width and height of the heightmap sampler.
Then I'm converting the vertex coordinates to the interval [0, 1]^2.
Then I'm calculating the offset in texture coordinates by multiplying the vertex position with the heightmap size. The left part (using floor()) should return the number of texels in each direction.
Example: The texure is of size 4x4, position is (0.55, 0.5). This means I would got 2 texels to the right, and two texels upwards -> (2.0, 2.0).
In the right part, I add another (0.5, 0.5) because I want the center of the texel. The (2.0, 2.0) becomes (2.5, 2.5). Note: whatever the coordinates are, the fractional part should be 0.5 in the end.
Now comes the strange part. I'm testing for the result by specifying two colors. If the fractional part of the offset is "close" to 0.5, I'm setting the resulting color to green, otherwise red. The image is almost all red.
How is it possible, that either fract() does not result in a vector of two integer values (or close to integer because of float accuracy), or the adding of vec2(0.5, 0.5) has no effect? Am I missing something else?

OpenGL point sprites rotation in fragment shader

I'm following this tutorial to learn something more about OpenGL and in particular point sprites. But I'm stuck on one of the exercises at the end of the page:
Try to rotate the point sprites 45 degrees by changing the fragment shader.
There are no hints about this sort of thing in the chapter, nor in the previous ones. And I didn't find any documentation on how to do it. These are my vertex and fragment shaders:
Vertex Shader
#version 140
attribute vec2 coord2d;
varying vec4 f_color;
uniform float offset_x;
uniform float scale_x;
uniform float point_size;
void main(void) {
gl_Position = vec4((coord2d.x + offset_x) * scale_x, coord2d.y, 0.0, 1.0);
f_color = vec4(coord2d.xy / 2.0 + 0.5, 1.0, 1.0);
gl_PointSize = point_size;
}
Fragment Shader
#version 140
varying vec4 f_color;
uniform sampler2D texture;
void main(void) {
gl_FragColor = texture2D(texture, gl_PointCoord) * f_color;
}
I thought about using a 2x2 matrix in the FS to rotate the gl_PointCoord, but I have no idea how to fill the matrix to accomplish it. Should I pass it directly to the FS as a uniform?
The traditional method is to pass a matrix to the shader, whether vertex or fragment. If you don't know how to fill in a rotation matrix, Google and Wikipedia can help.
The main thing is that you're going to run into is the simple fact that a 2D rotation is not enough. gl_PointCoord goes from [0, 1]. A pure rotation matrix rotates around the origin, which is the bottom-left in point-coord space. So you need more than a pure rotation matrix.
You need a 3x3 matrix, which has part rotation and part translation. This matrix should be generated as follows (using GLM for math stuff):
glm::mat4 currMat(1.0f);
currMat = glm::translate(currMat, glm::vec3(0.5f, 0.5f, 0.0f));
currMat = glm::rotate(currMat, angle, glm::vec3(0.0f, 0.0f, 1.0f));
currMat = glm::translate(currMat, glm::vec3(-0.5f, -0.5f, 0.0f));
You then pass currMat to the shader as a 4x4 matrix. Your shader does this:
vec2 texCoord = (rotMatrix * vec4(gl_PointCoord, 0, 1)).xy
gl_FragColor = texture2D(texture, texCoord) * f_color;
I'll leave it as an exercise for you as to how to move the translation from the fourth column into the third, and how to pass it as a 3x3 matrix. Of course, in that case, you'll do vec3(gl_PointCoord, 1) for the matrix multiply.
I was stuck in the same problem too, but I found a tutorial that explain how to perform a 2d texture rotation in the same fragment shader with only with passing the rotate value (vRotation).
#version 130
uniform sampler2D tex;
varying float vRotation;
void main(void)
{
float mid = 0.5;
vec2 rotated = vec2(cos(vRotation) * (gl_PointCoord.x - mid) + sin(vRotation) * (gl_PointCoord.y - mid) + mid,
cos(vRotation) * (gl_PointCoord.y - mid) - sin(vRotation) * (gl_PointCoord.x - mid) + mid);
vec4 rotatedTexture=texture2D(tex, rotated);
gl_FragColor = gl_Color * rotatedTexture;
}
Maybe this method is slow but is only to prove/show that you have an alternative to perform a texture 2D rotation inside fragment shader instead of passing a Matrix.
Note: vRotation should be in Radians.
Cheers,
You're right - a 2x2 rotation matrix will do what you want.
This page: http://www.cg.info.hiroshima-cu.ac.jp/~miyazaki/knowledge/teche31.html shows how to compute the elements. Note that you will be rotating the texture coordinates, not the vertex positions - the result will probably not be what you're expecting - it will rotate around the 0,0 texture coordinate, for example.
You may alse need to multiply the point_size by 2 and shrink the gl_PointCoord by 2 to ensure the whole texture fits into the point sprite when it's rotated. But do that as a second change. Note that a straight scale of texture coordinates move them towards the texture coordinate origin, not the middle of the sprite.
If you use a higher dimension matrix (3x3) then you will be able to combine the offset, scale and rotation into one operation.