Last active
September 14, 2024 16:22
-
-
Save partybusiness/970a018cd45c62b1d2b3c897bfe626aa to your computer and use it in GitHub Desktop.
Dithered transparency hole
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
extends Node3D | |
# Sets position of hole that makes character visible through walls | |
func _process(delta): | |
RenderingServer.global_shader_parameter_set("character_position", global_position) | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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