système de sauvegarde, scène 3D de test sur la base astra et passage en forward+
This commit is contained in:
13
common/assets/materials/default_3d.tres
Normal file
13
common/assets/materials/default_3d.tres
Normal file
@@ -0,0 +1,13 @@
|
||||
[gd_resource type="StandardMaterial3D" format=3 uid="uid://dvvi1k5c5iowc"]
|
||||
|
||||
[ext_resource type="Texture2D" uid="uid://dlsx1mofjccib" path="res://common/assets/textures/3d_texture.png" id="1_cc1ni"]
|
||||
|
||||
[resource]
|
||||
diffuse_mode = 3
|
||||
specular_mode = 2
|
||||
albedo_texture = ExtResource("1_cc1ni")
|
||||
roughness = 0.0
|
||||
rim_tint = 0.48
|
||||
stencil_flags = 2
|
||||
stencil_reference = 5
|
||||
stencil_outline_thickness = 0.626
|
||||
9
common/assets/materials/glass_3d.tres
Normal file
9
common/assets/materials/glass_3d.tres
Normal file
@@ -0,0 +1,9 @@
|
||||
[gd_resource type="StandardMaterial3D" format=3 uid="uid://b5vlcdry8vtgi"]
|
||||
|
||||
[ext_resource type="Texture2D" uid="uid://dlsx1mofjccib" path="res://common/assets/textures/3d_texture.png" id="1_buts1"]
|
||||
|
||||
[resource]
|
||||
transparency = 1
|
||||
depth_draw_mode = 1
|
||||
albedo_color = Color(1, 1, 1, 0.79607844)
|
||||
albedo_texture = ExtResource("1_buts1")
|
||||
BIN
common/assets/textures/3d_texture.png
Normal file
BIN
common/assets/textures/3d_texture.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.1 KiB |
41
common/assets/textures/3d_texture.png.import
Normal file
41
common/assets/textures/3d_texture.png.import
Normal file
@@ -0,0 +1,41 @@
|
||||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://dlsx1mofjccib"
|
||||
path.s3tc="res://.godot/imported/3d_texture.png-5d7cb2d23d37985e4dc0c0bd241d8e43.s3tc.ctex"
|
||||
metadata={
|
||||
"imported_formats": ["s3tc_bptc"],
|
||||
"vram_texture": true
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://common/assets/textures/3d_texture.png"
|
||||
dest_files=["res://.godot/imported/3d_texture.png-5d7cb2d23d37985e4dc0c0bd241d8e43.s3tc.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=2
|
||||
compress/high_quality=false
|
||||
compress/lossy_quality=0.7
|
||||
compress/uastc_level=0
|
||||
compress/rdo_quality_loss=0.0
|
||||
compress/hdr_compression=1
|
||||
compress/normal_map=0
|
||||
compress/channel_pack=0
|
||||
mipmaps/generate=true
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=0
|
||||
roughness/src_normal=""
|
||||
process/channel_remap/red=0
|
||||
process/channel_remap/green=1
|
||||
process/channel_remap/blue=2
|
||||
process/channel_remap/alpha=3
|
||||
process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/normal_map_invert_y=false
|
||||
process/hdr_as_srgb=false
|
||||
process/hdr_clamp_exposure=false
|
||||
process/size_limit=0
|
||||
detect_3d/compress_to=0
|
||||
BIN
common/assets/textures/dither_texture.jpg
Normal file
BIN
common/assets/textures/dither_texture.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 170 KiB |
41
common/assets/textures/dither_texture.jpg.import
Normal file
41
common/assets/textures/dither_texture.jpg.import
Normal file
@@ -0,0 +1,41 @@
|
||||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://dc8gajse5ti5t"
|
||||
path.s3tc="res://.godot/imported/dither_texture.jpg-29df728b3fc6c30c4db6300481aaf214.s3tc.ctex"
|
||||
metadata={
|
||||
"imported_formats": ["s3tc_bptc"],
|
||||
"vram_texture": true
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://common/assets/textures/dither_texture.jpg"
|
||||
dest_files=["res://.godot/imported/dither_texture.jpg-29df728b3fc6c30c4db6300481aaf214.s3tc.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=2
|
||||
compress/high_quality=false
|
||||
compress/lossy_quality=0.7
|
||||
compress/uastc_level=0
|
||||
compress/rdo_quality_loss=0.0
|
||||
compress/hdr_compression=1
|
||||
compress/normal_map=0
|
||||
compress/channel_pack=0
|
||||
mipmaps/generate=true
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=0
|
||||
roughness/src_normal=""
|
||||
process/channel_remap/red=0
|
||||
process/channel_remap/green=1
|
||||
process/channel_remap/blue=2
|
||||
process/channel_remap/alpha=3
|
||||
process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/normal_map_invert_y=false
|
||||
process/hdr_as_srgb=false
|
||||
process/hdr_clamp_exposure=false
|
||||
process/size_limit=0
|
||||
detect_3d/compress_to=0
|
||||
@@ -3,7 +3,7 @@ extends Node
|
||||
const MIN_VOLUME = -60.
|
||||
const MAX_VOLUME = 24.
|
||||
|
||||
@onready var settings : SettingsData = GameInfo.game_data.settings
|
||||
@onready var settings : SettingsData = GameInfo.settings_data
|
||||
|
||||
@export var default_fade_time = 1.0
|
||||
|
||||
|
||||
@@ -3,14 +3,18 @@ class_name GameData
|
||||
|
||||
signal current_region_data_updated(p : RegionData)
|
||||
|
||||
@export var settings : SettingsData = SettingsData.new()
|
||||
|
||||
@export var current_run : RunData = null
|
||||
|
||||
@export var current_region_data : RegionData = null
|
||||
|
||||
@export var player_data : PlayerData = PlayerData.new()
|
||||
|
||||
@export var max_mutations_by_plant : int = 2
|
||||
|
||||
@export var dialogs_done : Array[String] = []
|
||||
|
||||
@export var actual_scene : Scene = null
|
||||
|
||||
func start_run():
|
||||
player_data.inventory.clear()
|
||||
current_run = RunData.new()
|
||||
@@ -29,3 +33,4 @@ func reset_all():
|
||||
func start_region(region_param : RegionParameter):
|
||||
current_region_data = RegionData.new(region_param)
|
||||
current_region_data_updated.emit(current_region_data)
|
||||
GameInfo.save_game_data()
|
||||
|
||||
@@ -92,6 +92,7 @@ func choose_next_run_point(run_point : RunPoint) -> RunPoint:
|
||||
if current_run_point:
|
||||
visited_run_points.append(current_run_point)
|
||||
current_run_point = run_point
|
||||
GameInfo.game_data.start_region(run_point.region_parameter)
|
||||
next_run_points = generate_next_run_points(current_run_point)
|
||||
return current_run_point
|
||||
|
||||
|
||||
@@ -30,8 +30,8 @@ func card_info() -> CardInfo:
|
||||
info.type_icon = TYPE_ICON
|
||||
|
||||
info.stats.append_array([
|
||||
CardStatInfo.new(tr("%d_GARDEN_POINTS") % region_parameter.objective, OBJECTIVE_ICON),
|
||||
CardStatInfo.new(tr("%d_CHARGES_AVAILABLE") % region_parameter.charges, CHARGE_ICON),
|
||||
CardStatInfo.new(str(region_parameter.objective), OBJECTIVE_ICON),
|
||||
CardStatInfo.new(str(region_parameter.charges), CHARGE_ICON),
|
||||
])
|
||||
|
||||
return info
|
||||
@@ -1,34 +1,73 @@
|
||||
extends Node
|
||||
|
||||
const SAVE_GAME_LOCATION = "res://stw_demo_save.tres"
|
||||
const SAVE_SETTINGS_LOCATION = "res://stw_settings.tres"
|
||||
|
||||
var game_loaded = false
|
||||
|
||||
signal game_data_updated(g : GameData)
|
||||
|
||||
var game_data : GameData :
|
||||
set(v):
|
||||
game_data = v
|
||||
game_data_updated.emit(v)
|
||||
set(v):
|
||||
game_data = v
|
||||
game_data_updated.emit(v)
|
||||
|
||||
var settings_data : SettingsData
|
||||
|
||||
func load_game_data() -> GameData:
|
||||
if ResourceLoader.exists(SAVE_GAME_LOCATION):
|
||||
game_loaded = true
|
||||
game_data = ResourceLoader.load(SAVE_GAME_LOCATION).duplicate_deep()
|
||||
else :
|
||||
game_data = GameData.new()
|
||||
save_game_data()
|
||||
|
||||
return game_data
|
||||
|
||||
func save_game_data():
|
||||
if game_data:
|
||||
ResourceSaver.save(game_data, SAVE_GAME_LOCATION)
|
||||
|
||||
func load_settings_data() -> SettingsData:
|
||||
if ResourceLoader.exists(SAVE_SETTINGS_LOCATION):
|
||||
print("settings exists")
|
||||
settings_data = ResourceLoader.load(SAVE_SETTINGS_LOCATION).duplicate_deep()
|
||||
else :
|
||||
settings_data = SettingsData.new()
|
||||
save_settings()
|
||||
|
||||
return settings_data
|
||||
|
||||
func save_settings():
|
||||
if settings_data:
|
||||
ResourceSaver.save(settings_data, SAVE_SETTINGS_LOCATION)
|
||||
|
||||
func restart_game_data():
|
||||
game_data = GameData.new()
|
||||
save_game_data()
|
||||
|
||||
func _init():
|
||||
if not game_data:
|
||||
game_data = GameData.new()
|
||||
update_language_settings(game_data.settings)
|
||||
update_video_settings(game_data.settings)
|
||||
game_data.settings.language_changed.connect(_on_settings_language_changed)
|
||||
game_data.settings.video_changed.connect(_on_settings_video_changed)
|
||||
load_game_data()
|
||||
load_settings_data()
|
||||
update_language_settings(settings_data)
|
||||
update_video_settings(settings_data)
|
||||
settings_data.language_changed.connect(_on_settings_language_changed)
|
||||
settings_data.video_changed.connect(_on_settings_video_changed)
|
||||
|
||||
|
||||
func _on_settings_video_changed(s : SettingsData):
|
||||
update_video_settings(s)
|
||||
update_video_settings(s)
|
||||
|
||||
func _on_settings_language_changed(s : SettingsData):
|
||||
update_language_settings(s)
|
||||
update_language_settings(s)
|
||||
|
||||
func update_language_settings(s : SettingsData):
|
||||
TranslationServer.set_locale(s.language)
|
||||
TranslationServer.set_locale(s.language)
|
||||
|
||||
func update_video_settings(s : SettingsData):
|
||||
DisplayServer.window_set_mode(DisplayServer.WINDOW_MODE_FULLSCREEN if s.full_screen else DisplayServer.WINDOW_MODE_WINDOWED)
|
||||
DisplayServer.window_set_mode(DisplayServer.WINDOW_MODE_FULLSCREEN if s.full_screen else DisplayServer.WINDOW_MODE_WINDOWED)
|
||||
|
||||
func update_input(action : String, old_event : InputEvent, event : InputEvent):
|
||||
InputMap.action_erase_event(action, old_event)
|
||||
InputMap.action_add_event(action, event)
|
||||
|
||||
InputMap.action_erase_event(action, old_event)
|
||||
InputMap.action_add_event(action, event)
|
||||
|
||||
|
||||
1
common/icons/coin.svg
Normal file
1
common/icons/coin.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="#ffffff" class="icon icon-tabler icons-tabler-filled icon-tabler-coin"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M17 3.34a10 10 0 1 1 -15 8.66l.005 -.324a10 10 0 0 1 14.995 -8.336zm-5 2.66a1 1 0 0 0 -1 1a3 3 0 1 0 0 6v2a1.024 1.024 0 0 1 -.866 -.398l-.068 -.101a1 1 0 0 0 -1.732 .998a3 3 0 0 0 2.505 1.5h.161a1 1 0 0 0 .883 .994l.117 .007a1 1 0 0 0 1 -1l.176 -.005a3 3 0 0 0 -.176 -5.995v-2c.358 -.012 .671 .14 .866 .398l.068 .101a1 1 0 0 0 1.732 -.998a3 3 0 0 0 -2.505 -1.501h-.161a1 1 0 0 0 -1 -1zm1 7a1 1 0 0 1 0 2v-2zm-2 -4v2a1 1 0 0 1 0 -2z" /></svg>
|
||||
|
After Width: | Height: | Size: 662 B |
43
common/icons/coin.svg.import
Normal file
43
common/icons/coin.svg.import
Normal file
@@ -0,0 +1,43 @@
|
||||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://cs5jk1312wauu"
|
||||
path="res://.godot/imported/coin.svg-08623932cd6c1ab8a19bb6ae049240d0.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://common/icons/coin.svg"
|
||||
dest_files=["res://.godot/imported/coin.svg-08623932cd6c1ab8a19bb6ae049240d0.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/high_quality=false
|
||||
compress/lossy_quality=0.7
|
||||
compress/uastc_level=0
|
||||
compress/rdo_quality_loss=0.0
|
||||
compress/hdr_compression=1
|
||||
compress/normal_map=0
|
||||
compress/channel_pack=0
|
||||
mipmaps/generate=false
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=0
|
||||
roughness/src_normal=""
|
||||
process/channel_remap/red=0
|
||||
process/channel_remap/green=1
|
||||
process/channel_remap/blue=2
|
||||
process/channel_remap/alpha=3
|
||||
process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/normal_map_invert_y=false
|
||||
process/hdr_as_srgb=false
|
||||
process/hdr_clamp_exposure=false
|
||||
process/size_limit=0
|
||||
detect_3d/compress_to=1
|
||||
svg/scale=2.0
|
||||
editor/scale_with_editor_scale=false
|
||||
editor/convert_colors_with_editor_theme=false
|
||||
@@ -7,7 +7,6 @@ signal scene_node_ready(scene : Scene)
|
||||
|
||||
var loading_scene = false
|
||||
var generating_node = false
|
||||
var actual_scene : Scene = null
|
||||
var next_scene_node : Node
|
||||
@onready var current_scene_node : Node = get_tree().root.get_children().back()
|
||||
|
||||
@@ -30,7 +29,7 @@ func change_scene(scene_id : String, with_loading = true):
|
||||
if not scene:
|
||||
printerr("Scene %s not found" % scene_id)
|
||||
return
|
||||
actual_scene = scene
|
||||
GameInfo.game_data.actual_scene = scene
|
||||
loading_scene = true
|
||||
var scene_path_to_load = scene.scene_path
|
||||
ResourceLoader.load_threaded_request(scene_path_to_load)
|
||||
@@ -70,11 +69,11 @@ func change_scene(scene_id : String, with_loading = true):
|
||||
func _process(_delta):
|
||||
if loading_scene:
|
||||
var progress = []
|
||||
var load_status := ResourceLoader.load_threaded_get_status(actual_scene.scene_path, progress)
|
||||
var load_status := ResourceLoader.load_threaded_get_status(GameInfo.game_data.actual_scene.scene_path, progress)
|
||||
LoadingScreen.loading_value = progress[0]
|
||||
if load_status == ResourceLoader.THREAD_LOAD_LOADED:
|
||||
loading_scene = false
|
||||
scene_loaded.emit(actual_scene)
|
||||
scene_loaded.emit(GameInfo.game_data.actual_scene)
|
||||
if load_status == ResourceLoader.THREAD_LOAD_FAILED or load_status == ResourceLoader.THREAD_LOAD_INVALID_RESOURCE:
|
||||
printerr()
|
||||
elif generating_node:
|
||||
@@ -85,4 +84,4 @@ func _process(_delta):
|
||||
scene_node_ready.emit()
|
||||
elif next_scene_node.is_node_ready():
|
||||
generating_node = false
|
||||
scene_node_ready.emit(actual_scene)
|
||||
scene_node_ready.emit(GameInfo.game_data.actual_scene)
|
||||
|
||||
@@ -7,7 +7,7 @@ const VOWEL = ["a","e","i","o","u","y"]
|
||||
const CONSONANTS = ["b", "c", "d", "f", "g", "h", "j", "k", "l", "m", "n", "p", "q", "r", "s", "t", "v", "w", "x", "z"]
|
||||
|
||||
|
||||
static func generate_random_name(random_seed = randi()) -> String:
|
||||
static func generate_random_word(random_seed = randi()) -> String:
|
||||
var word_len = randf_range(4,8)
|
||||
var word = ''
|
||||
var last_letter_is_vowel = false
|
||||
@@ -21,18 +21,24 @@ static func generate_random_name(random_seed = randi()) -> String:
|
||||
last_letter_is_vowel = not last_letter_is_vowel
|
||||
return word.capitalize()
|
||||
|
||||
static func mutate_name(word : String) -> String:
|
||||
return word
|
||||
# TODO
|
||||
static func mutate_word(word : String) -> String:
|
||||
var rand_int = randi()
|
||||
|
||||
if len(word) > MIN_WORD_LEN and rand_int % 3 == 0:
|
||||
return shorten_word(word)
|
||||
elif len(word) < MAX_WORD_LEN and rand_int % 3 == 1:
|
||||
return elongate_word(word)
|
||||
|
||||
return replace_character(word)
|
||||
|
||||
|
||||
func shorten_name(word : String):
|
||||
static func shorten_word(word : String):
|
||||
if randi()%2 == 0:
|
||||
return word.left(len(word) - 1).capitalize()
|
||||
else :
|
||||
return word.right(len(word) - 1).capitalize()
|
||||
|
||||
func elongate_name(word : String):
|
||||
static func elongate_word(word : String):
|
||||
if randi()%2 == 0:
|
||||
var letter = CONSONANTS.pick_random() if word.left(1) in VOWEL else VOWEL.pick_random()
|
||||
return (letter + word).capitalize()
|
||||
@@ -40,6 +46,14 @@ func elongate_name(word : String):
|
||||
var letter = CONSONANTS.pick_random() if word.right(1) in VOWEL else VOWEL.pick_random()
|
||||
return (word + letter).capitalize()
|
||||
|
||||
func replace_character(word : String):
|
||||
# TODO
|
||||
static func replace_character(word : String):
|
||||
var character_id = randi_range(0, len(word))
|
||||
var character = word[character_id]
|
||||
|
||||
if character in VOWEL:
|
||||
character = VOWEL.pick_random()
|
||||
else:
|
||||
character = CONSONANTS.pick_random()
|
||||
|
||||
word[character_id] = character
|
||||
return word
|
||||
|
||||
@@ -1,272 +1,85 @@
|
||||
shader_type spatial;
|
||||
render_mode unshaded, blend_mix, depth_draw_never, depth_test_disabled;
|
||||
render_mode unshaded, depth_draw_opaque, depth_prepass_alpha;
|
||||
|
||||
/*
|
||||
AUTHOR: Hannah "EMBYR" Crawford
|
||||
ENGINE_VERSION: 4.0.3
|
||||
|
||||
HOW TO USE:
|
||||
1. Create a MeshInstance3D node and place it in your scene.
|
||||
2. Set it's size to 2x2.
|
||||
3. Enable the "Flip Faces" option.
|
||||
4. Create a new shader material with this shader.
|
||||
5. Assign the material to the MeshInstance3D
|
||||
|
||||
LIMITATIONS:
|
||||
Does not work well with TAA enabled.
|
||||
|
||||
MOBILE_NOTES:
|
||||
The mobile renderer does not have access to the normal_roughness texture
|
||||
so we must rely on techniques to reconstruct this information from the
|
||||
depth buffer.
|
||||
|
||||
If you require support on mobile please uncomment the SUPPORT_MOBILE line
|
||||
below. I have done my best to match the appearance between the two modes
|
||||
however, mobile does not take into account smooth-shaded faces.
|
||||
|
||||
The high-quality reconstruction method used on mobile is rather heavy on
|
||||
texture samples. If you would like to use the lower-quality recontruction
|
||||
method for better performance, please uncomment the NAIVE_NORMAL_RECONSTRUCTION
|
||||
line below.
|
||||
*/
|
||||
//#define SUPPORT_MOBILE
|
||||
//#define NAIVE_NORMAL_RECONSTRUCTION
|
||||
// Inspired by https://godotshaders.com/shader/3d-pixel-art-outline-highlight-post-processing-shader/
|
||||
|
||||
group_uniforms outline;
|
||||
uniform vec4 outlineColor: source_color = vec4(0.0, 0.0, 0.0, 0.78);
|
||||
uniform float depth_threshold = 0.025;
|
||||
uniform float normal_threshold : hint_range(0.0, 1.5) = 0.5;
|
||||
uniform float normal_smoothing : hint_range(0.0, 1.0) = 0.25;
|
||||
uniform sampler2D DEPTH_TEXTURE : hint_depth_texture, filter_linear_mipmap;
|
||||
uniform sampler2D SCREEN_TEXTURE : hint_screen_texture, filter_linear_mipmap;
|
||||
uniform sampler2D NORMAL_TEXTURE : hint_normal_roughness_texture, filter_nearest;
|
||||
|
||||
group_uniforms thickness;
|
||||
uniform float max_thickness: hint_range(0.0, 5.0) = 1.3;
|
||||
uniform float min_thickness = 0.5;
|
||||
uniform float max_distance = 75.0;
|
||||
uniform float min_distance = 2.0;
|
||||
|
||||
group_uniforms grazing_prevention;
|
||||
uniform float grazing_fresnel_power = 5.0;
|
||||
uniform float grazing_angle_mask_power = 1.0;
|
||||
uniform float grazing_angle_modulation_factor = 50.0;
|
||||
uniform vec3 shadow_color : source_color = vec3(0.0);
|
||||
uniform float shadow_thickness = 2.0;
|
||||
|
||||
uniform sampler2D DEPTH_TEXTURE : hint_depth_texture, filter_linear, repeat_disable;
|
||||
|
||||
#ifndef SUPPORT_MOBILE
|
||||
uniform sampler2D NORMR_TEXTURE : hint_normal_roughness_texture, filter_linear, repeat_disable;
|
||||
#else
|
||||
varying flat mat4 model_view_matrix;
|
||||
#endif// !SUPPORT_MOBILE
|
||||
|
||||
struct UVNeighbors {
|
||||
vec2 center;
|
||||
vec2 left; vec2 right; vec2 up; vec2 down;
|
||||
vec2 top_left; vec2 top_right; vec2 bottom_left; vec2 bottom_right;
|
||||
};
|
||||
|
||||
struct NeighborDepthSamples {
|
||||
float c_d;
|
||||
float l_d; float r_d; float u_d; float d_d;
|
||||
float tl_d; float tr_d; float bl_d; float br_d;
|
||||
};
|
||||
|
||||
UVNeighbors getNeighbors(vec2 center, float width, float aspect) {
|
||||
vec2 h_offset = vec2(width * aspect * 0.001, 0.0);
|
||||
vec2 v_offset = vec2(0.0, width * 0.001);
|
||||
UVNeighbors n;
|
||||
n.center = center;
|
||||
n.left = center - h_offset;
|
||||
n.right = center + h_offset;
|
||||
n.up = center - v_offset;
|
||||
n.down = center + v_offset;
|
||||
n.top_left = center - (h_offset - v_offset);
|
||||
n.top_right = center + (h_offset - v_offset);
|
||||
n.bottom_left = center - (h_offset + v_offset);
|
||||
n.bottom_right = center + (h_offset + v_offset);
|
||||
return n;
|
||||
vec2 getDepth(vec2 screen_uv, sampler2D depth_texture, mat4 inv_projection_matrix){
|
||||
float raw_depth = texture(depth_texture, screen_uv)[0];
|
||||
vec3 normalized_device_coordinates = vec3(screen_uv * 2.0 - 1.0, raw_depth);
|
||||
vec4 view_space = inv_projection_matrix * vec4(normalized_device_coordinates, 1.0);
|
||||
view_space.xyz /= view_space.w;
|
||||
return vec2(-view_space.z, raw_depth);
|
||||
}
|
||||
|
||||
float getMinimumDepth(NeighborDepthSamples ds){
|
||||
return min(ds.c_d, min(ds.l_d, min(ds.r_d, min(ds.u_d, min(ds.d_d, min(ds.tl_d, min(ds.tr_d, min(ds.bl_d, ds.br_d))))))));
|
||||
}
|
||||
|
||||
float getLinearDepth(float depth, vec2 uv, mat4 inv_proj) {
|
||||
vec3 ndc = vec3(uv * 2.0 - 1.0, depth);
|
||||
vec4 view = inv_proj * vec4(ndc, 1.0);
|
||||
view.xyz /= view.w;
|
||||
return -view.z;
|
||||
}
|
||||
|
||||
NeighborDepthSamples getLinearDepthSamples(UVNeighbors uvs, sampler2D depth_tex, mat4 invProjMat) {
|
||||
NeighborDepthSamples result;
|
||||
result.c_d = getLinearDepth(texture(depth_tex, uvs.center).r, uvs.center, invProjMat);
|
||||
result.l_d = getLinearDepth(texture(depth_tex, uvs.left).r , uvs.left , invProjMat);
|
||||
result.r_d = getLinearDepth(texture(depth_tex, uvs.right).r , uvs.right , invProjMat);
|
||||
result.u_d = getLinearDepth(texture(depth_tex, uvs.up).r , uvs.up , invProjMat);
|
||||
result.d_d = getLinearDepth(texture(depth_tex, uvs.down).r , uvs.down , invProjMat);
|
||||
result.tl_d = getLinearDepth(texture(depth_tex, uvs.top_left).r, uvs.top_left, invProjMat);
|
||||
result.tr_d = getLinearDepth(texture(depth_tex, uvs.top_right).r, uvs.top_right, invProjMat);
|
||||
result.bl_d = getLinearDepth(texture(depth_tex, uvs.bottom_left).r, uvs.bottom_left, invProjMat);
|
||||
result.br_d = getLinearDepth(texture(depth_tex, uvs.bottom_right).r, uvs.bottom_right, invProjMat);
|
||||
return result;
|
||||
}
|
||||
|
||||
float remap(float v, float from1, float to1, float from2, float to2) {
|
||||
return (v - from1) / (to1 - from1) * (to2 - from2) + from2;
|
||||
}
|
||||
|
||||
float fresnel(float amount, vec3 normal, vec3 view) {
|
||||
return pow((1.0 - clamp(dot(normalize(normal), normalize(view)), 0.0, 1.0 )), amount);
|
||||
}
|
||||
|
||||
float getGrazingAngleModulation(vec3 pixel_normal, vec3 view) {
|
||||
float x = clamp(((fresnel(grazing_fresnel_power, pixel_normal, view) - 1.0) / grazing_angle_mask_power) + 1.0, 0.0, 1.0);
|
||||
return (x + grazing_angle_modulation_factor) + 1.0;
|
||||
}
|
||||
|
||||
float detectEdgesDepth(NeighborDepthSamples depth_samples, vec3 pixel_normal, vec3 view) {
|
||||
float n_total =
|
||||
depth_samples.l_d +
|
||||
depth_samples.r_d +
|
||||
depth_samples.u_d +
|
||||
depth_samples.d_d +
|
||||
depth_samples.tl_d +
|
||||
depth_samples.tr_d +
|
||||
depth_samples.bl_d +
|
||||
depth_samples.br_d;
|
||||
|
||||
float t = depth_threshold * getGrazingAngleModulation(pixel_normal, view);
|
||||
return step(t, n_total - (depth_samples.c_d * 8.0));
|
||||
}
|
||||
|
||||
// Reconstruction helpers
|
||||
// Source: https://www.reddit.com/r/godot/comments/v70p2k/improved_normal_from_depth/
|
||||
#ifdef SUPPORT_MOBILE
|
||||
vec3 reconstructWorldPosition(float depth, mat4 model_view, mat4 inv_proj, vec2 screen_uv, mat4 world, mat4 inv_cam){
|
||||
vec4 pos = inverse(model_view) * inv_proj * vec4((screen_uv * 2.0 - 1.0), depth * 2.0 - 1.0, 1.0);
|
||||
pos.xyz /= (pos.w + 0.0001 * (1.-abs(sign(pos.w))));
|
||||
return (pos * inv_cam).xyz + world[3].xyz;
|
||||
}
|
||||
|
||||
#ifndef NAIVE_NORMAL_RECONSTRUCTION
|
||||
vec3 reconstructWorldNormal(sampler2D depth_tex, mat4 model_view, mat4 inv_proj, vec2 screen_uv, mat4 world, mat4 inv_cam, vec2 viewport_size) {
|
||||
vec2 e = vec2(1.0 / viewport_size);
|
||||
float c0 = texture(depth_tex, screen_uv ).r;
|
||||
float l2 = texture(depth_tex, screen_uv - vec2(2,0) * e).r;
|
||||
float l1 = texture(depth_tex, screen_uv - vec2(1,0) * e).r;
|
||||
float r1 = texture(depth_tex, screen_uv + vec2(1,0) * e).r;
|
||||
float r2 = texture(depth_tex, screen_uv + vec2(2,0) * e).r;
|
||||
float b2 = texture(depth_tex, screen_uv - vec2(0,2) * e).r;
|
||||
float b1 = texture(depth_tex, screen_uv - vec2(0,1) * e).r;
|
||||
float t1 = texture(depth_tex, screen_uv + vec2(0,1) * e).r;
|
||||
float t2 = texture(depth_tex, screen_uv + vec2(0,2) * e).r;
|
||||
|
||||
float dl = abs(l1 * l2 / (2.0 * l2 - l1) - c0);
|
||||
float dr = abs(r1 * r2 / (2.0 * r2 - r1) - c0);
|
||||
float db = abs(b1 * b2 / (2.0 * b2 - b1) - c0);
|
||||
float dt = abs(t1 * t2 / (2.0 * t2 - t1) - c0);
|
||||
|
||||
vec3 ce = reconstructWorldPosition(c0, model_view, inv_proj, screen_uv, world, inv_cam);
|
||||
|
||||
vec3 dpdx = (dl<dr) ? ce-reconstructWorldPosition(l1, model_view, inv_proj, screen_uv - vec2(1,0) * e, world, inv_cam) :
|
||||
-ce+reconstructWorldPosition(r1, model_view, inv_proj, screen_uv + vec2(1,0) * e, world, inv_cam) ;
|
||||
vec3 dpdy = (db<dt) ? ce-reconstructWorldPosition(b1, model_view, inv_proj, screen_uv - vec2(0,1) * e, world, inv_cam) :
|
||||
-ce+reconstructWorldPosition(t1, model_view, inv_proj, screen_uv + vec2(0,1) * e, world, inv_cam) ;
|
||||
|
||||
return normalize(cross(dpdx,dpdy));
|
||||
}
|
||||
#else
|
||||
vec3 reconstructWorldNormal(sampler2D depth_tex, mat4 model_view, mat4 inv_proj, vec2 screen_uv, mat4 world, mat4 inv_cam, vec2 viewport_size) {
|
||||
vec3 pos = reconstructWorldPosition(texture(depth_tex, screen_uv).x, model_view, inv_proj, screen_uv, world, inv_cam);
|
||||
return normalize(cross(dFdx(pos), dFdy(pos)));
|
||||
}
|
||||
#endif//!NAIVE_NORMAL_RECONSTRUCTION
|
||||
|
||||
float detectEdgesNormalReconstructed(UVNeighbors uvs, sampler2D depth_tex, mat4 model_view, mat4 inv_proj, vec2 screen_uv, mat4 world, mat4 inv_cam, vec2 viewport_size){
|
||||
vec3 n_u = reconstructWorldNormal(depth_tex, model_view, inv_proj, uvs.up, world, inv_cam, viewport_size);
|
||||
vec3 n_d = reconstructWorldNormal(depth_tex, model_view, inv_proj, uvs.down, world, inv_cam, viewport_size);
|
||||
vec3 n_l = reconstructWorldNormal(depth_tex, model_view, inv_proj, uvs.left, world, inv_cam, viewport_size);
|
||||
vec3 n_r = reconstructWorldNormal(depth_tex, model_view, inv_proj, uvs.right, world, inv_cam, viewport_size);
|
||||
vec3 n_tl = reconstructWorldNormal(depth_tex, model_view, inv_proj, uvs.top_left, world, inv_cam, viewport_size);
|
||||
vec3 n_tr = reconstructWorldNormal(depth_tex, model_view, inv_proj, uvs.top_right, world, inv_cam, viewport_size);
|
||||
vec3 n_bl = reconstructWorldNormal(depth_tex, model_view, inv_proj, uvs.bottom_left, world, inv_cam, viewport_size);
|
||||
vec3 n_br = reconstructWorldNormal(depth_tex, model_view, inv_proj, uvs.bottom_right, world, inv_cam, viewport_size);
|
||||
|
||||
vec3 normalFiniteDifference0 = n_tr - n_bl;
|
||||
vec3 normalFiniteDifference1 = n_tl - n_br;
|
||||
vec3 normalFiniteDifference2 = n_l - n_r;
|
||||
vec3 normalFiniteDifference3 = n_u - n_d;
|
||||
|
||||
float edgeNormal = sqrt(
|
||||
dot(normalFiniteDifference0, normalFiniteDifference0) +
|
||||
dot(normalFiniteDifference1, normalFiniteDifference1) +
|
||||
dot(normalFiniteDifference2, normalFiniteDifference2) +
|
||||
dot(normalFiniteDifference3, normalFiniteDifference3)
|
||||
) * 0.5;
|
||||
|
||||
return smoothstep(normal_threshold - normal_smoothing, normal_threshold + normal_smoothing, edgeNormal);
|
||||
}
|
||||
#else
|
||||
float detectEdgesNormal(UVNeighbors uvs, sampler2D normTex, vec3 camDirWorld){
|
||||
vec3 n_u = texture(normTex, uvs.up).xyz;
|
||||
vec3 n_d = texture(normTex, uvs.down).xyz;
|
||||
vec3 n_l = texture(normTex, uvs.left).xyz;
|
||||
vec3 n_r = texture(normTex, uvs.right).xyz;
|
||||
vec3 n_tl = texture(normTex, uvs.top_left).xyz;
|
||||
vec3 n_tr = texture(normTex, uvs.top_right).xyz;
|
||||
vec3 n_bl = texture(normTex, uvs.bottom_left).xyz;
|
||||
vec3 n_br = texture(normTex, uvs.bottom_right).xyz;
|
||||
|
||||
vec3 normalFiniteDifference0 = n_tr - n_bl;
|
||||
vec3 normalFiniteDifference1 = n_tl - n_br;
|
||||
vec3 normalFiniteDifference2 = n_l - n_r;
|
||||
vec3 normalFiniteDifference3 = n_u - n_d;
|
||||
|
||||
float edgeNormal = sqrt(
|
||||
dot(normalFiniteDifference0, normalFiniteDifference0) +
|
||||
dot(normalFiniteDifference1, normalFiniteDifference1) +
|
||||
dot(normalFiniteDifference2, normalFiniteDifference2) +
|
||||
dot(normalFiniteDifference3, normalFiniteDifference3)
|
||||
);
|
||||
|
||||
return smoothstep(normal_threshold - normal_smoothing, normal_threshold + normal_smoothing, edgeNormal);
|
||||
}
|
||||
#endif//SUPPORT_MOBILE
|
||||
|
||||
void vertex() {
|
||||
POSITION = vec4(VERTEX, 1.0);
|
||||
|
||||
#ifdef SUPPORT_MOBILE
|
||||
model_view_matrix = INV_VIEW_MATRIX * mat4(VIEW_MATRIX[0],VIEW_MATRIX[1],VIEW_MATRIX[2],VIEW_MATRIX[3]);;
|
||||
#endif
|
||||
}
|
||||
|
||||
void fragment() {
|
||||
float aspect = float(VIEWPORT_SIZE.y) / float(VIEWPORT_SIZE.x);
|
||||
vec2 e = vec2(1./VIEWPORT_SIZE.xy)*1.0;
|
||||
|
||||
float depth_diff = 0.0;
|
||||
float neg_depth_diff = .5;
|
||||
|
||||
UVNeighbors n = getNeighbors(SCREEN_UV, max_thickness, aspect);
|
||||
NeighborDepthSamples depth_samples = getLinearDepthSamples(n, DEPTH_TEXTURE, INV_PROJECTION_MATRIX);
|
||||
vec2 depth_data = getDepth(SCREEN_UV, DEPTH_TEXTURE, INV_PROJECTION_MATRIX);
|
||||
float depth = depth_data.x;
|
||||
vec3 color = texture(SCREEN_TEXTURE, SCREEN_UV).rgb;
|
||||
vec3 c = vec3(0.0);
|
||||
|
||||
float min_d = getMinimumDepth(depth_samples);
|
||||
float thickness = clamp(remap(min_d, min_distance, max_distance, max_thickness, min_thickness), min_thickness, max_thickness);
|
||||
float fade_a = clamp(remap(min_d, min_distance, max_distance, 1.0, 0.0), 0.0, 1.0);
|
||||
vec2 min_depth_data = depth_data;
|
||||
float min_depth = 9999999.9;
|
||||
|
||||
n = getNeighbors(SCREEN_UV, thickness, aspect);
|
||||
depth_samples = getLinearDepthSamples(n, DEPTH_TEXTURE, INV_PROJECTION_MATRIX);
|
||||
|
||||
vec3 normal = texture(NORMAL_TEXTURE, SCREEN_UV).rgb * 2.0 - 1.0;
|
||||
|
||||
#ifndef SUPPORT_MOBILE
|
||||
vec3 pixel_normal = texture(NORMR_TEXTURE, SCREEN_UV).xyz;
|
||||
#else
|
||||
vec3 pixel_normal = reconstructWorldNormal(DEPTH_TEXTURE, model_view_matrix, INV_PROJECTION_MATRIX, SCREEN_UV, MODEL_MATRIX, INV_VIEW_MATRIX, VIEWPORT_SIZE.xy);
|
||||
#endif
|
||||
|
||||
float depthEdges = detectEdgesDepth(depth_samples, pixel_normal, VIEW);
|
||||
|
||||
#ifndef SUPPORT_MOBILE
|
||||
float normEdges = min(detectEdgesNormal(n, NORMR_TEXTURE, CAMERA_DIRECTION_WORLD), 1.0);
|
||||
#else
|
||||
float normEdges = min(detectEdgesNormalReconstructed(n, DEPTH_TEXTURE, model_view_matrix, INV_PROJECTION_MATRIX, SCREEN_UV, MODEL_MATRIX, INV_VIEW_MATRIX, VIEWPORT_SIZE.xy), 1.0);
|
||||
#endif
|
||||
|
||||
ALBEDO.rgb = outlineColor.rgb;
|
||||
ALPHA = max(depthEdges, normEdges) * outlineColor.a * fade_a;
|
||||
for (float x = -shadow_thickness; x <= shadow_thickness;x += 1.0){
|
||||
for (float y = -shadow_thickness; y <= shadow_thickness; y += 1.0){
|
||||
if ((x == 0.0 && y == 0.0) || (shadow_thickness*shadow_thickness < (x*x + y*y))){
|
||||
continue;
|
||||
}
|
||||
|
||||
vec2 du_data = getDepth(SCREEN_UV+1.0*vec2(x, y)*e, DEPTH_TEXTURE, INV_PROJECTION_MATRIX);
|
||||
vec2 dd_data = getDepth(SCREEN_UV+0.5*vec2(x, y)*e, DEPTH_TEXTURE, INV_PROJECTION_MATRIX);
|
||||
|
||||
float du = du_data.x;
|
||||
float dd = dd_data.x;
|
||||
|
||||
float dd_diff = clamp(abs((depth - dd) - (dd - du)), 0.0, 1.0);
|
||||
|
||||
float val = clamp(abs(depth - du), 0., 1.)/(x*x + y*y)*dd_diff*dd_diff*5000.0;
|
||||
|
||||
val = clamp(val, 0.0, 1.0);
|
||||
|
||||
depth_diff += val;
|
||||
|
||||
if (du < min_depth){
|
||||
min_depth = du;
|
||||
min_depth_data = du_data;
|
||||
c = texture(SCREEN_TEXTURE, SCREEN_UV+vec2(x, y)*e).rgb;
|
||||
|
||||
c *= clamp(0.5+ 0.5*dot(normalize(vec2(x, y)), (vec2(0.0, 1.0))), 0.0, 1.0);
|
||||
|
||||
}
|
||||
|
||||
vec3 nu = texture(NORMAL_TEXTURE, SCREEN_UV+vec2(x, y)*e).rgb * 2.0 - 1.0;
|
||||
|
||||
depth_diff += (1.0-abs(dot(nu, normal)))/max(min(dd, depth), 2.0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
depth_diff = smoothstep(0.2, 0.3, depth_diff);
|
||||
|
||||
vec3 final = c*shadow_color;
|
||||
ALBEDO = final;
|
||||
|
||||
float alpha_mask = depth_diff;
|
||||
DEPTH = min_depth_data.y*alpha_mask + depth_data.y*(1.0-alpha_mask);
|
||||
ALPHA = clamp((alpha_mask) * 5., 0., 1.);
|
||||
|
||||
}
|
||||
56
common/vfx/materials/shaders/3d_outline_2.gdshader
Normal file
56
common/vfx/materials/shaders/3d_outline_2.gdshader
Normal file
@@ -0,0 +1,56 @@
|
||||
shader_type spatial;
|
||||
render_mode blend_premul_alpha, unshaded, ambient_light_disabled;
|
||||
|
||||
uniform sampler2D depth_texture : hint_depth_texture;
|
||||
uniform int step_count : hint_range(3, 15, 2) = 3; // 2 samples per step
|
||||
uniform float thickness : hint_range(1.0, 16.0, 0.1) = 3.0;
|
||||
uniform vec3 edge_color : source_color = vec3(0.);
|
||||
uniform float fade_start : hint_range(1.0, 1000.0, 0.1) = 100.0;
|
||||
uniform float fade_length : hint_range(1.0, 1000.0, 0.1) = 200.0;
|
||||
|
||||
void fragment() {
|
||||
// Setup step parameters
|
||||
vec2 step_length = 1.0 / VIEWPORT_SIZE * thickness;
|
||||
float step_angle = TAU / float(step_count);
|
||||
// Per-pixel jitter to reduce patterning
|
||||
float start_angle = fract(sin(dot(SCREEN_UV, vec2(12.9898, 78.233))) * 43758.5453) * TAU;
|
||||
vec2 dir = vec2(cos(start_angle), sin(start_angle));
|
||||
// step rotation matrix
|
||||
mat2 rot = mat2(
|
||||
vec2(cos(step_angle), -sin(step_angle)),
|
||||
vec2(sin(step_angle), cos(step_angle)));
|
||||
vec3 avg_dx = vec3(0.0);
|
||||
vec3 avg_dy = vec3(0.0);
|
||||
// save closest pixel to uniformly fade line.
|
||||
float min_z = 1e6;
|
||||
// Sample and average derivatives for all pairs
|
||||
for (int i = 0; i < step_count; i++) {
|
||||
vec2 uv1 = SCREEN_UV + dir * step_length;
|
||||
vec2 uv2 = SCREEN_UV - dir * step_length;
|
||||
float d1 = texture(depth_texture, uv1).r;
|
||||
float d2 = texture(depth_texture, uv2).r;
|
||||
vec4 up1 = INV_PROJECTION_MATRIX * vec4(uv1 * 2.0 - 1.0, d1, 1.0);
|
||||
vec4 up2 = INV_PROJECTION_MATRIX * vec4(uv2 * 2.0 - 1.0, d2, 1.0);
|
||||
vec3 p1 = up1.xyz / up1.w;
|
||||
vec3 p2 = up2.xyz / up2.w;
|
||||
min_z = min(min_z, min(-p1.z, -p2.z));
|
||||
vec3 diff = p1 - p2;
|
||||
avg_dx += diff * dir.x;
|
||||
avg_dy += diff * dir.y;
|
||||
|
||||
dir = rot * dir; // rotate direction for next step
|
||||
}
|
||||
// fade outline width with distance
|
||||
float distance_fade = 1e-4 + smoothstep(fade_start + fade_length, fade_start, min_z);
|
||||
|
||||
// Edge mask
|
||||
float edge = 1.0 - smoothstep(0.1, 0.15, dot(normalize(cross(avg_dy, avg_dx)), VIEW));
|
||||
|
||||
// Small vignette at screen edges
|
||||
edge *= smoothstep(0.00, 0.015 * thickness,
|
||||
1.0 - max(abs(SCREEN_UV.x - 0.5), abs(SCREEN_UV.y - 0.5)) * 2.0);
|
||||
|
||||
// blend_premul_alpha avoids need to sample screentexture.
|
||||
ALBEDO = edge_color * edge;
|
||||
ALPHA = edge * distance_fade;
|
||||
}
|
||||
1
common/vfx/materials/shaders/3d_outline_2.gdshader.uid
Normal file
1
common/vfx/materials/shaders/3d_outline_2.gdshader.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://n6r7u234l2iy
|
||||
@@ -1,62 +1,56 @@
|
||||
shader_type canvas_item;
|
||||
shader_type spatial;
|
||||
render_mode blend_premul_alpha, unshaded, ambient_light_disabled;
|
||||
|
||||
uniform vec4 color : source_color = vec4(1.0);
|
||||
uniform float width : hint_range(0, 10) = 1.0;
|
||||
uniform int pattern : hint_range(0, 2) = 0; // diamond, circle, square
|
||||
uniform bool inside = false;
|
||||
uniform bool add_margins = true; // only useful when inside is false
|
||||
|
||||
void vertex() {
|
||||
if (add_margins) {
|
||||
VERTEX += (UV * 2.0 - 1.0) * width;
|
||||
}
|
||||
}
|
||||
|
||||
bool hasContraryNeighbour(vec2 uv, vec2 texture_pixel_size, sampler2D texture) {
|
||||
for (float i = -ceil(width); i <= ceil(width); i++) {
|
||||
float x = abs(i) > width ? width * sign(i) : i;
|
||||
float offset;
|
||||
|
||||
if (pattern == 0) {
|
||||
offset = width - abs(x);
|
||||
} else if (pattern == 1) {
|
||||
offset = floor(sqrt(pow(width + 0.5, 2) - x * x));
|
||||
} else if (pattern == 2) {
|
||||
offset = width;
|
||||
}
|
||||
|
||||
for (float j = -ceil(offset); j <= ceil(offset); j++) {
|
||||
float y = abs(j) > offset ? offset * sign(j) : j;
|
||||
vec2 xy = uv + texture_pixel_size * vec2(x, y);
|
||||
|
||||
if ((xy != clamp(xy, vec2(0.0), vec2(1.0)) || texture(texture, xy).a <= 0.0) == inside) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
uniform sampler2D depth_texture : hint_depth_texture;
|
||||
uniform int step_count : hint_range(3, 15, 2) = 3; // 2 samples per step
|
||||
uniform float thickness : hint_range(1.0, 16.0, 0.1) = 3.0;
|
||||
uniform vec3 edge_color : source_color = vec3(0.);
|
||||
uniform float fade_start : hint_range(1.0, 1000.0, 0.1) = 100.0;
|
||||
uniform float fade_length : hint_range(1.0, 1000.0, 0.1) = 200.0;
|
||||
|
||||
void fragment() {
|
||||
vec2 uv = UV;
|
||||
|
||||
if (add_margins) {
|
||||
vec2 texture_pixel_size = vec2(1.0) / (vec2(1.0) / TEXTURE_PIXEL_SIZE + vec2(width * 2.0));
|
||||
|
||||
uv = (uv - texture_pixel_size * width) * TEXTURE_PIXEL_SIZE / texture_pixel_size;
|
||||
|
||||
if (uv != clamp(uv, vec2(0.0), vec2(1.0))) {
|
||||
COLOR.a = 0.0;
|
||||
} else {
|
||||
COLOR = texture(TEXTURE, uv);
|
||||
}
|
||||
} else {
|
||||
COLOR = texture(TEXTURE, uv);
|
||||
}
|
||||
|
||||
if ((COLOR.a > 0.0) == inside && hasContraryNeighbour(uv, TEXTURE_PIXEL_SIZE, TEXTURE)) {
|
||||
COLOR.rgb = inside ? mix(COLOR.rgb, color.rgb, color.a) : color.rgb;
|
||||
COLOR.a += (1.0 - COLOR.a) * color.a;
|
||||
// Setup step parameters
|
||||
vec2 step_length = 1.0 / VIEWPORT_SIZE * thickness;
|
||||
float step_angle = TAU / float(step_count);
|
||||
// Per-pixel jitter to reduce patterning
|
||||
float start_angle = fract(sin(dot(SCREEN_UV, vec2(12.9898, 78.233))) * 43758.5453) * TAU;
|
||||
vec2 dir = vec2(cos(start_angle), sin(start_angle));
|
||||
// step rotation matrix
|
||||
mat2 rot = mat2(
|
||||
vec2(cos(step_angle), -sin(step_angle)),
|
||||
vec2(sin(step_angle), cos(step_angle)));
|
||||
vec3 avg_dx = vec3(0.0);
|
||||
vec3 avg_dy = vec3(0.0);
|
||||
// save closest pixel to uniformly fade line.
|
||||
float min_z = 1e6;
|
||||
// Sample and average derivatives for all pairs
|
||||
for (int i = 0; i < step_count; i++) {
|
||||
vec2 uv1 = SCREEN_UV + dir * step_length;
|
||||
vec2 uv2 = SCREEN_UV - dir * step_length;
|
||||
float d1 = texture(depth_texture, uv1).r;
|
||||
float d2 = texture(depth_texture, uv2).r;
|
||||
vec4 up1 = INV_PROJECTION_MATRIX * vec4(uv1 * 2.0 - 1.0, d1, 1.0);
|
||||
vec4 up2 = INV_PROJECTION_MATRIX * vec4(uv2 * 2.0 - 1.0, d2, 1.0);
|
||||
vec3 p1 = up1.xyz / up1.w;
|
||||
vec3 p2 = up2.xyz / up2.w;
|
||||
min_z = min(min_z, min(-p1.z, -p2.z));
|
||||
vec3 diff = p1 - p2;
|
||||
avg_dx += diff * dir.x;
|
||||
avg_dy += diff * dir.y;
|
||||
|
||||
dir = rot * dir; // rotate direction for next step
|
||||
}
|
||||
// fade outline width with distance
|
||||
float distance_fade = 1e-4 + smoothstep(fade_start + fade_length, fade_start, min_z);
|
||||
|
||||
// Edge mask
|
||||
float edge = 1.0 - smoothstep(0.1, 0.15, dot(normalize(cross(avg_dy, avg_dx)), VIEW));
|
||||
|
||||
// Small vignette at screen edges
|
||||
edge *= smoothstep(0.00, 0.015 * thickness,
|
||||
1.0 - max(abs(SCREEN_UV.x - 0.5), abs(SCREEN_UV.y - 0.5)) * 2.0);
|
||||
|
||||
// blend_premul_alpha avoids need to sample screentexture.
|
||||
ALBEDO = edge_color * edge;
|
||||
ALPHA = edge * distance_fade;
|
||||
}
|
||||
19
common/vfx/materials/shaders/pixelisation.gdshader
Normal file
19
common/vfx/materials/shaders/pixelisation.gdshader
Normal file
@@ -0,0 +1,19 @@
|
||||
shader_type spatial;
|
||||
|
||||
render_mode unshaded, fog_disabled;
|
||||
|
||||
uniform sampler2D screen_texture : hint_screen_texture;
|
||||
uniform float pixel_size : hint_range(0.0, 100., 2.0) = 8.0;
|
||||
|
||||
void fragment() {
|
||||
vec2 tex_size = vec2(textureSize(screen_texture, 0));
|
||||
vec2 pixel_count = tex_size / pixel_size;
|
||||
vec2 pixelated_uv = floor(SCREEN_UV * pixel_count) / pixel_count;
|
||||
vec4 color = texture(screen_texture, pixelated_uv);
|
||||
ALBEDO = color.rgb;
|
||||
}
|
||||
|
||||
//void light() {
|
||||
// // Called for every pixel for every light affecting the material.
|
||||
// // Uncomment to replace the default light processing function with this one.
|
||||
//}
|
||||
1
common/vfx/materials/shaders/pixelisation.gdshader.uid
Normal file
1
common/vfx/materials/shaders/pixelisation.gdshader.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://c3gjvpai3w7cu
|
||||
Reference in New Issue
Block a user