- Home /
"Floor" function produces artifacts in shader
Hello everyone, here is the output of the simple shader
tex2D( myTex , floor(i.uv*3)/3);
I need to do this to get a coherent noise from sampling an image.
How can I avoid the flickering artifacts that appear? They appear in the middle, at points where i.uv.x/3=1, 2, 3 and i.uv.y/3=1, 2, 3
Answer by Nicate · Jul 31, 2018 at 08:27 AM
I know this question is old, but for those with a similar problem frantically googling for whatever could be causing this (like me):
This is caused by discontinuities in the texture coordinates. The GPU shades 4 fragments at the same time and uses a derived gradient (slope) in texture coordinate space (taken over screen space) between these fragments to determine which mip map to select. So, use of floor(), round(), ceil(), or if statements to sample from a texture at a wildly different location will create this effect.
See also https://www.gamedev.net/forums/topic/587967-trying-to-render-fonts-unwanted-artifacts-at-edges-of-texture-samples/ and https://forum.unity.com/threads/manual-mipmapping-in-the-shader.130874/
The line itself actually consists of the highly blurred pixels from what is probably the lowest quality mip map it can find.
To solve this you can do one of the following: 1. Disable the generation of mip maps in the Unity material. This of course will cause the material to start looking really noisy as the distance to the camera increases. 2. Sample the same mip map for every fragment (for example, mip map 0) using tex2Dlod(). This has the same noisy effect, but can be useful if your texture is more of a data texture. 3. Manually calculate the distance to the camera and use tex2Dlod() to select a mip map directly. As the GPU normally does somewhat more advanced stuff to select a mip map, this will decrease the quality of the render or at least make it look different. 4. Provide the GPU with a replacement gradient using tex2Dgrad() so it can select the correct mip map by itself. This means you have to figure out what the partial derivatives for texture coordinate space over screen space are at the location of your fragment.
Option 4 generally exists so you can use an analytical derivative (as opposed to the numerical rate of change calculated by the GPU by comparing neighbouring fragments) to increase the quality of the mip mapping selection.
But you can also use option 4 by having the GPU calculate the gradient for another variable that does not suffer from discontinuities, and then performing the calculations you would use to arrive at a texture coordinate on the gradient as well to produce a gradient with the same properties as the texture coordinate gradient but without the discontinuities.
In my case, I was able to do this by applying ddx() and ddy() (these provide the numerical derivatives for the provided value, over screen space) to the fragment position in object space, and then transforming the resulting gradient into the texture coordinate space in the same way I would transform the position into the texture coordinate space. This removed the artifacts but kept the mip mapping on the correct parts exactly the same.
So for the original question, if it concerns a data texture one might want to use tex2Dlod() with a mip map level of 0. If it concerns a rendered texture, one might try something like:
tex2Dgrad(myTex , floor(i.uv * 3) / 3, ddx(i.uv), ddy(i.uv));