Jump to content
  • Advertisement
Sign in to follow this  
BrightBit

frac(x) results in strange artifacts when x is close to zero

This topic is 1483 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

I am using a cg shader to create a white grid on my terrain. The terrain iteself consists of tiles where each tile is 4x4 units big. I'm using this fact to calculate locale UV coordinates for each tile to draw the content of a grid texture. This grid texture's size is only 128x128 pixels. It's border consists of white pixels and the middle part is transparent.

The calculation of those local uv coordinates is done by using frac(x). However, it results in artifacts on the transition to neighbouring tiles. Disabling the generation of MipMaps for the grid texture makes those artifacts disappear. I still don't understand why those MipMaps are causing this.

Do you know why? Can I avoid this somehow?


kxfnsreh.png

 

Shader "Custom/World"
{
    Properties
    {
        _Color ("Main Color", Color)            = (0.5,0.5,0.5,1)
        _MainTex ("Base (RGB) Trans (A)", 2D)   = "white" {}
        _Grid ("Base (RGA) Trans (A)", 2D)      = "white" {}
        _Cutoff ("Alpha cutoff", Range(0,1))    = 0.5
    }
 
    SubShader
    {
        Tags
        {
            "Queue"           = "AlphaTest"
            "IgnoreProjector" = "True"
            "RenderType"      = "TransparentCutout"
        }
        LOD 200
       
        CGPROGRAM
        #pragma surface surf Lambert alphatest:_Cutoff
 
        sampler2D _MainTex;
        sampler2D _Grid;
        fixed4 _Color;
 
        struct Input
        {
            float2 uv_MainTex;
            float3 worldNormal;
            float3 worldPos;
        };
 
        float2 getGridUV(float3 worldPos)
        {
            float u = frac(worldPos.x / 4.0); // a tile is 4 units wide
            float v = frac(worldPos.z / 4.0); // and also 4 units deep
 
            return float2(u, v);
        }
 
        void surf(Input IN, inout SurfaceOutput o)
        {
            fixed4 c    = tex2D(_MainTex, IN.uv_MainTex) * _Color;
            float alpha = tex2D(_Grid, getGridUV(IN.worldPos)).a;
 
            o.Albedo    = c.rgb * (1.0 - alpha) + (1,1,1,1) * alpha;
            o.Alpha     = c.a;
        }
 
        ENDCG
    }
}

Edit: The title isn't really correct but I can't change it anymore. Sorry.

Edited by BrightBit

Share this post


Link to post
Share on other sites
Advertisement

You'll have to point out what exactly is wrong with the image you posted. Or at least post an image that looks correct, so we can spot the differences. I'm not sure what I'm looking at.

 

Also, the title of the post claims frac is the cause of the problem, but the content of your post indicates that it's mipmaps that are the thing that determines whether the artifact appears or not. Your use of frac looks correct to me, so I would guess your problem is that your mipmaps don't look the way you think they do.

 

How are you generating mips? Do you ever look at your mips to make sure they're correct? Is it possible your mips have some kind of corruption near the border?

Edited by Samith

Share this post


Link to post
Share on other sites

Thank you Erik. Your suggestion did the trick and someone submitted this link to me that seems to fit to your assumption about the mip mapping behaviour of the gpu.

Share this post


Link to post
Share on other sites

Fwiw, you should never avoid using mips without good reason. Mips are there largely to avoid thrashing the cache, and therefore improve performance. You can avoid the problem Erik described by supplying your own derivatives into the tex2d call that are calculated prior to the frac() call. This lets the hardware use the mips that it would have had the frac() call not been there. Something along the lines of this:

float2 uv = worldPos.xz / 4; // <- swizzle elements however you need, instead of doing your math seperately on x and z!
float alpha = tex2Dgrad(_Grid, frac(uv), ddx(uv), ddy(uv)).a;

Also, its perfectly acceptible to use texture coordinates that are outside of 0 to 1. You can assign sampler addressing modes to let the hardware automatically repeat the texture, or clamp it, in either dimension. That avoids the need for the tex2Dgrad & frac completely.

EDIT: sorry, i missed the part where Erik already pointed out the usages of the wrap mode. I'll leave my comment about tex2Dgrad though, as its handy for similar situations that cant be fixed via repeat (such as when tiling within 0 to 1)

Edited by Digitalfragment

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!