#Shaders duplication help!

14 messages · Page 1 of 1 (latest)

potent hill
#

I've got a confusing situation - after duplicating my shaders don't work. How can i properly duplicate shader that will work separately for each mesh instance?
Shader:

shader_type spatial;

render_mode cull_disabled;

varying float hole_radius;
varying vec3 frag_pos;
uniform vec4 my_color : source_color;

const int MAX_HOLES = 10;
uniform vec3 holes[MAX_HOLES];
uniform float hole_radii[MAX_HOLES];
uniform int num_holes;
uniform sampler2D my_texture;



varying vec2 uv;

void vertex() {
    uv = UV;
    frag_pos = VERTEX;
}

void fragment() {
    vec3 color = vec3(0.1f,  0.1f,  0.1f);
    vec4 tex_color = texture(my_texture, uv);
    ALBEDO = mix(tex_color.rgb, color,  0.5) * float(FRONT_FACING);
    
    for (int i =  0; i < num_holes; ++i) {
        ALBEDO = mix(tex_color.rgb, color,  0.5) * float(FRONT_FACING);
        float dist = distance(frag_pos, holes[i]);
        if (dist < hole_radii[i]){
            discard;
        } else {
            EMISSION = my_color.rgb * (1.0f - clamp((dist / hole_radii[i] *  1.5f),  0.0f,  1.0f)) + (1.0f - float(FRONT_FACING)) * my_color.rgb;
        }
    }
}
#

Code:

extends MeshInstance3D

var is_cutting_hole : bool = false
@onready var area = $Area3D

const MAX_HOLES =  10 # Maximum number of holes you want to support
var holes = [] # Array to store hole positions
var hole_radii = []
var shader_material : ShaderMaterial

var hole_size : float = 0.1
var aabb_size : float
@export var emission_color : Color
@export var path_to_texture : CompressedTexture2D

func _ready():
    shader_material  = mesh.surface_get_material(0)
    shader_material.set_shader_parameter("my_color", emission_color)
    shader_material.set_shader_parameter("holes", holes) # Pass the array of hole positions to the shader
    shader_material.set_shader_parameter("hole_radii", hole_radii)
    shader_material.set_shader_parameter("num_holes", len(holes)) # Set the number of holes
    shader_material.set_shader_parameter("my_texture", path_to_texture)
    
    aabb_size = get_aabb().size.length()

func _physics_process(delta):
    cut_hole()

func cut_hole():
    if is_cutting_hole:
        hole_size += 0.01
        update_radii()
        if hole_size >= aabb_size:
            is_cutting_hole = false

func _on_area_3d_input_event(camera, event, _position, normal, shape_idx):
    if event is InputEventMouseButton:
        add_hole(_position)

func add_hole(_position,_radius : float = 0.01):
    if len(holes) < MAX_HOLES:
        holes.append(_position)
        hole_radii.append(_radius)
        is_cutting_hole = true
        update_shader_params()
        
func update_shader_params():
    shader_material.set_shader_parameter("num_holes", len(holes))
    shader_material.set_shader_parameter("holes", holes)
    shader_material.set_shader_parameter("hole_radii", hole_radii)

func update_radii():
    for i in range(len(hole_radii)):
        hole_radii[i] +=  0.01
    shader_material.set_shader_parameter("hole_radii", hole_radii)
    
rancid spoke
#

after duplicating the material, you need to assign the new one to the mesh

#
shader_material = mesh.surface_get_material(0).duplicate()
material_override = shader_material
#

without the second line, the new material is not used anywhere

#

I also recommend using material override in MeshInstance3D (as in the example above) instead of modifying the material in the mesh resource directly, because the mesh resource is still shared by all of the MeshInstance3Ds, and duplicating mesh resources can be expensive

potent hill
#

It kinda works, but not really

rancid spoke
#

if your mesh has multiple surfaces and you only want to override the material on one of them, MeshInstance3D has a method to do it too

rancid spoke
potent hill
rancid spoke
#

the shader parameters already seem to be applied separately so that's an improvement

potent hill
#

Yeah, thanks on this! Will try to fix everything else now 🙏

#

Yeah, it was position that was off! @rancid spoke Thank you very much