Outline shader overrides the whole mesh, not just the outline

Godot Version

Question

Did you forget to add the details? Also no need to clutter up the feed by closing a question just update it

Seems there might be a bug or I missed something. Could post before without problem

Your post is empty, it just says “Godot Version” and “Question”, did you put something else in?

Im writing feedback in Reply cause of a posting bug:

edit: Adding some additional comments

My question: In my script, the shader is never loaded while is_selected is set to true!

extends Node3D

var outline_material: ShaderMaterial = load("res://shaders/OutlineShader.tres")

func _ready():
	print("OutlineApplier ready")
	# Connect to the selection_changed signals emitted by child nodes
	for child in get_children():
		if child.has_method("connect_selection_signal"):
			child.connect_selection_signal(Callable(self, "_on_selection_changed"))

# Called when the selection changes
func _on_selection_changed(node, is_selected: bool):
	if is_selected:
		print(node.name + " selected")
		apply_outline_shader(node)
	else:
		print(node.name + " deselected")
		remove_outline_shader(node)

func apply_outline_shader(node):
	print("Applying outline shader to ", node.name)
	# Apply the outline shader to the node itself if it's a MeshInstance or StaticBody3D
	if node.is_class("MeshInstance") or node.is_class("StaticBody3D"):
		var material = node.get_surface_material(0)
		# Check if the node already has a material applied
		if not material:
			# If no material is applied, apply the outline shader material
			node.set_surface_material(0, outline_material.instance())
	else:
		# Apply the outline shader to all MeshInstance children of the node
		for child in node.get_children():
			if child.is_class("MeshInstance"):
				var material = child.get_surface_material(0)
				# Check if the child already has a material applied
				if not material:
					# If no material is applied, apply the outline shader material
					child.set_surface_material(0, outline_material.instance())



func remove_outline_shader(node):
	print("Removing outline shader from ", node.name)
	# Remove the outline shader from all MeshInstance children of the node
	for child in node.get_children():
		if child.is_class("MeshInstance"):
			if child.get_surface_material(0) == outline_material:
				child.set_surface_material(0, null)


I just wrote a functioning reply to myself. Give it a try

So now Ive chose to go with material override instead. This works by changing the materials when units are selected. But the my shader overrides everything, so the objects are simply seen as outlines… Anyone got a clue?

extends Node3D

var outline_material: ShaderMaterial = load("res://shaders/OutlineShader.tres")

func _ready():
	print("OutlineApplier ready")
	# Connect to the selection_changed signals emitted by child nodes
	for child in get_children():
		if child.has_method("connect_selection_signal"):
			child.connect_selection_signal(Callable(self, "_on_selection_changed"))

# Called when the selection changes
func _on_selection_changed(node, is_selected: bool):
	if is_selected:
		print(node.name + " selected")
		apply_outline_shader(node)
	else:
		print(node.name + " deselected")
		remove_outline_shader(node)

func apply_outline_shader(node):
	print("Applying outline shader to ", node.name)
	# Check if the node is a MeshInstance
	if node.is_class("MeshInstance3D"):
		# Apply the outline shader material override if it's not already set
		if not node.material_override:
			node.material_override = outline_material
	else:
		# Iterate over all children and apply the outline shader recursively
		for child in node.get_children():
			apply_outline_shader(child)

func remove_outline_shader(node):
	print("Removing outline shader from ", node.name)
	# Remove the outline shader override from all MeshInstance children of the node
	for child in node.get_children():
		if child.is_class("MeshInstance3D"):
			if child.material_override == outline_material:
				child.material_override = null
		else:
			# Recursively remove the outline shader override from child nodes
			remove_outline_shader(child)


shader_type spatial;

render_mode unshaded, cull_front;

uniform float outline_thickness : hint_range(0.0, 0.2) = 0.1;
uniform vec4 outline_color : source_color = vec4(1.0, 1.0, 0.0, 1.0); // Yellow by default
uniform sampler2D texture_albedo : hint_screen_texture; // Texture uniform

void vertex() {
    VERTEX += NORMAL * outline_thickness;
}

void fragment() {
    ALBEDO = outline_color.rgb;
}

So I finnaly found my solution. Feel free to use for selected outlines!

extends Node3D

var outline_material: ShaderMaterial = preload("res://shaders/OutlineShader.tres")

func _ready():
	print("OutlineApplier ready")
	# Connect to the selection_changed signals emitted by child nodes
	for child in get_children():
		if child.has_method("connect_selection_signal"):
			child.connect_selection_signal(Callable(self, "_on_selection_changed"))

# Called when the selection changes
func _on_selection_changed(node, is_selected: bool):
	if is_selected:
		print(node.name + " selected")
		apply_outline_shader(node)
	else:
		print(node.name + " deselected")
		remove_outline_shader(node)

func apply_outline_shader(node):
	print("Applying outline shader to ", node.name)
	# Check if the node is a MeshInstance
	if node.is_class("MeshInstance3D"):
		# Apply the outline shader material override if it's not already set
		if not node.get_surface_override_material(0):
			var original_material = node.mesh.surface_get_material(0)
			if original_material:
				# Create a new material that uses the outline as a next pass
				var material_with_outline = original_material.duplicate()
				material_with_outline.next_pass = outline_material
				node.set_surface_override_material(0, material_with_outline)
	else:
		# Iterate over all children and apply the outline shader recursively
		for child in node.get_children():
			apply_outline_shader(child)

func remove_outline_shader(node):
	print("Removing outline shader from ", node.name)
	# Check if the node is a MeshInstance
	if node.is_class("MeshInstance3D"):
		# Remove the outline shader override if it's set to the outline material
		if node.get_surface_override_material(0) and node.get_surface_override_material(0).next_pass == outline_material:
			node.set_surface_override_material(0, null)
	else:
		# Iterate over all children and remove the outline shader override recursively
		for child in node.get_children():
			remove_outline_shader(child)

shader_type spatial;

render_mode unshaded, cull_front;

uniform float outline_thickness : hint_range(0.0, 0.2) = 0.1;
uniform vec4 outline_color : source_color = vec4(0.0, 0.0, 1.0, 1.0); // Blue outline color

void vertex() {
    VERTEX += NORMAL * outline_thickness;
}

void fragment() {
    ALBEDO = outline_color.rgb;
}