Disable texture filtering (nearest) after shader

Godot Version

4.2

Question

I’m adding PS1 style vertex snapping to my game. I have the following shader:

shader_type spatial;

uniform sampler2D albedo_texture : source_color;
uniform vec4 tint_color = vec4(1.0, 0.0, 0.0, 1.0); // Red tint


void vertex() {
    // Adjust the snap intensity; higher values result in more noticeable snapping.
    float snap_value = 1.0 / 120.0; // Adjust as needed

    // Transform to clip space
    vec4 clip_pos = PROJECTION_MATRIX * MODELVIEW_MATRIX * vec4(VERTEX, 1.0);

    // Perspective division to NDC
    vec3 ndc_pos = clip_pos.xyz / clip_pos.w;

    // Snap in screen space
    ndc_pos.xy = floor(ndc_pos.xy / snap_value + 0.5) * snap_value;

    // Reconstruct clip space position
    clip_pos.xyz = ndc_pos * clip_pos.w;

    // Set the transformed position
    POSITION = clip_pos;
}

void fragment() {
    vec4 tex_color = texture(albedo_texture, UV);
    ALBEDO = tex_color.rgb * tint_color.rgb;
    ALPHA = tex_color.a;
}

And it’s applied from here:

extends Node

# Preload your custom snap shader
var snap_shader = preload("res://snap_shader.gdshader")

# Define texture flag constants
const FILTER_FLAG = 1 << 0    # 1
const MIPMAPS_FLAG = 1 << 1   # 2
# Add other flags if necessary

func _ready():
	apply_snap_shader_to_tree(get_tree().get_root())

func apply_snap_shader_to_tree(node):
	if node is MeshInstance3D:
		# Get the currently active material
		var existing_material = node.get_active_material(0)
		
		if existing_material:
			# Create a new ShaderMaterial
			var shader_material = ShaderMaterial.new()
			shader_material.shader = snap_shader
			
			# Handle different material types
			if existing_material is StandardMaterial3D:
				# Duplicate and modify the albedo texture
				var original_albedo = existing_material.albedo_texture
				if original_albedo:
					var albedo_dup = original_albedo.duplicate()
					shader_material.set_shader_parameter("albedo_texture", albedo_dup)
				
				# Duplicate and modify the normal texture if it exists
				if existing_material.normal_texture:
					var original_normal = existing_material.normal_texture
					var normal_dup = original_normal.duplicate()
					shader_material.set_shader_parameter("normal_texture", normal_dup)
				
				# Copy the albedo color (tint color)
				shader_material.set_shader_parameter("tint_color", existing_material.albedo_color)
				
			elif existing_material is ShaderMaterial:
				# Handle ShaderMaterial differently if needed
				# For simplicity, you can skip or merge shaders if needed
				pass
			
			else:
				# Handle other material types if necessary
				pass
			
			# Apply the new shader material as a material override
			node.material_override = shader_material
	# Recursively apply to all child nodes
	for child in node.get_children():
		apply_snap_shader_to_tree(child)

ChatGPT helped with a lot of this…
Anyway, it looks really nice, but textures are now blurry. I want them to have nearest neighbour pixellation. I can’t find a way to do this. ChatGPT has given me a million variations of code re flags, which don’t seem to be a thing in 4.2. Any help is massively appreciated!

This is how you fix it for 2D. Might work. Probably won’t.

  1. Project → Project Settings
  2. rendering/textures/canvas_textures/default_texture_filter

What happens if you apply the texture to the Material Override manually?

Thanks for your suggestions, I already have rendering/textures/canvas_textures/default_texture_filter as Nearest which doesn’t work.

I’ve just tried the second suggestion, unfortunately it still comes out blurry in game after the shader is applied.

Solved it! If I add filter_nearest like this:

uniform sampler2D albedo_texture : source_color, filter_nearest;

It works!

1 Like

awesome! Glad you got it! I’ll remember that trick.