Looking for some shader trick to add some grass variety

GeffreyGeffrey Posts: 28Member

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

Comments

  • MegalomaniakMegalomaniak Posts: 1,662Admin

    I believe what you are looking to do is something along the lines of this:
    http://www.iquilezles.org/www/articles/texturerepetition/texturerepetition.htm

  • GeffreyGeffrey Posts: 28Member

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

  • TwistedTwiglegTwistedTwigleg Posts: 1,709Admin

    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(
            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);
    }
    

    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,662Admin

    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: 28Member

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

  • GeffreyGeffrey Posts: 28Member
    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

  • GeffreyGeffrey Posts: 28Member

    Now that I think about it, I think messed up during the implementation of the texturenotile function as I didn't use fragcord/iresolution. I will have to play around with it some more.

  • MegalomaniakMegalomaniak Posts: 1,662Admin
    edited September 15

    To get rid of repetition over multiple tiles, you would likely want to look into some global/world-space texture mapping.

    I mean if you get rid of the tiling/pattern repetition within a tile and then tile it...

  • TwistedTwiglegTwistedTwigleg Posts: 1,709Admin
    edited September 15

    If I recall correctly, fragcoord is the position of the fragment/pixel in world space, so that is probably what you will need if you want the texture to tile in world space instead of local space.

    Though if you do not mind that the UV maps are modified, you can work around the issue by shifting the UV by the world position of the vertex. I had to reference the Triplanar Godot shader, but with the vertex modifications it should look like this:

    shader_type spatial;
    render_mode world_vertex_coords;
    
    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 vertex()
    {
        UV += (VERTEX).xy * NORMAL.z;
        UV += (VERTEX).xz * NORMAL.y;
        UV += (VERTEX).zy * vec2(-1.0, 1.0) * NORMAL.x;
    }
    
    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;
    }
    

    The tiling doesn't seem to totally work with the cube I was using for testing though.

  • GeffreyGeffrey Posts: 28Member

    Thanks so much, I'll try this When I have access to my main computer again.

  • MagicMikeMagicMike Posts: 13Member

    Hey.

    I would just blend the texture with the same or other textures scaled (UV scaled) to different sizes in the visual shader.

    If you want to do something like random dirt patches, you could have one big black and white texture that represents grass/dirt and then sample that with U/V coords and use that value to interpolate between a point on the grass texture and the corresponding point on the dirt texture.

    Anyways just some ideas.

Leave a Comment

BoldItalicStrikethroughOrdered listUnordered list
Emoji
Image
Align leftAlign centerAlign rightToggle HTML viewToggle full pageToggle lights
Drop image/file