Why can't texelFetch fetches the complete texture?

Hello everyone, this is the first time I have been here.

Just gotten into OpenGL around half a month, and it is definitely difficult on understanding some of the concepts, and definitely got lost in the buffer part, but I know learning OpenGL takes a lot time before I can do something useful while I want to create something with it, so I accept the challenge.

I am currently learning it using OpenGL SuperBible 7th edition, trying to port the C++ code into zig, but I have stuck at the “Simple Texture” part, not because I can’t reproduce the texture using a different language, but I don’t understand the texelFetch() function in the fragment shader, where I don’t seem to see even the official example use this function to draw the texture although the book say so.

I have followed a GitHub example, using texture() in the fragment shader:

#version 450 core
uniform sampler2D s;
out vec4 color;

void main(void)
{
    color = color = texture(s, gl_FragCoord.xy / textureSize(s, 0));
}

Everything works as intended where the program generated, grid like texture is properly filled (left image). However, when I followed the book and replaced the texture function as the following:

color = texelFetch(s, ivec2(gl_FragCoord.xy), 0);

Considering the resolution and color of the texture, seems that only the texture at the bottom left are generated, but beyond a certain point, the function stops drawing the texture (right image):

I heard about the difference between texture and texelFetch is that the coordination is normalized for texelFetch(), not for the texture(), but why is that causing texelFetch() drawing part of the texture? Or did I misunderstood these two functions?

The use of normalised coordinates isn’t the only difference.

texelFetch is a very low-level function. It retrieves a specific texel from a specific level of a texture. That is all.

OTOH, texture is much higher-level; it performs filtering and wrapping according to either the current sampler object or the texture’s built-in state (see glTexParameter or glSamplerParameter). Given your results, the wrap modes are presumably set to GL_REPEAT.

In your example, it appears that the texture is much smaller than the window. This results in gl_FragCoord being larger than the texture size, meaning that the coordinates passed to texture are greater than 1.0 (resulting in wrapping) and the coordinates passed to texelFetch are greater than the size of the texture (resulting in undefined behaviour).

tl;dr: texture does wrapping, texelFetch doesn’t.

Thanks for the explaining, and now it makes sense because when I change the wrapping attribute to MIRRORED_REPEAT, the texture is now mirrored if I use the texture() function, suggesting that texture has wrapped as you have described; thus, texelFetch did rendered the whole texture, just not tiled.

Based on what you have mentioned, I attempted to manually wrapping the texture by performing a modulo, cycling from 0 and the size of the texture, and seems this method does work:

color = texelFetch(s, ivec2(gl_FragCoord.xy) % textureSize(s, 0), 0);

Should I manually so this? Or if this is the case, I should stick with texture() and let the function warps things for me?

Well, why do you want to use texelFetch? It’s a function with a specialized purpose. If you need that purpose, then you should use it. If you don’t, you should use something else.

Well, the answer is really: I don’t know

While I understand all functions are made by a specific purpose, and this is true for any frameworks or libraries, I am really not sure why I should use this function over the texelFetch while the book just teach the audience using that without really explaining why it is preferred over the texture() function. (In fact, in the start of the texture section, they don’t even mentioned the “texture()” function at all.)

I could take a search on the internet, but most of the time, the answer is often “Normalized vs not Normalized”, so I would be appreciated if you could explain when I should use texelFetch over the higher level texture.

Well, that is like 90% of the answer. The deeper question is “what are you doing such that your texture coordinates are not normalized?”

The purpose of normalized texture coordinates is to abstract away the particular nature of the texture. You’re not asking for a specific texel’s color value; you’re asking what the color would be at this particular position in the virtual space of the texture. That sounds like the same question, but it’s not.

texture doesn’t deal with texels; it fetches color data within an abstract space. Your texture may be 512x512, 1024x512, 934x845, texture does not care. If your texture coordinates are (0.5, 0.5), you’re getting the color value from the “middle” of the texture. If your texture coordinates are (1.25, -4.15), this is not a nonsensical value; it’s a location within the virtual space of your texture, and texture wrapping options will explain how that gets converted into a texel value.

Then there’s bilinear filtering, mipmap filtering, anisotropic filtering and other mechanics that get applied when deciding what the data is within this abstract space.

texelFetch is for doing a memory access of an array (of possibly multiple dimensions).

So, are you sampling from a texture or just accessing memory?

For real, I probably don’t because I got confused by the book and what you have mentioned as the following which is very clear:

All I need to do is to learn about how to apply textures on a triangle and nothing else, so using normalized texture make sense so that I can apply the texture using a relative position, and beyond that, the function handles the wrapping.

If I am not mistaken, seems like texelFetch is designed for fetching a specific texel value so that I can modify the specific point of the texel at the shader level, but since my triangle shader doesn’t really need to process texel like that, nor I need to extract things like a game sprite which has a precise location, texelFetch is not necessary.

I think I have some idea now, but now sure if that is right.