Hi,
I'm working on an algorithm where I need to detect if the current `gl_FragCoord` is an even or odd row/column. A typical implementation would look something like `if (mod(gl_FragCoord.x, 2.0) < 1.0) { ... } else { ... }`. After running into issues with this approach a quick google search pointed me to quite some good information:
Especially this forum post is pretty much identical to the issue I'm looking into.
I've created this repository to experiment with this issue and to solve it. I'm using a MiBox MDZ-16-AB. This repository is created for Android Studio. It will setup a basic custom `GLSurfaceView` that uses the `TestGlView` that instantiates the `TestGlRenderer` class. In `TestGlRenderer` I create a simple filter shader that applies the `if (mod(..))` logic to draw different colors for odd/even columns. I create a FBO with a size of 3840 x 2160 to detect floating point precision issues. When rendering into a FBO with a color attachment of a texture with a size of 1920 x 1080 the issues is a lot less.
In the image below (3840 x 2160) you can clearly see the issue. It should show vertical red and black lines from left to right.
When rendering using a 1920 x 1080 things get a little bit better, but still not 100% correct.
In `TestGlRenderer` I create an instance of `GlRenderToTexture` which is just a thin wrapper that create a FBO with one texture attachment. In `onSurfaceCreated()` of the `TestGlRenderer` class I create an instance of `GlRenderToTexture`. In the code I've added (a commented) version which creates either a 1920x1080 or 3840x2160 FBO.
Now I'm curious what would be a workaround or solution to be able to distinguish between odd and even rows/columns?
Thanks
I'm experimenting with this using the public test repository I created but the results I get are the same. At L23 I set the `highp` precision. and at L42 I fetch the pixels from the lookup table. The width of the lookup table is the same as the viewport/fbo into which I render (3840x2160).
This is the result when I render into 1920 x 1080, which looks correct:
But when I render into a 3840 x 2160 and then render that to the default framebuffer, I get something similar to the above issue:
It still seems like a precision issue; but if the texture lookups are done using a highp variant.
I assume you are downsampling from 2160p (offscreen) to 1080p (onscreen), so the downsample isn't going to be stable with GL_NEAREST filtering (you have twice as many texels as you have pixels, so the choice of black or white is going to be a bit unstable).
To verify that the 2160p render is doing the right thing I'd suggest a glReadPixels call just to snapshot the memory - it removes one moving part from the sequence.
Ah offcourse. Totally makes sense. The reason I was using GL_NEAREST was that GL_LINEAR would give me a grayish output. But you are totally right, I should use glReadPixels to download the result. Thanks and sorry for this silly mistake.
Great, I can confirm that using a lookup texture works.
I've got another related question: let's say that I have a fragment shader that I use when rendering into a texture 3840x2160 with an FBO. In this shader, I also want to sample from another texture of a size 1920x1080. But, I want to fetch the pixels around the current fragcoord taking into account the scaling.
So let's say that gl_FragCoord is 1920x1080 (so half of the FBO attachment texture), this means that my texture coordinate that has a value of (0.5, 0.5). When I want to sample the corresponding pixel from my smaller texture (the one of 1920x1080) I can simply use that texture coordinate. Using this would fetch me the pixel at (1920 * 0.5, 1080 * 0.5) => (960 x 540). But what if I want to fetch one pixel to the left and one pixel to the right of this one. So (959 x 540) and (961 x 540). I'm asking because I suspect that using arithmic in the fragment shader won't allow me to do this because of the low precision. Am I right?
If you try to do this with arithmetic in the shader code, then you are correct. As soon as you do any arithmetic on a variable you'll drop to fp16 precision. As a workaround you could simply create multiple texture coordinate varyings, each offset by one pixel's worth of space, and pass each directly into a texture call.
For a complex mesh that's going to be expensive in terms of varying memory bandwidth, but for a simple full screen quad I wouldn't expect that to be a major problem in terms of performance.
Thanks for the suggestion. Would that mean that in this case I would need 3 varyings? One for sampling the center pixel, one for the left and one for the right? And do you mean that I would have to upload these varyings as vertex attributes? I'm wondering how the varyings for the left and right lookup could be constructed as a varying.
Yes, that's the idea.
For normal texel centers your varyings are set to range from 0 to 1 across the triangle, which gives you samples at texel centers. If you know your texture is 1024 wide then you simply need to adjust the varyings to be offset by 1/1024 (subtract that to shift a sample left, and add it to shift a sample right).