0 votes

I have a shader that makes some really nice looking grass, but when I put things ontop of it, the grass sticks through. So far, I've found I can modify the entire grass mess with VERTEX.y after some fiddling with the shader, but I've not found a way to hide unwanted things. I'm open to doing this without using the shader, that just seemed like the way to go.

shader_type spatial;
render_mode cull_disabled;

uniform vec4 color_top:hint_color = vec4(1,1,1,1);
uniform vec4 color_bottom:hint_color = vec4(0,0,0,1);

uniform float deg_sway_pitch = 80.0;
uniform float deg_sway_yaw = 45.0;

uniform float wind_scale = 4.0;
uniform float wind_speed = 1.0;

varying float wind;

const vec3 UP = vec3(0,1,0);
const vec3 RIGHT = vec3(1,0,0);

const float PI = 3.14159;
const float DEG2RAD = (PI / 180.0);

uniform vec3 wind_direction = vec3(0,0,-1);

uniform vec3 character_position = vec3(0.0, 0.0, 0.0);
uniform vec3 position;
uniform float character_radius = 3.0;
uniform sampler2D character_distance_falloff_curve : hint_black_albedo;
uniform float character_push_strength = 1.0;

varying float debug_wind;

mat3 mat3_from_axis_angle(float angle, vec3 axis) {
    float s = sin(angle);
    float c = cos(angle);
    float t = 1.0 - c;
    float x = axis.x;
    float y = axis.y;
    float z = axis.z;
    return mat3(
        vec3(t*x*x+c,t*x*y-s*z,t*x*z+s*y),
        vec3(t*x*y+s*z,t*y*y+c,t*y*z-s*x),
        vec3(t*x*z-s*y,t*y*z+s*z,t*z*z+c)
    );
}

vec2 random2(vec2 p) {
    return fract(sin(vec2(
        dot(p, vec2(127.32, 231.4)),
        dot(p, vec2(12.3, 146.3))
    )) * 231.23);
}

float worley2(vec2 p) {
    float dist = 1.0;
    vec2 i_p = floor(p);
    vec2 f_p = fract(p);
    for(int y=-1;y<=1;y++) {
        for(int x=-1;x<=1;x++) {
            vec2 n = vec2(float(x), float(y));
            vec2 diff = n + random2(i_p + n) - f_p;
            dist = min(dist, length(diff));
        }
    }
    return dist;
}

void vertex() {
    NORMAL = UP;
    vec3 vertex = VERTEX;
    vec3 wind_direction_normalized = normalize(wind_direction);
    float time = TIME * wind_speed;
    vec2 uv = (WORLD_MATRIX * vec4(vertex,-1.0)).xz * wind_scale;
    uv += wind_direction_normalized.xz * time;
    wind = pow(worley2(uv),2.0) * UV2.y;

    mat3 to_model = inverse(mat3(WORLD_MATRIX));
    vec3 wind_forward = to_model * wind_direction_normalized;
    vec3 wind_right = normalize(cross(wind_forward, UP));

    float sway_pitch = ((deg_sway_pitch * DEG2RAD) * wind) + INSTANCE_CUSTOM.z;
    float sway_yaw = ((deg_sway_yaw * DEG2RAD) * sin(time) * wind) + INSTANCE_CUSTOM.w;


    mat3 rot_right = mat3_from_axis_angle(sway_pitch, wind_right);
    mat3 rot_forward = mat3_from_axis_angle(sway_yaw, wind_forward);


    float displacement_affect = (1.0 - UV.y);



    // At the moment the blades are pushed away in a perfectly circular manner.
    // We could distort the distance to the character based on a noise, to break a bit the
    // circular shape. We could distort the falloff by sampling in a noise based on the xz coordinates.
    // The task is left to the reader

    vec3 dir_to_character = character_position - WORLD_MATRIX[3].xyz;
    // uncomment the following line to have a horizontal only character push
    dir_to_character.y = 0.0;
    float distance_to_character = length(dir_to_character);
    float falloff = 1.0 - smoothstep(0.0, 1.0, distance_to_character/character_radius);
    // Because we operate in vertex space, we need to convert the direction to the character
    // in vertex space. Otherwise, it wouldn't work for rotated blades of grass.
    // comment the next line to observe how the blades are not all facing away from the character.
    dir_to_character = (inverse(WORLD_MATRIX) * vec4(dir_to_character, 0.0)).xyz;
    dir_to_character = normalize(dir_to_character);

    // sample the curve based on how far we are from the character, in normalized coordinates
    float falloff_curve = texture(character_distance_falloff_curve, vec2(falloff)).x;
    // direction to character is inverted because we want to point away from it

    vertex.xz *= INSTANCE_CUSTOM.x;
    vertex.y *= INSTANCE_CUSTOM.y;

    VERTEX = rot_right * rot_forward * vertex;



    VERTEX += normalize(-dir_to_character) * falloff_curve * character_push_strength * displacement_affect;


    COLOR = mix(color_bottom, color_top, UV2.y);
    //0iCOLOR = textureLod(SCREEN_TEXTURE, SCREEN_UV, blur_amount); 
    //VERTEX.y -= position.y;
}

void fragment() {
    float side = FRONT_FACING ? 1.0 : -1.0;
    NORMAL = NORMAL * side;

    ALBEDO = mix(color_bottom, color_top, clamp(1.0 - (wind * 0.5), 0.0, 1.0)).rgb;
    //ALBEDO = COLOR.rgb;
    SPECULAR = 0.5;
    ROUGHNESS = clamp(1.0 - (wind * 0.75), 0.0, 1.0);
    //ALPHA = clamp(sign(dot(surfaceNormal, world_vert - generatePoint)), 0.0, 1.0);

    //float noiseValue = (1.0 - (texture(noiseTexture, UV).r * texture(screenFadeTexture, SCREEN_UV).r )) - adjustedFade;
    //ALPHA = round(adjustedFade);
    //EMISSION = round(adjustedFade) * color.rgb;
}
Godot version v3.3.3 stable
in Engine by (26 points)

1 Answer

0 votes

Yeah, if you don't need collisions then shaders are the way to go.

There are different methods depending on what you're looking for.

1, just add another uniform with the location of the object that's falling onto the grass. Now VERTEX is local so you'll need to take the xform of the grass' basis from the colliding obj's global origin. Or you could use its aabb bounding box for extra accuracy.

Then you can literally just get the distance from the VERTEX to the centre of the obj / edges of the obj and perform the desired behaviour.

2, you can use textures with another viewport as detailed here:

https://www.youtube.com/watch?v=BXo97H55EhA

by (2,152 points)

That youtube video looks really promising, thank you! I've tried passing in the object location the same way I'm using to make the player affect the grass VERTEX.y += normalize(-dir_to_character).y * falloff_curve * character_push_strength * displacement_affect;. But that affects the entire area. Could you expand a little bit on the first method?

Edit: I tried following the youtube video you posted, and I got a sprite to draw on screeen with a TextureRect. But I can't seem to get that texture to affect the grass. I've got something like this (All in the vertex shader)

uniform sampler2D dynamic_dirt_mask;

float grass_mask = COLOR.r;
grass_mask *= texture(dynamic_dirt_mask, UV).r * COLOR.r;
Welcome to Godot Engine Q&A, where you can ask questions and receive answers from other members of the community.

Please make sure to read Frequently asked questions and How to use this Q&A? before posting your first questions.
Social login is currently unavailable. If you've previously logged in with a Facebook or GitHub account, use the I forgot my password link in the login box to set a password for your account. If you still can't access your account, send an email to [email protected] with your username.