Skip to content

Commit

Permalink
feat: rewrite force field shader for Godot 4.3 (#57)
Browse files Browse the repository at this point in the history
* Update force field shader

- Remove ball throwing from demo
- Add comments and sliders
- Change linear depth calculation

* chore: proofread forcefield comments

---------

Co-authored-by: Nathan Lovato <[email protected]>
  • Loading branch information
tetrapod00 and NathanLovato authored Sep 17, 2024
1 parent 583be1f commit 7d1d44a
Show file tree
Hide file tree
Showing 5 changed files with 116 additions and 156 deletions.
139 changes: 16 additions & 123 deletions godot/Demos/ForceField/ForceFieldScene.tscn
Original file line number Diff line number Diff line change
@@ -1,104 +1,25 @@
[gd_scene load_steps=17 format=3 uid="uid://haehxmhcirdj"]
[gd_scene load_steps=7 format=3 uid="uid://haehxmhcirdj"]

[ext_resource type="Script" path="res://Demos/ForceField/ForceFieldEmitter.gd" id="1"]
[ext_resource type="Shader" path="res://Shaders/force_field.gdshader" id="5"]
[ext_resource type="Material" uid="uid://cp7fpmko6heid" path="res://Demos/ForceField/force_field_material.tres" id="4_4jjbs"]

[sub_resource type="BoxShape3D" id="1"]
size = Vector3(6, 0.25, 3)

[sub_resource type="BoxMesh" id="2"]
size = Vector3(6, 0.25, 3)

[sub_resource type="StandardMaterial3D" id="3"]
albedo_color = Color(0.623529, 0.643137, 0.670588, 1)

[sub_resource type="CylinderMesh" id="4"]
top_radius = 0.25
bottom_radius = 0.25
height = 0.5

[sub_resource type="StandardMaterial3D" id="5"]
albedo_color = Color(0.32549, 0.27451, 0.239216, 1)
metallic = 0.48
roughness = 0.23

[sub_resource type="PhysicsMaterial" id="6"]
friction = 120.0
rough = true
bounce = 0.25

[sub_resource type="SphereShape3D" id="7"]
radius = 0.05

[sub_resource type="SphereMesh" id="8"]

[sub_resource type="StandardMaterial3D" id="9"]
albedo_color = Color(0.270588, 0.278431, 0.337255, 1)

[sub_resource type="SphereMesh" id="10"]

[sub_resource type="ShaderMaterial" id="11"]
render_priority = 0
shader = ExtResource("5")

[sub_resource type="Animation" id="13"]
resource_name = "Expand"
length = 0.5
tracks/0/type = "value"
tracks/0/imported = false
tracks/0/enabled = true
tracks/0/path = NodePath(".:visible")
tracks/0/interp = 1
tracks/0/loop_wrap = true
tracks/0/keys = {
"times": PackedFloat32Array(0),
"transitions": PackedFloat32Array(1),
"update": 1,
"values": [true]
}
tracks/1/type = "value"
tracks/1/imported = false
tracks/1/enabled = true
tracks/1/path = NodePath(".:scale")
tracks/1/interp = 1
tracks/1/loop_wrap = true
tracks/1/keys = {
"times": PackedFloat32Array(0, 0.5),
"transitions": PackedFloat32Array(1, 1),
"update": 0,
"values": [Vector3(0.01, 0.01, 0.01), Vector3(1.5, 1.5, 1.5)]
}

[sub_resource type="Animation" id="14"]
length = 0.001
tracks/0/type = "value"
tracks/0/imported = false
tracks/0/enabled = true
tracks/0/path = NodePath(".:visible")
tracks/0/interp = 1
tracks/0/loop_wrap = true
tracks/0/keys = {
"times": PackedFloat32Array(0),
"transitions": PackedFloat32Array(1),
"update": 0,
"values": [false]
}
tracks/1/type = "value"
tracks/1/imported = false
tracks/1/enabled = true
tracks/1/path = NodePath(".:scale")
tracks/1/interp = 1
tracks/1/loop_wrap = true
tracks/1/keys = {
"times": PackedFloat32Array(0),
"transitions": PackedFloat32Array(1),
"update": 0,
"values": [Vector3(0.01, 0.01, 0.01)]
}

[sub_resource type="BoxShape3D" id="12"]
size = Vector3(20, 0.2, 20)

[sub_resource type="SphereMesh" id="SphereMesh_jwmkr"]
radius = 1.5
height = 3.0

[node name="ForceFieldScene" type="Node3D"]

[node name="WallBody" type="StaticBody3D" parent="."]
Expand All @@ -109,53 +30,25 @@ shape = SubResource("1")

[node name="Wall" type="MeshInstance3D" parent="WallBody"]
mesh = SubResource("2")
material/0 = SubResource("3")

[node name="Camera3D" type="Camera3D" parent="."]
transform = Transform3D(0.918571, -0.125535, 0.374792, 0, 0.948224, 0.317603, -0.395257, -0.29174, 0.871011, 1.63275, 1.38087, 3.55278)

[node name="MeshInstance3D" type="MeshInstance3D" parent="."]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 1.94743, 0.25, 1.83367)
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 1.55994, 0.25, 1.83398)
mesh = SubResource("4")
material/0 = SubResource("5")

[node name="RigidBody3D" type="RigidBody3D" parent="."]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.539, 1.064, 3.841)
physics_material_override = SubResource("6")
gravity_scale = 0.0
max_contacts_reported = 1
contact_monitor = true
sleeping = true
axis_lock_angular_x = true
axis_lock_angular_y = true
axis_lock_angular_z = true
script = ExtResource("1")

[node name="CollisionShape3D" type="CollisionShape3D" parent="RigidBody3D"]
shape = SubResource("7")

[node name="MeshInstance3D" type="MeshInstance3D" parent="RigidBody3D"]
transform = Transform3D(0.05, 0, 0, 0, 0.05, 0, 0, 0, 0.05, 0, 0, 0)
mesh = SubResource("8")
material/0 = SubResource("9")

[node name="ForceField" type="MeshInstance3D" parent="RigidBody3D"]
transform = Transform3D(0.01, 0, 0, 0, 0.01, 0, 0, 0, 0.01, 0, 0, 0)
visible = false
mesh = SubResource("10")
material/0 = SubResource("11")

[node name="OmniLight3D" type="OmniLight3D" parent="RigidBody3D/ForceField"]
light_color = Color(0.839216, 0.521569, 1, 1)
omni_range = 2.2

[node name="AnimationPlayer" type="AnimationPlayer" parent="RigidBody3D/ForceField"]
autoplay = "RESET"
anims/Expand = SubResource("13")
anims/RESET = SubResource("14")

[node name="FloorBody" type="StaticBody3D" parent="."]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -0.1, 0)

[node name="CollisionShape3D" type="CollisionShape3D" parent="FloorBody"]
shape = SubResource("12")

[node name="ForceField" type="MeshInstance3D" parent="."]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.373823, 0.612613, 0.925982)
mesh = SubResource("SphereMesh_jwmkr")
surface_material_override/0 = ExtResource("4_4jjbs")

[node name="OmniLight3D2" type="OmniLight3D" parent="ForceField"]
light_color = Color(0.839216, 0.521569, 1, 1)
omni_range = 2.2
20 changes: 20 additions & 0 deletions godot/Demos/ForceField/force_field_material.tres
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
[gd_resource type="ShaderMaterial" load_steps=3 format=3 uid="uid://cp7fpmko6heid"]

[ext_resource type="Shader" path="res://Shaders/force_field.gdshader" id="1_hm6s7"]
[ext_resource type="Texture2D" uid="uid://b2skoftifgi55" path="res://Demos/ForceField/hexagon_grid.png" id="2_tm5w2"]

[resource]
render_priority = 0
shader = ExtResource("1_hm6s7")
shader_parameter/color = Color(0.607843, 0.219608, 1, 1)
shader_parameter/pattern_scroll_speed = 0.025
shader_parameter/pattern_uv_scale = Vector2(6, 3)
shader_parameter/pattern_texture = ExtResource("2_tm5w2")
shader_parameter/fresnel_power = 4.0
shader_parameter/edge_intensity = 2.0
shader_parameter/center_intensity = 0.1
shader_parameter/pulsing_strength = 0.25
shader_parameter/pulsing_speed = 1.0
shader_parameter/scanline_frequency = 0.5
shader_parameter/scanline_width = 0.1
shader_parameter/scanline_intensity = 0.35
4 changes: 2 additions & 2 deletions godot/Demos/ForceField/hexagon_grid.png.import
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ compress/lossy_quality=0.7
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/generate=true
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
Expand All @@ -32,4 +32,4 @@ 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
detect_3d/compress_to=0
5 changes: 3 additions & 2 deletions godot/Demos/ForceFieldDemo.tscn
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
[gd_scene load_steps=5 format=3 uid="uid://ynusfw8t4kgp"]

[ext_resource type="PackedScene" uid="uid://haehxmhcirdj" path="res://Demos/ForceField/ForceFieldScene.tscn" id="1"]
[ext_resource type="PackedScene" path="res://Shared/DemoInterface.tscn" id="2"]
[ext_resource type="PackedScene" path="res://Shared/Demo3DEnvironment.tscn" id="3"]
[ext_resource type="PackedScene" uid="uid://diofpwcvq5elu" path="res://Shared/DemoInterface.tscn" id="2"]
[ext_resource type="PackedScene" uid="uid://dquhei2qv5csj" path="res://Shared/Demo3DEnvironment.tscn" id="3"]
[ext_resource type="Script" path="res://addons/ShaderSecretsHelper/DemoScreen.gd" id="4"]

[node name="ForceFieldDemo" type="CanvasLayer"]
Expand All @@ -13,3 +13,4 @@ script = ExtResource("4")
[node name="Demo3DEnvironment" parent="." instance=ExtResource("3")]

[node name="DemoInterface" parent="." instance=ExtResource("2")]
anchors_preset = 10
104 changes: 75 additions & 29 deletions godot/Shaders/force_field.gdshader
Original file line number Diff line number Diff line change
@@ -1,58 +1,104 @@
shader_type spatial;
render_mode depth_draw_opaque, cull_disabled, ambient_light_disabled, blend_add, shadows_disabled;

group_uniforms ColorAndPattern;
/** Base color. */
uniform vec4 color : source_color;
uniform float fresnel_power = 1.0;
uniform float edge_intensity = 2.0;
uniform float fill_amount : hint_range(0, 1) = 0.1;
/** The speed that the pattern scrolls. */
uniform float pattern_scroll_speed = 0.025;
/** The scale of the pattern. Bigger values make the hexagons smaller. */
uniform vec2 pattern_uv_scale = vec2(6.0, 3.0);
/** The texture of the pattern. */
uniform sampler2D pattern_texture : source_color;
group_uniforms;

group_uniforms Intensity;
/** The thickness of the edges. Higher values mean thinner edges. */
uniform float fresnel_power: hint_range(0.5, 8.0) = 4.0;
/** The intensity of the edges. Values over [code]1.0[/code] will glow, if you have Glow enabled on your environment.
*/
uniform float edge_intensity: hint_range(0.0, 2.0) = 1.5;
/** The intensity of the center. */
uniform float center_intensity : hint_range(0.0, 1.0) = 0.1;
group_uniforms;

group_uniforms Pulsing;
/** Magnitude of the pulsing effect.
* This is the amplitude of the sine wave used to move the vertices. The pulsing is additive and done in model space, so if you scale the mesh or the mesh instance, you'll need to adjust this value.
*/
uniform float pulsing_strength = 0.25;
/** Speed of the pulsing effect. Higher values are faster.
* The frequency of the sine wave used to move the vertices.
*/
uniform float pulsing_speed = 1.0;
group_uniforms;

uniform float scanline_period = 0.5;
uniform float scanline_width : hint_range(0, 0.49) = 0.1;
group_uniforms Scanlines;
/** The frequency of the scanline effect. Higher values mean the scanlines repeat more often.
*/
uniform float scanline_frequency = 0.5;
/** The width of the scanlines. */
uniform float scanline_width : hint_range(0, 0.5) = 0.1;
/** The intensity (brightness) of the scanlines. */
uniform float scanline_intensity = 0.35;
group_uniforms;

uniform float pattern_scroll_speed = 0.025;
uniform vec2 pattern_uv_offset = vec2(6.0, 3.0);

uniform sampler2D pattern_texture : source_color;
uniform sampler2D DEPTH_TEXTURE: hint_depth_texture, filter_linear_mipmap;

void vertex() {
float pulse_distance = sin(TIME * pulsing_speed) * 0.1 * pulsing_strength;
VERTEX += NORMAL * pulse_distance;
}

float get_linear_depth(sampler2D depth_texture, vec2 screen_uv, mat4 inv_projection_matrix, bool use_opengl_ndc) {
float depth_raw = texture(depth_texture, screen_uv).x;
vec3 ndc;
if (use_opengl_ndc){
// Compatibility. Uses OpenGL NDC space: range of [-1,1] for NDC.xy, range of [-1,1] for NDC.z
ndc = vec3(screen_uv, depth_raw) * 2.0 - 1.0;
} else {
// Forward+ or Mobile. Uses Vulkan NDC space: range of [-1,1] for NDC.xy, range of [0,1] for NDC.z
ndc = vec3(screen_uv * 2.0 - 1.0, depth_raw);
}
vec4 position_view = inv_projection_matrix * vec4(ndc, 1.0);
position_view.xyz /= position_view.w;
return -position_view.z;
}

void fragment() {
// Create a fresnel effect from the NORMAL and VIEW vectors.
float fresnel = pow(1.0 - dot(NORMAL, VIEW), fresnel_power) * edge_intensity;

// Add back transparency in the middle
fresnel = fresnel + fill_amount;

// Get the raw linear depth from the depth texture into a [-1, 1] range
float depth = texture(DEPTH_TEXTURE, SCREEN_UV).r * 2.0 - 1.0;
// Recreate linear depth of the intersecting geometry using projection matrix, and subtract the vertex of the sphere
depth = PROJECTION_MATRIX[3][2] / (depth + PROJECTION_MATRIX[2][2]) + VERTEX.z;
fresnel = fresnel + center_intensity;

// Get the linear depth.
float depth = get_linear_depth(DEPTH_TEXTURE, SCREEN_UV, INV_PROJECTION_MATRIX, OUTPUT_IS_SRGB);

// Get the difference between the linear depth and the fragment's linear depth.
// VERTEX is the interpolated position of each fragment, in view space.
// VERTEX.z is the linear depth of each fragment.
depth = depth + VERTEX.z;

// Intensity intersection effect
depth = pow(1.0 - clamp(depth, 0, 1), fresnel_power) * edge_intensity;
// Calculate final alpha using fresnel and depth joined together

// Calculate final alpha using fresnel and depth joined together.
fresnel = fresnel + depth;
// Calculate UV scrolling pattern

// Calculate UV scrolling pattern.
float scrolling_time = TIME * pattern_scroll_speed;
vec4 pattern = texture(pattern_texture, (UV * pattern_uv_offset) + vec2(scrolling_time));
// Use pattern to cut holes in alpha
vec4 pattern = texture(pattern_texture, (UV * pattern_uv_scale) + vec2(scrolling_time));

// Use pattern to cut holes in alpha.
fresnel *= pattern.r;
float uv_offset = mod(-TIME * scanline_period, 2.0) - 1.0;
float scanline = smoothstep(0.5 - scanline_width, 0.5, UV.y + uv_offset)

float uv_offset = mod(-TIME * scanline_frequency, 2.0) - 1.0;
float scanline = smoothstep(0.5 - scanline_width, 0.5, UV.y + uv_offset)
* (1.0 - smoothstep(0.5, 0.5 + scanline_width, UV.y + uv_offset)) * pattern.r;
// Apply final color

// Apply final color.
ALBEDO = vec3(0);
EMISSION = color.rgb;
ALPHA = smoothstep(0.0, 1.0, fresnel + scanline * scanline_intensity);
}
}

0 comments on commit 7d1d44a

Please sign in to comment.