As stated in the experiment page, the intention here is to add and remove dirt from meshes dynamically during runtime. My implementation works in packaged games and projects, without any performance hit (dealing with vertex colors is cheap).
In any case, I am not going to use this implementation and this approach. I decided to move forward with splat maps and realtime texture painting with Render Textures from Viewports, because then I don’t have to worry about mesh density. But it’s fine to use this implementation for simple meshes and simple use cases.
The source-code and a sample working project is available on my Github.
Mesh and Material Setup
- In any mesh, paint in any of the vertex color RGB channels (or in all of them for different effects) where you want to have dirt (or any kind of texture)
- In the material, you interpolate between the different textures with the main texture of the mesh, where the amount/alpha are the vertex colors.
Painting in the editor (Preloading vertex colors)
In order to map the starting colors of the vertices (for example, where in the car it’s going to be dirty?), the vertices have to be pre-painted.
They can be painted both in an external 3D package (Blender, etc) or inside Godot, but to paint inside Godot you need the VPainter addon.
Then, with this implementation, you can paint the vertices at runtime, for example, to remove dirt and other effects.
- Attach a script to your character or camera.
- Point your camera to a mesh with vertex colors painted and a mesh that has the previous material, and paint (or clean!).
- Download the sample project from GitHub.
extends Spatial var recheck := false var ray_length = 1000 var brush_size:float = .3 var brush_opacity:float = .25 var brush_hardness:float = 0.2 var paint_color = Color(1, 1, 1, 1) var click_position export var mesh_path : NodePath var current_mesh : MeshInstance func _ready(): current_mesh = get_node(mesh_path) as MeshInstance func _unhandled_input(event): if event is InputEventMouseMotion: recheck = true func _physics_process(delta): if recheck: var position2D = get_tree().root.get_mouse_position() var camera = $Camera var from = camera.project_ray_origin(position2D) var to = from + camera.project_ray_normal(position2D) * ray_length var space_state = get_world().direct_space_state # use global coordinates, not local to node var result = space_state.intersect_ray(from, to, [self], 1, true, true) if result.size() > 0: click_position = result.position _paint_tool() recheck = false # # This code segment is taken from https://github.com/tomankirilov/VPainter # Copyright (c) 2020 toman kirilov (MIT License) # func _paint_tool() -> void: var data = MeshDataTool.new() data.create_from_surface(current_mesh.mesh, 0) for i in range(data.get_vertex_count()): var vertex := current_mesh.to_global(data.get_vertex(i)) var vertex_distance:float = vertex.distance_to(click_position) if vertex_distance < brush_size/2: var linear_distance = 1 - (vertex_distance / (brush_size/2)) var calculated_hardness = linear_distance * brush_hardness data.set_vertex_color(i, data.get_vertex_color(i).linear_interpolate(paint_color, brush_opacity)) current_mesh.mesh.surface_remove(0) data.commit_to_surface(current_mesh.mesh)
Coffee, Coins, Thumbs Up, and Newsletter
If you like my content or if you learned something from it, buy me a coffee ☕, be my Patreon or simply check all of my links 🔗 and follow me/subscribe/star my repositories/whatever you prefer. If you want to learn Godot, be sure to check my courses 📚!
Or you can simply add my game to your Steam Wishlist – that helps GREATLY and it’s easy and free 🙂
Also, be sure to sign up to my newsletter 📧 in order to receive my technical and creative content, just like this post.
Where to place the materials?
In the example project, the example Mesh is a car from Kenney, which has many different surfaces.
In this case, MeshInstance’s “Surface 3” contains the material
R_M_SuvHood.tres and “Surface 5” contains the material
Tools Used in this Log
Styles Related to this Log
Current Project Status
All Logs from the Project
- In-game vertex painting with Unreal Engine (Wash Car Effect) (July 30, 2022 13:49)
- In-game vertex painting with Godot Engine (Wash Car Effect) (July 30, 2022 18:06)