Attempting a cleaning system shader

Godot Version

4.3

Question

`So i’m attempting a cleaning system kind of like Viscera cleanup detail/power wash sim and for the most part i understand you need to utilize shaders on an object and then get the UV position of an object and then work from there. I utilized the basic framework of another users’(evoshogames at godotshaders) work doing something similar, but he utilized a raycast system and almost a 2D perspectivefor aiming/cleaning so i attempted to fix it up to fit my needs. I’m just seeing if anyone has done somthing similar and can give me pointers on where to go/if i should be using something else instead. currently my scripts are as follows:

On the dirt cube-
`extends MeshInstance3D

var mask_image: Image
var dirt_mask: ImageTexture

func _ready():
# Create a white mask (fully dirty)
mask_image = Image.new() # This should be created with the size of your dirt mask texture
#mask_image = Image.create(dirt_mask_size, dirt_mask_size, false, Image.FORMAT_L8)
mask_image.fill(Color(1, 1, 1)) # White = Full Dirt

# Create texture from the mask
dirt_mask = ImageTexture.new()
dirt_mask = ImageTexture.create_from_image(mask_image)

#sets our dirt_mask from our shader to the dirt_mask we just made
var override_material: ShaderMaterial = get_surface_override_material(0)
override_material.set_shader_parameter(‘dirt_mask’, dirt_mask)

func clean_surface(brush_position: Vector3, brush_size: int):
# Convert world position to local texture coordinates
var local_position = brush_position

mask_image.set_pixel(brush_position.x, brush_position.z, Color(0, 0, 0))  # Set to black (clean)

# Update the dirt mask texture
dirt_mask = ImageTexture.create_from_image(mask_image)

#update the shader to use the updated mask texture
var override_material: ShaderMaterial = get_surface_override_material(0)
override_material.set_shader_parameter('dirt_mask', dirt_mask)`

on the mop-

extends Area3D

func _ready():
	connect("area_entered", _on_mop_hits_dirty_object)

func _on_mop_hits_dirty_object(area):
	var dirty_object = area.get_parent()  # Get the parent node (MeshInstance3D)
	print(dirty_object)
	if dirty_object is MeshInstance3D and dirty_object.has_method("clean_surface"):
		var collision_point = global_position  # Approximate impact position
		print(collision_point)
		dirty_object.clean_surface(collision_point, 3

shader-

shader_type spatial;

uniform sampler2D clean_texture;
uniform sampler2D dirty_texture;
uniform sampler2D dirt_mask;

// Control for the amount of dirtiness applied
uniform float dirt_strength : hint_range(0.0, 1.0) = 1.0;

void fragment() {
    // Sample colors from the clean and dirty textures
    vec4 clean_color = texture(clean_texture, UV);
    vec4 dirty_color = texture(dirty_texture, UV);

    // Sample the dirt mask (typically a grayscale texture)
    float mask_value = texture(dirt_mask, UV).r;

    // Adjust the mask by dirt_strength to control the blending
    float adjusted_mask = mask_value * dirt_strength;

    // Final blend factor considering both the mask and the dirty texture's alpha
    float blend_factor = adjusted_mask * dirty_color.a;

    // Blend the clean and dirty colors, preserving some of the clean texture's influence
    vec4 final_color = mix(clean_color, dirty_color, blend_factor);

    // For ALPHA, use the higher of clean or dirty alpha to ensure visibility
    ALBEDO = final_color.rgb;
    ALPHA = max(clean_color.a, blend_factor);
}

`
And thank you if you’ve had the patience to read all this

An alternative approach would to be to use decals. Just remove the decals as the raycast passes over them and/or replace them with “less dirty” versions.

I considered that, but unless i use hundreds of tiny decals i was hoping to have it be a little more versatile with being able to clean certain sections and be precise with it.