Search Unity

generate random float between 0 and 1 in shader?

Discussion in 'Shaders' started by zhutianlun810, Jan 11, 2019.

  1. zhutianlun810

    zhutianlun810

    Joined:
    Sep 17, 2017
    Posts:
    172
    Hi,

    Is there any way of constructing a high quality random number generator in a surface shader? I've implemented one but I feel it is not very random...

    Thanks
     
  2. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,352
  3. Przemyslaw_Zaworski

    Przemyslaw_Zaworski

    Joined:
    Jun 9, 2017
    Posts:
    328
    Code (CSharp):
    1.             float random (float2 uv)
    2.             {
    3.                 return frac(sin(dot(uv,float2(12.9898,78.233)))*43758.5453123);
    4.             }
    5.  
    Example usage:
    float k = random(IN.uv_MainTex);
     
    Cactus_on_Fire likes this.
  4. jvo3dc

    jvo3dc

    Joined:
    Oct 11, 2013
    Posts:
    1,520
    Whenever possible I try to exclude the sin to reduce computation. The dot and frac have to stay in, but are relatively light compared to the sin.
     
  5. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,352
    The noise function posted above is a super common example that you'll find in plenty of shaders or there in the wild, and threads on the internet.

    It's also pretty darn terrible both in quality and in performance (vs other similar quality noise functions). The link I posted of a ShaderToy search has a ton of different example noise, or hash functions, often with discussions in the comments about implementations and their various pros and cons. Most are using some form of simple trigonometry free (no sin or cos) hash with multiple octaves which end up being similar in perf to the example above, but significantly higher quality.

    Depending on your needs, any of them might work well enough, even with out multiple octaves. However it's fairly well accepted that the best quality noise you can get is some form of blue noise, and for that you will need to use a texture as it's much too expensive to calculate on the fly. Unity's own post processing shaders use blue noise textures now almost exclusively.

    Here's a site with a generator, as well as links to a ton of pregenerated blue noise textures of various sizes and formats:
    http://momentsingraphics.de/?p=127

    There's also perlin and simplex noise and the like, which can be based off of various hash functions.

    Though there are a handful of examples of real-time psuedo blue noise on ShaderToy as well you can look at if you search. Mostly forms of grid limited voronoi.

    Then there are the gradient noise and fixed pattern bayer or Halton series "noise" options. These are common for dithering and psuedo random stuff.

    Really depends on what you're going for.
     
    grobonom, lang_fox, mantisjoe and 4 others like this.
  6. zhutianlun810

    zhutianlun810

    Joined:
    Sep 17, 2017
    Posts:
    172
    Thank you very much! I see many of the implementation use the fragCoord as the seed. How can I get it in Unity's surface shader?
     
  7. zhutianlun810

    zhutianlun810

    Joined:
    Sep 17, 2017
    Posts:
    172
    This is like my old implemantation. Do you notice that there are some weird patterns in the final results?
     

    Attached Files:

    Last edited: Jan 11, 2019
  8. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,352
    The fragcoord value is OpenGL's gl_Fragcoord. That's the screen pixel position for each fragment. You'll see a lot of those shaders divide that value by the screen resolution to get a 0.0 to 1.0 range more similar to what you would get from UVs.

    HLSL's equivalent is VPOS, which isn't available in surface shaders, but you can use the float4 screenPos; variable in the Input struct to get the normalized 0.0 to 1.0 range screen UV.

    Code (csharp):
    1. Input {
    2.     // stuff
    3.     float4 screenPos;
    4. }
    5.  
    6. // in surf function
    7. float2 screenUV = IN.screenPos.xy / IN.screenPos.w;
    8. float2 screenCoord = screenUV * _ScreenParams.xy; // results should be identical to the ShaderToy fragcoord
    So those artifacts don't appear to be from the noise function really. I mean, a better noise function will indeed not look as bad and the artifacts are likely there in large part because of how poor the usual sine based noise is, but to me that looks like a moire pattern caused by aliasing. I'm going to guess you're using the uv_MainTex to drive a noise function?

    Using screenPos should remove that kind of artifact, even if you're using the same noise function you already are. If you're doing noise using a mesh's UVs you'll likely want to use something like perlin or simplex noise instead ... or a noise texture. You might also try scaling the UVs by some giant number and see if that helps at all.
     
  9. dkaloger

    dkaloger

    Joined:
    Jul 7, 2019
    Posts:
    39
    is there any 3d version of this?
     
  10. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,352
    Of course.
    https://www.shadertoy.com/view/4djSRW
    The Common tab has a ton of very effective hash functions that take 1,2,3 or 4 inputs and return 1,2,3 or 4 outputs. To convert them from GLSL to HLSL replace all occurrences of
    vec
    with
    float
    , and all
    fract
    with
    frac
    . Though it should be noted that implementation assumes you’re passing in integer values, so you may need to multiply the inputs by some arbitrary large value.

    And that’s just one example. There are other examples on ShaderToy, and libraries on GitHub of even more implementations and types of noise for all sorts of dimensions.
     
    grobonom, maurosso and dkaloger like this.
  11. maurosso

    maurosso

    Joined:
    Feb 19, 2017
    Posts:
    50
    Hmm, how would one go about such a case:

    I'd like to use a random number on a per-material basis in my shader. So I'd like it to be "constant" throughout the fragment stage.

    I have a range from which I'd like to choose, so using something like the Unity supplied code:

    float randomno = frac(sin(dot(Seed, float2(12.9898, 78.233))) * 43758.5453);
    randomno = floor(lerp(1, In.InRow+1, randomno));

    would work fine for me, but how could I set the Seed value to be random (like time.time), but constant for a particular material?
    Is my only option to expose is a property and set it externally and pass it through? It's part of a procedural rendering pipeline, so I don't have easy access to generated materials and would prefer to avoid it.
     
  12. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,352
    If the object doesn't move, and isn't batched, you might be able to use the world space pivot position as a seed.

    Or you might need to bake a random value into the mesh's vertex color or extra UV channel. Or assign it via a material property block. Or yes, directly on the material. Either way it'll need to be done from c#.
     
    maurosso likes this.
  13. maurosso

    maurosso

    Joined:
    Feb 19, 2017
    Posts:
    50
    I like the random number baking in a UV stream approach during mesh creation. That will work well. Thank you :)