Skip to content

Instantly share code, notes, and snippets.

@partybusiness
Last active September 14, 2024 16:22
Show Gist options
  • Save partybusiness/970a018cd45c62b1d2b3c897bfe626aa to your computer and use it in GitHub Desktop.
Save partybusiness/970a018cd45c62b1d2b3c897bfe626aa to your computer and use it in GitHub Desktop.
Dithered transparency hole
extends Node3D
# Sets position of hole that makes character visible through walls
func _process(delta):
RenderingServer.global_shader_parameter_set("character_position", global_position)
shader_type spatial;
// creates dithered transparency on walls in front of character
// position of character that should be visible through hole
// you'll need to set this through script, something like this on the character:
// RenderingServer.global_shader_parameter_set("character_position", global_position)
global uniform vec3 character_position;
// radius of hole in metres
global uniform float hole_radius;
// noise texture used to dither the edge of the hole
// I've found I get visible banding with blue noise and white noise but not with simplex noise
// using :source_color hist could change curve depending what you like the look of
// I originally did this as a global uniform but there are some bugs around that
// https://github.com/godotengine/godot/issues/92178
// You can make this into a global if you know you'll only use an image file as the texture
// and not a procedural texture like NoiseTexture2D
uniform sampler2D dither_texture;
// calculated position of character
varying vec3 projected_character_position;
// https://mathworld.wolfram.com/Point-LineDistance3-Dimensional.html
float distance_between_point_and_line(vec3 ls, vec3 le, vec3 point) {
// ls = line start
// le = line end
// point = posiiton of point
float dis = length(cross((le-ls),(ls-point)))/length(le-ls);
return dis;
}
// just used for normal appearance
uniform vec4 wall_colour:source_color;
void vertex() {
// gets position of character in viewspace
projected_character_position = (VIEW_MATRIX * vec4(character_position,1.0)).xyz; //scale UVs so we use repeating
}
void fragment() {
// scaled UVs so screen dimensions don't stretch noise texture
vec2 scaled_uv = SCREEN_UV * VIEWPORT_SIZE / vec2(textureSize(dither_texture, 0));
// uses dither texture so edges of hole aren't sharp
float ditherTest = texture(dither_texture, scaled_uv).r;
// creates circle hole around position of character
float radiusTest = (clamp(distance_between_point_and_line(EYE_OFFSET,EYE_OFFSET+VIEW,projected_character_position)/hole_radius, 0.0, 1.0)-0.5)*2.0;
//distance(VERTEX,projected_character_position);
// gets whether normal of surface is pointing towards the character position
vec3 dir = (projected_character_position - VERTEX);
float aligned = dot(NORMAL,dir);
// hole should only appear in front of where character is and not behind
float depthTest = max(max(length(VERTEX)-length(projected_character_position), aligned+1.0), 0.0);
// combine them all
ditherTest = depthTest + ditherTest + radiusTest;
// based on shadow workaround listed here:
// https://github.com/godotengine/godot-proposals/issues/4443
// will need to change in Godot 4.3 to PROJECTION_MATRIX[2][3] because of reverse depth
bool is_shadow_pass = PROJECTION_MATRIX[3][2] == 0.0;
if (!is_shadow_pass && ditherTest<1.0)
discard;
// from here do whatever you want the normal appearance to be
ALBEDO.rgb = wall_colour.rgb;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment