Looking for some shader trick to add some grass variety

GeffreyGeffrey Posts: 24Member

I'm using a tileable texture for the grass texture. I'm using a simple visual shader that looks like this:

I think one method of adding some randomization would have been to randomly rotate the texture (or UV?) 0, 90, 180 degrees etc. But, is there any way to do it through visual shader.

I have not fully mastered code based shader yet, but I guess I could try coding if there is no rand function in visualshader


  • MegalomaniakMegalomaniak Posts: 1,386Admin

    I believe what you are looking to do is something along the lines of this:

  • GeffreyGeffrey Posts: 24Member

    What can I use in place of the hash4? Godot seems to not have that function.

  • TwistedTwiglegTwistedTwigleg Posts: 1,408Admin

    Looking at the article @Megalomaniak linked, it appears the hash4 function is a custom function. Initially I thought it was something OpenGL specific, but in the ShaderToy shader linked in the article, it turns out that hash4 is just a function created in the shader:

    vec4 hash4( vec2 p )
        return fract(sin(vec4(

    This should be doable in Godot's shader language.

    I'm not totally sure if it is possible through the visual shader though.

    In Godot 3.2 there will be a way to add a expression node to the visual shader editor, which you could use to define the hash4 function.

  • MegalomaniakMegalomaniak Posts: 1,386Admin

    think of each thing there as a node:

    fract takes a single input, and it's output is the final return.

    This should already hint to you that visual/flowgraphs in comparison to written code are represented in reverse, btw. So order of operations as you read them is opposite, but for the computer they will be the same, of course.

    Next we see a sinewave, but we need to also account for the end of brackets. So the next node working backwards(from output toward input) would be a math multiplication, scalar to be exact. Note that the output of sine is multiplied by 103.0 there.

    So next we look at what is input into the sinewave function? It's a vec4 with each of it's components holding the result of another math operation, this time an addition. So on and so forth. You just deconstruct that hash4 and reconstruct it as a visual flowgraph of the logic. That's basically it.

  • GeffreyGeffrey Posts: 24Member

    @TwistedTwigleg Oh, I see. Thanks. Makes sense.

  • GeffreyGeffrey Posts: 24Member
    edited August 16

    Sorry for late response, I had to lower this in my to-dos. I finally got it to work. But there is still some repetition because each ground tile in the gridmap has the same texture, but it's less obvious because there is no obvious repetition WITHIN a tile. Especially there are some weird artifacts as the edges of the grid tiles :(

    Maybe I didn't do it correctly? Am I supposed to use Fragcoord?

    Here is the code I used:

    shader_type spatial;
    vec4 hash4(vec2 p){
        return fract ( sin(vec4( 1.0+dot(p,vec2(37.0,17.0)), 2.0+dot(p,vec2(11.0,47.0)), 3.0+dot(p,vec2(41.0,29.0)), 4.0+dot(p,vec2(23.0,31.0)) ) ) *103.0);
    vec4 textureNoTile (sampler2D samp, vec2 uv)
        vec2 iuv = floor(uv);
        vec2 fuv = fract(uv);
        vec4 ofa = hash4( iuv + vec2(0,0) );
        vec4 ofb = hash4( iuv + vec2(0,0) );
        vec4 ofc = hash4( iuv + vec2(0,0) );
        vec4 ofd = hash4( iuv + vec2(0,0) );
        vec2 ddx = dFdx(uv);
        vec2 ddy = dFdy(uv);
        ofa.zw = sign(ofa.zw -0.5 );
        ofb.zw = sign(ofb.zw -0.5 );
        ofc.zw = sign(ofc.zw -0.5 );
        ofd.zw = sign(ofd.zw -0.5 );
        vec2 uva = uv*ofa.zw + ofa.xy, ddxa = ddx*ofa.zw, ddya = ddy*ofa.zw;
        vec2 uvb = uv*ofb.zw + ofb.xy, ddxb = ddx*ofb.zw, ddyb = ddy*ofb.zw;
        vec2 uvc = uv*ofc.zw + ofc.xy, ddxc = ddx*ofc.zw, ddyc = ddy*ofc.zw;
        vec2 uvd = uv*ofd.zw + ofd.xy, ddxd = ddx*ofd.zw, ddyd = ddy*ofd.zw;
        vec2 b = smoothstep (0.25, 0.75, fuv);
        return mix(mix (textureGrad(samp, uva, ddxa, ddya), textureGrad(samp, uvb, ddxb, ddyb), b.x), mix(textureGrad( samp, uvc, ddxc, ddyc ), textureGrad( samp, uvd, ddxd, ddyd ), b.x), b.y);
    uniform sampler2D grass_tex;
    uniform vec4 col_mult: hint_color;
    void fragment() {
        vec4 texture_modified = textureNoTile(grass_tex, vec2(UV.x *3.0, UV.y * 3.0));
        //vec3 col = col_mult.rgb * texture_modified.rgb;
        ALBEDO = texture_modified.rgb;

    Another question what is the equivalent of VectorOP>Multiply from the visual shader in written shader? Just doing color.rgb * texture.rgb doesn't seem to do the same thing as the resulting color is different from visualshader's vectorOP>Multiply

Sign In or Register to comment.