<postprocess>, fix tab open/close, fix some URL bugs

This commit is contained in:
Face
2025-08-30 14:19:03 +03:00
parent 9344c7818a
commit 322cb4072e
59 changed files with 1144 additions and 17 deletions

236
docs/docs/postprocess.md Normal file
View File

@@ -0,0 +1,236 @@
---
sidebar_position: 5
---
# Postprocess
The `<postprocess>` tag in Gurted allows you to apply real-time visual effects to your entire webpage through GPU-accelerated shaders. These effects are rendered as an overlay on top of your content, not affecting the browser UI.
## Usage
The postprocess tag supports three ways to define shader effects:
```html
<!-- Using built-in presets -->
<postprocess preset="crt" />
<!-- Loading external shader files -->
<postprocess src="custom-shader.gdshader" />
<!-- Inline shader code -->
<postprocess>
shader_type canvas_item;
void fragment() {
COLOR = texture(SCREEN_TEXTURE, SCREEN_UV);
}
</postprocess>
```
## Shader Tutorial
For documentation on how to write GDShader code, see the [Godot Shader Language documentation](https://docs.godotengine.org/en/stable/tutorials/shading/index.html).
### Parameters
HTML attributes are automatically converted to shader uniforms with the following supported types:
#### Numeric Types
```html
<!-- Float values -->
<postprocess preset="vignette" vignette_strength="1.5"></postprocess>
<!-- Integer values -->
<postprocess preset="snowfall" num_of_layers="25"></postprocess>
<!-- Boolean values -->
<postprocess preset="custom" enable_effect="true"></postprocess>
```
#### Vector Types
```html
<!-- Vector2 -->
<postprocess preset="rblur" blur_center="Vector2(0.3, 0.7)"></postprocess>
<postprocess preset="rblur" blur_center="vec2(0.3, 0.7)"></postprocess>
<!-- Vector3 -->
<postprocess preset="lensflare" tint="Vector3(1.4, 1.2, 1.0)"></postprocess>
<postprocess preset="lensflare" tint="vec3(1.4, 1.2, 1.0)"></postprocess>
<!-- Vector4 -->
<postprocess preset="vignette" vignette_color="Vector4(0.0, 0.0, 0.0, 1.0)"></postprocess>
<postprocess preset="vignette" vignette_color="vec4(0.0, 0.0, 0.0, 1.0)"></postprocess>
```
#### Color Values
```html
<!-- Hex colors -->
<postprocess preset="vignette" vignette_color="#ff0000"></postprocess>
<!-- Named colors work as strings -->
<postprocess preset="snowfall" snow_color="white"></postprocess>
```
## Built-in Preset Shaders
### CRT Monitor Effect
Creates a retro CRT monitor appearance with scanlines, distortion, and screen curvature.
```html
<postprocess preset="crt"></postprocess>
```
![CRT effect](../static/img/docs/crt.gif)
**Available Properties:**
- `curvature` (0.0-10.0, default: 2.0) - Screen curvature amount
- `skip` (0.0-1.0, default: 1.0) - Vertical hold distortion intensity
- `image_flicker` (0.0-1.0, default: 1.0) - Image flicker intensity
- `vignette_flicker_speed` (0.0-2.0, default: 1.0) - Vignette animation speed
- `vignette_strength` (0.0-2.0, default: 1.0) - Dark edge vignette intensity
- `small_scanlines_speed` (0.0-10.0, default: 1.0) - Fine scanline animation speed
- `small_scanlines_proximity` (0.01-2.0, default: 1.0) - Fine scanline density
- `small_scanlines_opacity` (0.01-5.0, default: 1.0) - Fine scanline visibility
- `scanlines_opacity` (0.0-2.0, default: 1.0) - Main scanline visibility
- `scanlines_speed` (0.0-5.0, default: 1.0) - Main scanline animation speed
- `scanline_thickness` (0.0-0.6, default: 0.5) - Scanline thickness
- `scanlines_spacing` (0.3-3.0, default: 1.0) - Space between scanlines
### Film Grain Effect
Adds vintage film grain noise to give content an analog film appearance.
```html
<postprocess preset="film"></postprocess>
```
![Film grain effect](../static/img/docs/film.png)
**Available Properties:**
- `grain_amount` (0.0-1.0, default: 0.05) - Amount of grain noise added
- `grain_size` (0.1-10.0, default: 1.0) - Size/scale of individual grain particles
### Vignette Effect
Creates a darkened border around the screen edges, focusing attention on the center.
```html
<postprocess preset="vignette"></postprocess>
```
![Vignette effect](../static/img/docs/vignette.png)
**Available Properties:**
- `inner_radius` (0.0-1.0, default: 0.1) - Inner edge of the vignette effect
- `outer_radius` (0.0-1.0, default: 1.0) - Outer edge where vignette is strongest
- `vignette_strength` (0.0-2.0, default: 1.0) - Intensity of the darkening effect
- `dither_strength` (0.0-1.0, default: 0.03) - Noise added to prevent banding
- `vignette_color` (color, default: black) - Color of the vignette overlay
### Pencil Effect
Converts the webpage into a pencil drawing style with adjustable thresholds for line detection.
```html
<postprocess preset="pencil"></postprocess>
```
![Pencil effect](../static/img/docs/pencil.png)
**Available Properties:**
- `u_bgColorFactor` (0.0-1.0, default: 0.4) - Blend amount of background color
- `u_threshold1` (0.0-1.0, default: 0.75) - Brightest areas threshold
- `u_threshold2` (0.0-1.0, default: 0.50) - Medium-bright areas threshold
- `u_threshold3` (0.0-1.0, default: 0.25) - Medium-dark areas threshold
- `u_threshold4` (0.0-1.0, default: 0.05) - Darkest areas threshold
- `u_bgTiling` (Vector2, default: vec2(1.0, 1.0)) - Background texture tiling
- `u_patternTiling` (Vector2, default: vec2(1.0, 1.0)) - Pattern texture tiling
- `u_bgColor` (color, default: white) - Background paper color
- `u_patternColor` (color, default: black) - Pencil line color
### Snowfall Effect
Adds animated falling snow particles over the content with customizable layers and physics.
```html
<postprocess preset="snowfall"></postprocess>
```
![Snowfall effect](../static/img/docs/snowfall.gif)
**Available Properties:**
- `spread` (0.0-1.5, default: 0.5) - Horizontal spread of snowflakes
- `size` (0.01-5.0, default: 0.5) - Size of individual snowflakes
- `snow_color` (color, default: white) - Color of the snow particles
- `snow_transparency` (-0.5-1.0, default: 0.2) - Transparency of snow overlay
- `speed` (0.0-10.0, default: 0.5) - Falling speed of snowflakes
- `wind` (-2.0-2.0, default: 0.0) - Horizontal wind effect
- `num_of_layers` (integer, default: 40) - Number of snow layers for depth
### Chromatic Aberration Effect
Simulates lens distortion by separating RGB color channels, creating a retro video glitch effect.
```html
<postprocess preset="chrome"></postprocess>
```
![Chrome effect](../static/img/docs/chrome.png)
**Available Properties:**
- `levels` (integer, default: 3) - Number of color separation levels
- `spread` (float, default: 0.01) - Amount of color channel separation
### Radial Blur Effect
Creates motion blur radiating from a center point, useful for speed effects or focus attention.
```html
<postprocess preset="rblur"></postprocess>
```
![Radial blur effect](../static/img/docs/rblur.png)
**Available Properties:**
- `blur_center` (Vector2, default: vec2(0.5, 0.5)) - Center point of the blur effect
- `blur_power` (0.0-1.0, default: 0.01) - Intensity of the blur effect
- `sampling_count` (1-64, default: 2) - Quality of the blur (higher = smoother)
### Lens Flare Effect
Adds realistic lens flare effects, simulating light hitting a camera lens.
```html
<postprocess preset="lensflare"></postprocess>
```
![Lensflare effect](../static/img/docs/lensflare.png)
**Available Properties:**
- `sun_position` (Vector2, default: vec2(400.0, 0.0)) - Position of the light source
- `tint` (Vector3, default: vec3(1.4, 1.2, 1.0)) - Color tint of the lens flare
### Foliage Sway Effect
Creates animated wave distortion effects, mimicking vegetation movement or underwater effects.
```html
<postprocess preset="foliage"></postprocess>
```
![Foliage effect](../static/img/docs/foliage.gif)
**Available Properties:**
- `x_intensity` (float, default: 3.0) - Horizontal sway intensity
- `y_intensity` (float, default: 0.5) - Vertical sway intensity
- `offset` (float, default: 0.0) - Animation offset for timing
- `speed` (0-20, default: 2.0) - Animation speed
- `wave_frequency` (0-100, default: 20) - Frequency of wave oscillations
- `wave_length` (50-800, default: 200.0) - Length of wave patterns
### Dithering Effect
Applies retro pixel art dithering with color palette reduction for a vintage gaming aesthetic.
```html
<postprocess preset="dither"></postprocess>
```
![Dither effect](../static/img/docs/dither.png)
:::note
Due to the limitations of GDShader, the dithering effect relies on a `GradientTexture` node internally. This means the colors are hardcoded to be a rusty tint - unchangeable via parameters. A similar effect can be achieved with a custom `<postprocess>` shader without external nodes - however, that's beyond the scope of this preset.
:::
**Available Properties:**
- `pixel` (float, default: 1.0) - Pixel size for the dithering pattern
All postprocess shaders are GPU-accelerated thanks to Godot's shader system.

View File

@@ -31,7 +31,6 @@ const sidebars: SidebarsConfig = {
items: [
'dns-system',
'flumi-browser',
'search-engine'
],
},
{
@@ -40,6 +39,7 @@ const sidebars: SidebarsConfig = {
items: [
'html',
'css',
'postprocess',
],
},
],

BIN
docs/static/img/docs/chrome.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 686 KiB

BIN
docs/static/img/docs/crt.gif vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 MiB

BIN
docs/static/img/docs/dither.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 426 KiB

BIN
docs/static/img/docs/film.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 117 KiB

BIN
docs/static/img/docs/foliage.gif vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 MiB

BIN
docs/static/img/docs/lensflare.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 406 KiB

BIN
docs/static/img/docs/pencil.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 MiB

BIN
docs/static/img/docs/rblur.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 438 KiB

BIN
docs/static/img/docs/snowfall.gif vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 MiB

BIN
docs/static/img/docs/vignette.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 327 B

View File

@@ -0,0 +1,34 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://c8eervlu44g5e"
path="res://.godot/imported/bayer8tile4.png-af05367272eb1e9c5c1494aea351d772.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://Assets/Textures/bayer8tile4.png"
dest_files=["res://.godot/imported/bayer8tile4.png-af05367272eb1e9c5c1494aea351d772.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
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/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

Binary file not shown.

After

Width:  |  Height:  |  Size: 323 KiB

View File

@@ -0,0 +1,34 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://bh6ou8qfqhml3"
path="res://.godot/imported/blue_noise.png-3429c5fff13cc3510f3a6adb3d9b290a.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://Assets/Textures/blue_noise.png"
dest_files=["res://.godot/imported/blue_noise.png-3429c5fff13cc3510f3a6adb3d9b290a.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
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/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

Binary file not shown.

After

Width:  |  Height:  |  Size: 616 B

View File

@@ -0,0 +1,34 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://crdyaxcy0l5fe"
path="res://.godot/imported/dither_matrix.png-e5f72ab3b733bd12bb9be69cb29c5303.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://Assets/Textures/dither_matrix.png"
dest_files=["res://.godot/imported/dither_matrix.png-e5f72ab3b733bd12bb9be69cb29c5303.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
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/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

Binary file not shown.

After

Width:  |  Height:  |  Size: 709 KiB

View File

@@ -0,0 +1,34 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://b8x712hkjbsh"
path="res://.godot/imported/parchment.jpg-ea29c24066df61d46bc7b0bcc25a86c1.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://Assets/Textures/parchment.jpg"
dest_files=["res://.godot/imported/parchment.jpg-ea29c24066df61d46bc7b0bcc25a86c1.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
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/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

Binary file not shown.

After

Width:  |  Height:  |  Size: 363 KiB

View File

@@ -0,0 +1,34 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://dnvdbk47n56ej"
path="res://.godot/imported/pencil.jpg-3bb1b8082faf74808f54e9dd81d1b7d0.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://Assets/Textures/pencil.jpg"
dest_files=["res://.godot/imported/pencil.jpg-3bb1b8082faf74808f54e9dd81d1b7d0.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
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/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

View File

@@ -0,0 +1,6 @@
[gd_scene load_steps=2 format=3 uid="uid://bpn7d4x5qr8vw"]
[ext_resource type="Script" path="res://Scripts/Tags/postprocess.gd" id="1_postprocess"]
[node name="postprocess" type="Control"]
script = ExtResource("1_postprocess")

View File

@@ -430,6 +430,9 @@ func get_all_images() -> Array[String]:
func get_all_scripts() -> Array[String]:
return get_attribute_values("script", "src")
func get_all_postprocess() -> Array[String]:
return get_attribute_values("postprocess", "src")
func process_scripts(lua_api: LuaAPI, _lua_vm) -> void:
if not lua_api:
print("Warning: Lua API not available for script processing")
@@ -459,6 +462,14 @@ func process_external_scripts(lua_api: LuaAPI, _lua_vm, base_url: String = "") -
if not script_content.is_empty():
lua_api.execute_lua_script(script_content)
func process_postprocess() -> HTMLParser.HTMLElement:
var postprocess_elements = find_all("postprocess")
if postprocess_elements.is_empty():
return null
# Return the last postprocess element (last defined wins)
return postprocess_elements[-1]
func get_all_stylesheets() -> Array[String]:
return get_attribute_values("style", "src")

View File

@@ -672,7 +672,7 @@ func _handle_dom_operation(operation: Dictionary):
"get_text":
_handle_text_getting(operation)
"append_element":
LuaDOMUtils.handle_element_append(operation, dom_parser, self)
LuaDOMUtils.handle_element_append(operation, dom_parser)
"add_class":
LuaClassListUtils.handle_add_class(operation, dom_parser)
"remove_class":
@@ -682,7 +682,7 @@ func _handle_dom_operation(operation: Dictionary):
"remove_element":
LuaDOMUtils.handle_element_remove(operation, dom_parser)
"insert_before":
LuaDOMUtils.handle_insert_before(operation, dom_parser, self)
LuaDOMUtils.handle_insert_before(operation, dom_parser)
"insert_after":
LuaDOMUtils.handle_insert_after(operation, dom_parser)
"replace_child":

View File

@@ -9,7 +9,9 @@ static func is_gurt_domain(url: String) -> bool:
return true
if not url.contains("://"):
var parts = url.split(".")
# Extract just the domain part (before any path)
var domain = url.split("/")[0]
var parts = domain.split(".")
return parts.size() == 2
return false

View File

@@ -64,19 +64,33 @@ static func apply_element_styles(node: Control, element: HTMLParser.HTMLElement,
if (width != null or height != null) and not skip_sizing:
if width != null:
if width is String and width == "100%":
node.size_flags_horizontal = Control.SIZE_EXPAND_FILL
node.custom_minimum_size.x = 0
if width is String and width.ends_with("%"):
if width == "100%":
node.size_flags_horizontal = Control.SIZE_EXPAND_FILL
node.custom_minimum_size.x = 0
else:
# For other percentages, convert to viewport-relative size
var percent = float(width.replace("%", "")) / 100.0
var viewport_width = node.get_viewport().get_visible_rect().size.x if node.get_viewport() else 800
node.custom_minimum_size.x = viewport_width * percent
node.set_meta("size_flags_set_by_style_manager", true)
node.size_flags_horizontal = Control.SIZE_SHRINK_BEGIN
else:
node.custom_minimum_size.x = width
node.size_flags_horizontal = Control.SIZE_SHRINK_BEGIN
if height != null:
if height is String and height == "100%":
node.size_flags_vertical = Control.SIZE_EXPAND_FILL
node.custom_minimum_size.y = 0
if height is String and height.ends_with("%"):
if height == "100%":
node.size_flags_vertical = Control.SIZE_EXPAND_FILL
node.custom_minimum_size.y = 0
else:
# For other percentages, convert to viewport-relative size
var percent = float(height.replace("%", "")) / 100.0
var viewport_height = node.get_viewport().get_visible_rect().size.y if node.get_viewport() else 600
node.custom_minimum_size.y = viewport_height * percent
node.set_meta("size_flags_set_by_style_manager", true)
node.size_flags_vertical = Control.SIZE_SHRINK_BEGIN
else:
node.custom_minimum_size.y = height
node.size_flags_vertical = Control.SIZE_SHRINK_BEGIN

View File

@@ -210,7 +210,9 @@ func create_tab() -> void:
var index = tabs.size();
var tab = TAB.instantiate()
tabs.append(tab)
h_box_container.add_child(tab)
tab.tab_pressed.connect(_tab_pressed.bind(index))
tab.tab_closed.connect(_tab_closed.bind(index))
var viewport_width = get_viewport().get_visible_rect().size.x
var available_width = viewport_width - POPUP_BUTTON_WIDTH - NEW_TAB_BUTTON_WIDTH - OTHER_UI_PADDING
var visible_count = calculate_visible_tab_count(available_width)

View File

@@ -0,0 +1,162 @@
class_name HTMLPostprocess
extends Control
const Network = preload("res://Scripts/Network.gd")
var element: HTMLParser.HTMLElement
var parser: HTMLParser
var shader_material: ShaderMaterial
var built_in_shaders = {
"crt": "res://Shaders/crt.gdshader",
"film": "res://Shaders/film.gdshader",
"vignette": "res://Shaders/vignette.gdshader",
"pencil": "res://Shaders/pencil.gdshader",
"snowfall": "res://Shaders/snowfall.gdshader",
"chrome": "res://Shaders/chrome.gdshader",
"rblur": "res://Shaders/rblur.gdshader",
"lensflare": "res://Shaders/lensflare.gdshader",
"foliage": "res://Shaders/foliage.gdshader",
"dither": "res://Shaders/dither.gdshader"
}
func init(element_: HTMLParser.HTMLElement, parser_: HTMLParser):
element = element_
parser = parser_
var preset = element.get_attribute("preset")
if preset and built_in_shaders.has(preset):
await apply_preset_shader(preset)
elif element.has_attribute("src"):
await load_external_shader()
elif not element.text_content.is_empty():
apply_inline_shader()
func apply_preset_shader(preset_name: String):
var shader_resource = built_in_shaders[preset_name]
var shader = load(shader_resource)
shader_material = ShaderMaterial.new()
shader_material.shader = shader
shader_material.set_shader_parameter("u_bgTexture", load("res://Assets/Textures/parchment.jpg"))
shader_material.set_shader_parameter("u_patternTexture", load("res://Assets/Textures/pencil.jpg"))
var BLUE_NOISE = load("res://Assets/Textures/blue_noise.png")
shader_material.set_shader_parameter("uTexBlueNoise", BLUE_NOISE)
var gradient_texture = GradientTexture2D.new()
var gradient = Gradient.new()
gradient.colors = [Color("#8B4513"), Color("#CD853F"), Color("#D2691E"), Color("#B22222"), Color("#A0522D")]
gradient_texture.gradient = gradient
shader_material.set_shader_parameter("pallete", gradient_texture)
apply_shader_uniforms()
apply_postprocessing_to_viewport()
func load_external_shader():
var src = element.get_attribute("src")
var main = Engine.get_main_loop().current_scene
var resolved_url = URLUtils.resolve_url(main.current_domain, src)
var network = Network.new()
var response = await network.fetch_content(resolved_url)
if response.success:
var shader_code = response.content.get_string_from_utf8()
var shader = Shader.new()
shader.code = shader_code
shader_material = ShaderMaterial.new()
shader_material.shader = shader
apply_shader_uniforms()
apply_postprocessing_to_viewport()
else:
print("Failed to load shader from: ", resolved_url)
func apply_inline_shader():
var shader_code = element.text_content.strip_edges()
if shader_code.is_empty():
return
var shader = Shader.new()
shader.code = shader_code
shader_material = ShaderMaterial.new()
shader_material.shader = shader
apply_shader_uniforms()
apply_postprocessing_to_viewport()
func apply_shader_uniforms():
if not shader_material or not shader_material.shader:
return
for attr_name in element.attributes:
if attr_name in ["src", "preset"]:
continue
var attr_value = element.get_attribute(attr_name)
var uniform_value = parse_uniform_value(attr_value)
if uniform_value != null:
shader_material.set_shader_parameter(attr_name, uniform_value)
func parse_uniform_value(value_string: String):
if value_string.is_valid_float():
return value_string.to_float()
if value_string.is_valid_int():
return value_string.to_int()
if value_string.to_lower() == "true":
return true
elif value_string.to_lower() == "false":
return false
if value_string.begins_with("Vector2(") or value_string.begins_with("vec2("):
var vec_str = value_string.replace("Vector2(", "").replace("vec2(", "").replace(")", "")
var components = vec_str.split(",")
if components.size() == 2:
return Vector2(components[0].strip_edges().to_float(), components[1].strip_edges().to_float())
if value_string.begins_with("Vector3(") or value_string.begins_with("vec3("):
var vec_str = value_string.replace("Vector3(", "").replace("vec3(", "").replace(")", "")
var components = vec_str.split(",")
if components.size() == 3:
return Vector3(components[0].strip_edges().to_float(), components[1].strip_edges().to_float(), components[2].strip_edges().to_float())
if value_string.begins_with("Vector4(") or value_string.begins_with("vec4("):
var vec_str = value_string.replace("Vector4(", "").replace("vec4(", "").replace(")", "")
var components = vec_str.split(",")
if components.size() == 4:
return Vector4(components[0].strip_edges().to_float(), components[1].strip_edges().to_float(), components[2].strip_edges().to_float(), components[3].strip_edges().to_float())
if value_string.begins_with("#"):
return Color(value_string)
return value_string
func apply_postprocessing_to_viewport():
if not shader_material:
return
var main_scene = Engine.get_main_loop().current_scene
var active_tab = main_scene.get_active_tab()
var panel_container = active_tab.background_panel
var existing_overlay = panel_container.get_node_or_null("PostprocessOverlay")
if existing_overlay:
existing_overlay.queue_free()
var overlay_rect = ColorRect.new()
overlay_rect.name = "PostprocessOverlay"
overlay_rect.material = shader_material
overlay_rect.color = Color.WHITE
overlay_rect.mouse_filter = Control.MOUSE_FILTER_IGNORE
overlay_rect.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT)
overlay_rect.z_index = 100
panel_container.add_child(overlay_rect)

View File

@@ -0,0 +1 @@
uid://btm4uqcfr43cm

View File

@@ -95,7 +95,7 @@ static func get_element_last_child_handler(vm: LuauVM, dom_parser: HTMLParser, l
# DOM Manipulation Methods
static func handle_element_append(operation: Dictionary, dom_parser: HTMLParser, lua_api) -> void:
static func handle_element_append(operation: Dictionary, dom_parser: HTMLParser) -> void:
var parent_id: String = operation.parent_id
var child_id: String = operation.child_id
@@ -153,7 +153,7 @@ static func handle_element_remove(operation: Dictionary, dom_parser: HTMLParser)
if all_elements_index >= 0:
dom_parser.parse_result.all_elements.remove_at(all_elements_index)
static func handle_insert_before(operation: Dictionary, dom_parser: HTMLParser, lua_api) -> void:
static func handle_insert_before(operation: Dictionary, dom_parser: HTMLParser) -> void:
var parent_id: String = operation.parent_id
var new_child_id: String = operation.new_child_id
var reference_child_id: String = operation.reference_child_id

View File

@@ -472,7 +472,9 @@ func _set_interval_handler(vm: LuauVM) -> int:
func get_current_href() -> String:
var main_node = Engine.get_main_loop().current_scene
return main_node.current_domain
if main_node:
return main_node.current_domain
return ""
func _gurt_select_handler(vm: LuauVM) -> int:
var selector: String = vm.luaL_checkstring(1)

View File

@@ -29,15 +29,15 @@ const SELECT = preload("res://Scenes/Tags/select.tscn")
const TEXTAREA = preload("res://Scenes/Tags/textarea.tscn")
const DIV = preload("res://Scenes/Tags/div.tscn")
const AUDIO = preload("res://Scenes/Tags/audio.tscn")
const POSTPROCESS = preload("res://Scenes/Tags/postprocess.tscn")
const MIN_SIZE = Vector2i(750, 200)
var font_dependent_elements: Array = []
var current_domain = ""
func should_group_as_inline(element: HTMLParser.HTMLElement) -> bool:
# Don't group inputs unless they're inside a form
if element.tag_name == "input":
# Check if this element has a form ancestor
var parent = element.parent
while parent:
if parent.tag_name == "form":
@@ -60,8 +60,6 @@ func _ready():
call_deferred("render")
var current_domain = "" # Store current domain for display
func resolve_url(href: String) -> String:
return URLUtils.resolve_url(current_domain, href)
@@ -193,6 +191,20 @@ func render_content(html_bytes: PackedByteArray) -> void:
remove_child(lua_api)
lua_api.queue_free()
active_tab.lua_apis.clear()
var existing_postprocess = []
for child in get_children():
if child is HTMLPostprocess:
existing_postprocess.append(child)
for postprocess in existing_postprocess:
remove_child(postprocess)
postprocess.queue_free()
if active_tab.background_panel:
var existing_overlay = active_tab.background_panel.get_node_or_null("PostprocessOverlay")
if existing_overlay:
existing_overlay.queue_free()
else:
var existing_lua_apis = []
for child in get_children():
@@ -204,6 +216,20 @@ func render_content(html_bytes: PackedByteArray) -> void:
remove_child(lua_api)
lua_api.queue_free()
var postprocess_nodes: Array[Node] = []
for child in get_children():
if child is HTMLPostprocess:
postprocess_nodes.append(child)
for node in postprocess_nodes:
remove_child(node)
node.queue_free()
var default_panel = website_container.get_parent()
if default_panel and default_panel.has_method("get_node_or_null"):
var existing_overlay = default_panel.get_node_or_null("PostprocessOverlay")
if existing_overlay:
existing_overlay.queue_free()
if target_container.get_parent() and target_container.get_parent().name == "BodyMarginContainer":
var body_margin_container = target_container.get_parent()
var scroll_container = body_margin_container.get_parent()
@@ -320,6 +346,12 @@ func render_content(html_bytes: PackedByteArray) -> void:
if parse_result.external_scripts and not parse_result.external_scripts.is_empty():
await parser.process_external_scripts(lua_api, null, current_domain)
var postprocess_element = parser.process_postprocess()
if postprocess_element:
var postprocess_node = POSTPROCESS.instantiate()
add_child(postprocess_node)
await postprocess_node.init(postprocess_element, parser)
active_tab.current_url = current_domain
active_tab.has_content = true

View File

@@ -0,0 +1,29 @@
// Chromatic Aberration Shader by https://godotshaders.com/shader/chromatic-abberation/
// All credits to the original author!!!
shader_type canvas_item;
uniform sampler2D SCREEN_TEXTURE : hint_screen_texture, filter_linear_mipmap;
uniform int levels = 3;
uniform float spread = 0.01;
vec3 chromatic_slice(float t){
vec3 res = vec3(1.0-t, 1.0 - abs(t - 1.0), t - 1.0);
return max(res, 0.0);
}
void fragment(){
vec3 sum;
COLOR.rgb = vec3(0);
vec2 offset = (UV - vec2(0.5))*vec2(1,-1);
for(int i = 0; i < levels; i++){
float t = 2.0*float(i)/float(levels-1); // range 0.0->2.0
vec3 slice = vec3(1.0-t, 1.0 - abs(t - 1.0), t - 1.0);
slice = max(slice, 0.0);
sum += slice;
vec2 slice_offset = (t-1.0)*spread*offset;
COLOR.rgb += slice * texture(SCREEN_TEXTURE, SCREEN_UV + slice_offset).rgb;
}
COLOR.rgb /= sum;
}

View File

@@ -0,0 +1 @@
uid://dbkni4dqt0ral

View File

@@ -0,0 +1,90 @@
/* License CC BY-NC-SA 4.0 Deed */
/* https://creativecommons.org/licenses/by-nc-sa/4.0/ */
/* Fork of Ryk's VCR distortion shader */
/* https://www.shadertoy.com/view/ldjGzV */
shader_type canvas_item;
uniform sampler2D screen_texture: hint_screen_texture, filter_linear_mipmap, repeat_disable;
group_uniforms Image;
uniform float curvature: hint_range(0., 10., .01) = 2.;
uniform float skip: hint_range(0., 1., .01) = 1.;
uniform float image_flicker: hint_range(0., 1., .01) = 1.;
group_uniforms Vignette;
uniform float vignette_flicker_speed: hint_range(0., 2., .01) = 1.;
uniform float vignette_strength: hint_range(0., 2., 0.01) = 1.;
group_uniforms Scanlines;
uniform float small_scanlines_speed: hint_range(0., 10., .01) = 1.;
uniform float small_scanlines_proximity: hint_range(.01, 2., .01) = 1.;
uniform float small_scanlines_opacity: hint_range(0.01, 5., .01) = 1.;
uniform float scanlines_opacity: hint_range(0., 2., .01) = 1.;
uniform float scanlines_speed: hint_range(0., 5., .01) = 1.;
uniform float scanline_thickness: hint_range(0., .6, .01) = 0.5;
uniform float scanlines_spacing: hint_range(0.3, 3., .01) = 1.;
group_uniforms Noise;
uniform sampler2D noise_texture: filter_linear_mipmap, repeat_enable;
float noise(vec2 p, vec2 uv)
{
float s = texture(noise_texture,vec2(1.*TIME,2.*TIME)*8. + p*1.).x;
s *= s;
return s;
}
float onOff(float a, float b, float c)
{
return step(c, sin(TIME + a*cos(TIME*b)));
}
float ramp(float y, float start, float end)
{
float inside = step(start,y) - step(end,y);
float fact = (y-start)/(end-start)*inside;
return (1.-fact) * inside;
}
float stripes(vec2 uv)
{
float noi = noise(uv*vec2(0.5,1.) + vec2(1.,3.), uv)*scanlines_opacity;
return ramp(mod(uv.y*4.*scanlines_spacing + TIME*scanlines_speed/(2.*scanlines_spacing)+sin(TIME*scanlines_speed + sin(TIME*scanlines_speed*0.63*scanlines_spacing)),1.),scanline_thickness,.6)*noi;
}
vec3 getVideo(vec2 uv)
{
vec2 look = uv;
float window = 1./(1.+20.*(look.y-mod(TIME/4.,1.))*(look.y-mod(TIME/4.,1.)))*image_flicker;
look.x = look.x + sin(look.y*10. + TIME)/50.*onOff(4.,4.,.3)*(1.+cos(TIME*80.))*window;
float vShift = 0.4*onOff(2.,3.,.9)*(sin(TIME)*sin(TIME*20.)+(0.5 + 0.1*sin(TIME*200.)*cos(TIME)))*skip;
look.y = mod(look.y + vShift, 1.);
vec3 video = texture(screen_texture,look).xyz;
return video;
}
vec2 screenDistort(vec2 uv)
{
uv -= vec2(.5,.5);
uv = uv*1.2*(1./1.2+curvature*uv.x*uv.x*uv.y*uv.y);
uv += vec2(.5,.5);
return uv;
}
void fragment()
{
vec2 uv = FRAGCOORD.xy / (1.0 / SCREEN_PIXEL_SIZE).xy;
uv = screenDistort(uv);
vec3 video = getVideo(uv);
float vigAmt = 3.+.3*sin(TIME*vignette_flicker_speed+1. + 5.*cos(TIME*5.*vignette_flicker_speed+1.));
vigAmt *= vignette_strength;
float vignette = (1.-vigAmt*(uv.y-.5)*(uv.y-.5))*(1.-vigAmt*(uv.x-.5)*(uv.x-.5));
video += stripes(uv);
video += noise(uv*2., uv)/2.;
video *= vignette;
video *= (12./small_scanlines_opacity+mod(uv.y*30.*small_scanlines_proximity+TIME*small_scanlines_speed,1.))/13.*small_scanlines_opacity;
COLOR = vec4(video,1.0);
}

View File

@@ -0,0 +1 @@
uid://db3f2xubcujnq

View File

@@ -0,0 +1,45 @@
//GODOT 4
shader_type canvas_item;
uniform float pixel = 1.0;
uniform sampler2D pallete;
uniform sampler2D SCREEN_TEXTURE : hint_screen_texture, filter_linear_mipmap;
const float bit = 6.0;
const mat4 bayer = mat4(
vec4(1.0, 9.0, 3.0, 11.0),
vec4(13.0, 5.0, 15.0, 7.0),
vec4(4.0, 12.0, 2.0, 10.0),
vec4(16.0, 8.0, 14.0, 6.0));
float getbayer(int x, int y)
{
return bayer[x][y];
}
void fragment() {
vec4 original = texture(TEXTURE, UV);
vec2 FUV = floor(FRAGCOORD.xy / pixel) / floor((1.0 / SCREEN_PIXEL_SIZE) / pixel);
vec4 _color = texture(SCREEN_TEXTURE, SCREEN_UV);
vec4 color = _color * texture(pallete, vec2(_color.r, 1.0));
float b = getbayer(int(FRAGCOORD.x) % 4, int(FRAGCOORD.y) % 4);// * 1.0) / 1.0;
vec2 uv = FRAGCOORD.xy / SCREEN_PIXEL_SIZE;
vec4 col_noise = color;
vec3 noise = vec3(fract(sin(dot(FUV, vec2(12.9898, 78.233))) * 43758.5453));
noise *= 0.1;
noise.xy *= (b / 16.0) * 1.5;
col_noise.rgb += noise; //noise effect
vec4 post = col_noise * floor(col_noise * 4.0) / 4.0; //noise + post-effect
COLOR *= post;
}

View File

@@ -0,0 +1 @@
uid://bggtn4uh67v0u

View File

@@ -0,0 +1,20 @@
shader_type canvas_item;
// Uniforms
uniform sampler2D screen_texture : hint_screen_texture;
uniform float grain_amount : hint_range(0.0, 1.0) = 0.05; // Adjust the amount of grain
uniform float grain_size : hint_range(0.1, 10.0) = 1.0; // Adjust the size of the grain
void fragment() {
// Sample the original screen texture
vec4 original_color = texture(screen_texture, SCREEN_UV);
// Generate random noise
float noise = (fract(sin(dot(UV, vec2(12.9898, 78.233))) * 43758.5453) - 0.5) * 2.0;
// Add noise to the original color
original_color.rgb += noise * grain_amount * grain_size;
// Clamp the final color to make sure it stays in the valid range
COLOR = clamp(original_color, 0.0, 1.0);
}

View File

@@ -0,0 +1 @@
uid://bss3x1xthtaef

View File

@@ -0,0 +1,32 @@
shader_type canvas_item;
uniform sampler2D SCREEN_TEXTURE : hint_screen_texture, filter_linear_mipmap;
uniform float x_intensity = 3.0;
uniform float y_intensity = 0.5;
uniform float offset = 0.0;
uniform float speed : hint_range(0, 20) = 2.0;
uniform float wave_frequency : hint_range(0, 100) = 20;
uniform float wave_length : hint_range(50, 800) = 200.0;
void fragment() {
vec2 real_uv = UV;
vec2 vecToBottom = vec2(1, 1) - UV.y;
float distToBottom = length(vecToBottom);
float final_speed = TIME * (speed / 4.0) + offset;
float time_var = (cos(final_speed) * cos(final_speed * 4.0) * cos(final_speed * 2.0))/(200.0);
float time_var2 = (cos(final_speed) * cos(final_speed * 6.0) * cos(final_speed * 2.0))/(200.0);
float wave_from_x = (cos(real_uv.x * 100.0)/1000.0);
float wave_large_from_x = cos(TIME + (real_uv.x * wave_frequency))/wave_length;
float offset_x = time_var * (distToBottom * x_intensity) + wave_from_x + (wave_large_from_x);
float offset_y = time_var2 * (distToBottom * y_intensity);
vec2 distortion_offset = vec2(offset_x, offset_y);
COLOR = texture(SCREEN_TEXTURE, SCREEN_UV + distortion_offset);
}

View File

@@ -0,0 +1 @@
uid://12ar0h66dukp

View File

@@ -0,0 +1 @@
uid://c8nv7tds2neww

View File

@@ -0,0 +1,95 @@
// TRANSLATED & MODIFIED FROM: https://www.shadertoy.com/view/4sX3Rs
shader_type canvas_item;
render_mode blend_mix;
uniform sampler2D SCREEN_TEXTURE : hint_screen_texture, filter_linear_mipmap;
uniform vec2 sun_position = vec2(400.0, 0.0);
uniform vec3 tint = vec3(1.4,1.2,1.0);
uniform sampler2D noise_texture;
float noise_float(float t, vec2 texResolution)
{
return texture(noise_texture,vec2(t,0.0)/texResolution).x;
}
float noise_vec2(vec2 t, vec2 texResolution)
{
return texture(noise_texture,t/texResolution).x;
}
vec3 lensflare(vec2 uv,vec2 pos, vec2 texResolution)
{
vec2 main = uv-pos;
vec2 uvd = uv*(length(uv));
float ang = atan(main.x,main.y);
float dist = length(main);
dist = pow(dist,0.1);
float n = noise_vec2(vec2(ang*16.0,dist*32.0), texResolution);
// Do not need an artificial sun
//float f0 = 1.0/(length(uv-pos)*16.0+1.0);
//f0 = f0 + f0*(sin(noise_float(sin(ang*2.+pos.x)*4.0 - cos(ang*3.+pos.y), texResolution)*16.)*.1 + dist*.1 + .8);
float f1 = max(0.01-pow(length(uv+1.2*pos),1.9),.0)*7.0;
float f2 = max(1.0/(1.0+32.0*pow(length(uvd+0.8*pos),2.0)),.0)*00.25;
float f22 = max(1.0/(1.0+32.0*pow(length(uvd+0.85*pos),2.0)),.0)*00.23;
float f23 = max(1.0/(1.0+32.0*pow(length(uvd+0.9*pos),2.0)),.0)*00.21;
vec2 uvx = mix(uv,uvd,-0.5);
float f4 = max(0.01-pow(length(uvx+0.4*pos),2.4),.0)*6.0;
float f42 = max(0.01-pow(length(uvx+0.45*pos),2.4),.0)*5.0;
float f43 = max(0.01-pow(length(uvx+0.5*pos),2.4),.0)*3.0;
uvx = mix(uv,uvd,-.4);
float f5 = max(0.01-pow(length(uvx+0.2*pos),5.5),.0)*2.0;
float f52 = max(0.01-pow(length(uvx+0.4*pos),5.5),.0)*2.0;
float f53 = max(0.01-pow(length(uvx+0.6*pos),5.5),.0)*2.0;
uvx = mix(uv,uvd,-0.5);
float f6 = max(0.01-pow(length(uvx-0.3*pos),1.6),.0)*6.0;
float f62 = max(0.01-pow(length(uvx-0.325*pos),1.6),.0)*3.0;
float f63 = max(0.01-pow(length(uvx-0.35*pos),1.6),.0)*5.0;
vec3 c = vec3(.0);
c.r+=f2+f4+f5+f6; c.g+=f22+f42+f52+f62; c.b+=f23+f43+f53+f63;
c = c*1.3 - vec3(length(uvd)*.05);
// Do not need an artificial sun
//c+=vec3(f0);
return c;
}
vec3 cc(vec3 color, float factor,float factor2) // color modifier
{
float w = color.x+color.y+color.z;
return mix(color,vec3(w)*factor,w*factor2);
}
void fragment()
{
vec2 texResolution = 1.0 / TEXTURE_PIXEL_SIZE;
vec2 resolution = 1.0 / SCREEN_PIXEL_SIZE;
vec2 uv = FRAGCOORD.xy / resolution.xy - 0.5;
uv.x *= resolution.x/resolution.y; //fix aspect ratio
vec2 mouse = (sun_position.xy / resolution.xy) - vec2(0.5, 0.5);
mouse.x *= resolution.x / resolution.y; //fix aspect ratio
vec4 previousColor = texture(SCREEN_TEXTURE, SCREEN_UV);
vec3 color = previousColor.rgb;
color += tint * lensflare(uv, mouse.xy, texResolution);
color -= noise_vec2(FRAGCOORD.xy, texResolution)*.015;
color = cc(color,.5,.1);
COLOR = vec4(color,1.0);
}

View File

@@ -0,0 +1 @@
uid://c6auxoe0vpq46

View File

@@ -0,0 +1,58 @@
shader_type canvas_item;
uniform sampler2D SCREEN_TEXTURE : hint_screen_texture, filter_linear_mipmap;
uniform vec4 u_bgColor: source_color = vec4(1.0, 1.0, 1.0, 1.0);
uniform float u_bgColorFactor: hint_range(0.0, 1.0) = 0.4;
uniform vec4 u_patternColor: source_color = vec4(0.0, 0.0, 0.0, 1.0);
uniform float u_threshold1: hint_range(0.0, 1.0) = 0.75;
uniform float u_threshold2: hint_range(0.0, 1.0) = 0.50;
uniform float u_threshold3: hint_range(0.0, 1.0) = 0.25;
uniform float u_threshold4: hint_range(0.0, 1.0) = 0.05;
uniform vec2 u_bgTiling = vec2(1.0, 1.0);
uniform vec2 u_patternTiling = vec2(1.0, 1.0);
uniform sampler2D u_bgTexture;
uniform sampler2D u_patternTexture;
const float C2_SQRT2 = 0.707106781;
const mat2 ROT_45 = mat2(vec2(C2_SQRT2, -C2_SQRT2), vec2(C2_SQRT2, C2_SQRT2));
const vec4 COLOR_WHITE = vec4(1.0, 1.0, 1.0, 1.0);
float getIntensity(vec3 color)
{
return 0.299*color.r + 0.587*color.g + 0.114*color.b;
}
vec4 getPatternColor(vec2 uv, float intensity)
{
vec2 patternUV1 = vec2(-uv.x, uv.y) * u_patternTiling;
vec2 patternUV2 = uv * u_patternTiling;
vec2 patternUV3 = ROT_45*(uv + vec2(0.2358, 0.9123)) * u_patternTiling;
vec2 patternUV4 = (vec2(uv.x, -uv.y) + vec2(0.4123, 0.7218)) * u_patternTiling;
vec4 pCol1 = texture(u_patternTexture, patternUV1);
vec4 pCol2 = texture(u_patternTexture, patternUV2);
vec4 pCol3 = texture(u_patternTexture, patternUV3);
vec4 pCol4 = texture(u_patternTexture, patternUV4);
if(intensity > u_threshold1)
return vec4(1.0, 1.0, 1.0, 1.0);
if(intensity > u_threshold2)
return mix(pCol1, COLOR_WHITE, 0.5);
if(intensity > u_threshold3)
return mix(pCol1*pCol2, COLOR_WHITE, 0.3);
if(intensity > u_threshold4)
return mix(pCol1*pCol2*pCol3, COLOR_WHITE, 0.1);
return pCol1*pCol2*pCol3*pCol4*0.8;
}
void fragment()
{
vec4 origColor = texture(SCREEN_TEXTURE, SCREEN_UV);
float intensity = getIntensity(origColor.rgb);
vec4 bgColor = mix(texture(u_bgTexture, UV*u_bgTiling), u_bgColor, u_bgColorFactor);
vec4 patternColor = getPatternColor(UV, intensity);
vec4 color = mix(u_patternColor, bgColor, getIntensity(patternColor.rgb));
COLOR = color;
}

View File

@@ -0,0 +1 @@
uid://c54knriu0lot8

View File

@@ -0,0 +1 @@
uid://b4qebk1wtx6pj

View File

@@ -0,0 +1,27 @@
/*
放射状ブラーエフェクト by あるる(きのもと 結衣) @arlez80
Radial Blur Effect by Yui Kinomoto
MIT License
*/
shader_type canvas_item;
uniform sampler2D SCREEN_TEXTURE : hint_screen_texture, filter_linear_mipmap;
// 発射中央部
uniform vec2 blur_center = vec2( 0.5, 0.5 );
// ブラー強度
uniform float blur_power : hint_range( 0.0, 1.0 ) = 0.01;
// サンプリング回数
uniform int sampling_count : hint_range( 1, 64 ) = 2;
void fragment( )
{
vec2 direction = SCREEN_UV - blur_center;
vec3 c = vec3( 0.0, 0.0, 0.0 );
float f = 1.0 / float( sampling_count );
for( int i=0; i < sampling_count; i++ ) {
c += texture( SCREEN_TEXTURE, SCREEN_UV - blur_power * direction * float(i) ).rgb * f;
}
COLOR.rgb = c;
}

View File

@@ -0,0 +1 @@
uid://bcwhtc0sjmwr3

View File

@@ -0,0 +1 @@
uid://c366ik3prmltu

View File

@@ -0,0 +1,59 @@
// Modified version of https://godotshaders.com/shader/multilayer-snowfall-shader/
// All credits to the original author!!!
shader_type canvas_item;
uniform float spread : hint_range(0.0, 1.5) = 0.5;
uniform float size : hint_range(0.01, 5.0) = 0.5;
uniform vec4 snow_color : source_color = vec4(1.0);
uniform float snow_transparency: hint_range(-0.5, 1.0) = 0.2;
uniform float speed : hint_range(0.0, 10.0) = 0.5;
uniform float wind : hint_range(-2.0, 2.0) = 0.0;
uniform int num_of_layers = 40;
const mat3 NOISE_MATRIX = mat3(
vec3(13.323122, 23.5112, 21.71123),
vec3(21.1212, 28.7312, 11.9312),
vec3(21.8112, 14.7212, 61.3934)
);
vec3 generate_snowflake(vec2 coord, float layer_index, float time, float wind_strength) {
float layer_scale = 1.0 + layer_index * 0.5 / (max(size, 0.01) * 2.0);
vec2 scaled_coord = coord * layer_scale;
vec2 movement = vec2(
scaled_coord.y * (spread * mod(layer_index * 7.238917, 1.0) - spread * 0.5) +
(-wind_strength) * speed * time * 0.5,
-speed * time / (1.0 + layer_index * 0.5 * 0.03)
);
vec2 final_coord = scaled_coord + movement;
vec3 noise_input = vec3(floor(final_coord), 31.189 + layer_index);
vec3 noise_val = floor(noise_input) * 0.00001 + fract(noise_input);
vec3 random = fract((31415.9 + noise_val) / fract(NOISE_MATRIX * noise_val));
vec2 shape = abs(mod(final_coord, 1.0) - 0.5 + 0.9 * random.xy - 0.45);
shape += 0.01 * abs(2.0 * fract(10.0 * final_coord.yx) - 1.0);
float depth_offset = 5.0 * sin(time * 0.1);
float edge_softness = 0.005 + 0.05 * min(0.5 * abs(layer_index - 5.0 - depth_offset), 1.0);
float shape_value = 0.6 * max(shape.x - shape.y, shape.x + shape.y) + max(shape.x, shape.y) - 0.01;
return vec3(smoothstep(edge_softness, -edge_softness, shape_value) *
(random.x / (1.0 + 0.02 * layer_index * 0.5)));
}
void fragment() {
vec3 snow_accumulation = vec3(0.0);
for (int i = 0; i < num_of_layers; i++) {
snow_accumulation += generate_snowflake(UV, float(i), TIME, wind);
}
float snow_intensity = clamp(length(snow_accumulation), 0.0, 1.0);
vec4 transparency_color = vec4(snow_color.rgb * (1.0 + snow_transparency), snow_intensity);
COLOR = transparency_color;
}

View File

@@ -0,0 +1 @@
uid://deof0r34dt78d

View File

@@ -0,0 +1,17 @@
shader_type canvas_item;
uniform float inner_radius = 0.1;
uniform float outer_radius = 1;
uniform float vignette_strength = 1.0;
uniform float dither_strength = 0.03;
uniform vec4 vignette_color: source_color;
void fragment() {
float dist = distance(UV, vec2(0.5));
float vignette = smoothstep(inner_radius, outer_radius, dist) * vignette_strength;
float dither = fract(sin(dot(UV, vec2(12.9898, 78.233))) * 43758.5453123) * dither_strength;
COLOR = vec4(vignette_color.rgb, vignette + dither);
}

View File

@@ -0,0 +1 @@
uid://boba7hm3vito8

View File

@@ -0,0 +1 @@
uid://186q3n8sc7qw

View File

@@ -14,6 +14,8 @@
.copy-url-btn { bg-[#8b5cf6] text-white hover:bg-[#7c3aed] }
</style>
<postprocess preset="chrome" />
<script>
gurt.log('Script started!')

View File

@@ -5,6 +5,7 @@
<meta name="description" content="A stylish no-script dashboard">
<font name="roboto" src="https://fonts.gstatic.com/s/roboto/v48/KFO7CnqEu92Fr1ME7kSn66aGLdTylUAMa3KUBGEe.woff2" />
<postprocess preset="rblur" />
<style>
h1 { text-[#ffffff] text-3xl font-bold }