mirror of
https://github.com/marinho/godot-visual-effects.git
synced 2024-12-22 14:37:27 +08:00
Camera effects in CanvasLayer (+Cromatic Aberration)
This commit is contained in:
parent
0fc19ef815
commit
1668629916
@ -30,7 +30,7 @@
|
||||
|
||||
## Camera Effects
|
||||
|
||||
These effects are mostly to be placed as a child of Camera object
|
||||
These effects should be place in a CanvasLayer object in any scene
|
||||
|
||||
### Pixelate
|
||||
|
||||
@ -40,6 +40,10 @@ These effects are mostly to be placed as a child of Camera object
|
||||
|
||||
![Vignette](./docs/images/camera-effects/vignette.png)
|
||||
|
||||
### Chromatic Aberration
|
||||
|
||||
![Chromatic Aberration](./docs/images/camera-effects/chromatic-aberration.png)
|
||||
|
||||
## License
|
||||
|
||||
* Kenney Particle Pack is copyrighted by Kenney, and can be found at https://kenney.nl/assets/particle-pack
|
||||
|
@ -1,13 +1,14 @@
|
||||
[gd_scene load_steps=12 format=3 uid="uid://c051w6upl0t16"]
|
||||
[gd_scene load_steps=13 format=3 uid="uid://c051w6upl0t16"]
|
||||
|
||||
[ext_resource type="PackedScene" uid="uid://dgv546pcp176" path="res://camera-effects/camera-pixelate.tscn" id="1_1s7cw"]
|
||||
[ext_resource type="PackedScene" uid="uid://bydyult2k5rcb" path="res://addons/kenney_prototype_tools/scenes/green/green_01.tscn" id="1_61jfr"]
|
||||
[ext_resource type="PackedScene" uid="uid://b3fmmgvf3owwy" path="res://camera-effects/camera-vignette.tscn" id="2_c86ci"]
|
||||
[ext_resource type="PackedScene" uid="uid://bjupt5nu14hth" path="res://addons/kenney_prototype_tools/scenes/dark/dark_05.tscn" id="2_xlmfj"]
|
||||
[ext_resource type="PackedScene" uid="uid://bhuxup1cbugbw" path="res://addons/kenney_prototype_tools/scenes/orange/orange_06.tscn" id="3_xj16f"]
|
||||
[ext_resource type="PackedScene" uid="uid://ct0k1os3fx251" path="res://addons/kenney_prototype_tools/scenes/purple/purple_03.tscn" id="4_paly4"]
|
||||
[ext_resource type="PackedScene" uid="uid://d23c4qtufdpk7" path="res://addons/kenney_prototype_tools/scenes/red/red_02.tscn" id="5_dra7r"]
|
||||
[ext_resource type="PackedScene" uid="uid://npssehgc2462" path="res://addons/kenney_prototype_tools/scenes/light/light_06.tscn" id="6_y7myb"]
|
||||
[ext_resource type="PackedScene" uid="uid://sy04esp2kle1" path="res://camera-effects/chromatic_aberration.tscn" id="7_uoado"]
|
||||
[ext_resource type="PackedScene" uid="uid://djsrvtsqjesw8" path="res://camera-effects/pixelate.tscn" id="8_fshcg"]
|
||||
[ext_resource type="PackedScene" uid="uid://ds5xw2us1br3q" path="res://camera-effects/vignette.tscn" id="9_q1phu"]
|
||||
|
||||
[sub_resource type="Environment" id="Environment_tln01"]
|
||||
glow_blend_mode = 4
|
||||
@ -29,10 +30,6 @@ current = true
|
||||
fov = 13.5
|
||||
size = 13.34
|
||||
|
||||
[node name="Pixelate" parent="Camera3D" instance=ExtResource("1_1s7cw")]
|
||||
|
||||
[node name="Vignette" parent="Camera3D" instance=ExtResource("2_c86ci")]
|
||||
|
||||
[node name="DirectionalLight3D" type="DirectionalLight3D" parent="."]
|
||||
transform = Transform3D(1, 0, 0, 0, 0.5, 0.866025, 0, -0.866025, 0.5, 0, 2, 0)
|
||||
light_energy = 2.0
|
||||
@ -150,3 +147,13 @@ transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 6, 0, -7)
|
||||
|
||||
[node name="Cube36" parent="Scene" instance=ExtResource("6_y7myb")]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 5, 0, -8)
|
||||
|
||||
[node name="CanvasLayer" type="CanvasLayer" parent="."]
|
||||
|
||||
[node name="Chromatic Aberration" parent="CanvasLayer" instance=ExtResource("7_uoado")]
|
||||
visible = false
|
||||
|
||||
[node name="Pixelate" parent="CanvasLayer" instance=ExtResource("8_fshcg")]
|
||||
visible = false
|
||||
|
||||
[node name="Vignette" parent="CanvasLayer" instance=ExtResource("9_q1phu")]
|
||||
|
@ -1,26 +0,0 @@
|
||||
[gd_scene load_steps=5 format=3 uid="uid://dgv546pcp176"]
|
||||
|
||||
[sub_resource type="Gradient" id="Gradient_hmc0v"]
|
||||
offsets = PackedFloat32Array(0.225256, 0.703072)
|
||||
colors = PackedColorArray(0, 0, 0, 0, 0, 0, 0, 1)
|
||||
|
||||
[sub_resource type="GradientTexture2D" id="GradientTexture2D_1cjfc"]
|
||||
gradient = SubResource("Gradient_hmc0v")
|
||||
width = 256
|
||||
height = 256
|
||||
fill = 1
|
||||
fill_from = Vector2(0.5, 0.5)
|
||||
|
||||
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_q043q"]
|
||||
transparency = 1
|
||||
albedo_texture = SubResource("GradientTexture2D_1cjfc")
|
||||
roughness = 0.0
|
||||
|
||||
[sub_resource type="QuadMesh" id="QuadMesh_002xp"]
|
||||
material = SubResource("StandardMaterial3D_q043q")
|
||||
size = Vector2(24, 14)
|
||||
|
||||
[node name="Pixelate" type="MeshInstance3D"]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, -0.820396)
|
||||
visible = false
|
||||
mesh = SubResource("QuadMesh_002xp")
|
@ -1,47 +0,0 @@
|
||||
[gd_scene load_steps=8 format=3 uid="uid://b3fmmgvf3owwy"]
|
||||
|
||||
[sub_resource type="Curve" id="Curve_kdxx2"]
|
||||
max_value = 1.5
|
||||
_data = [Vector2(0, 1.5), 0.0, 0.0, 0, 0, Vector2(0.498361, 1.28219), 0.0, 0.0, 0, 0, Vector2(1, 1.5), 0.0, 0.0, 0, 0]
|
||||
point_count = 3
|
||||
|
||||
[sub_resource type="CurveTexture" id="CurveTexture_ts23d"]
|
||||
curve = SubResource("Curve_kdxx2")
|
||||
|
||||
[sub_resource type="ParticleProcessMaterial" id="ParticleProcessMaterial_a08vp"]
|
||||
gravity = Vector3(0, 0, 0)
|
||||
scale_curve = SubResource("CurveTexture_ts23d")
|
||||
|
||||
[sub_resource type="Gradient" id="Gradient_0ankm"]
|
||||
offsets = PackedFloat32Array(0.430605, 0.733096)
|
||||
colors = PackedColorArray(0, 0, 0, 0, 0.392157, 0, 0, 1)
|
||||
|
||||
[sub_resource type="GradientTexture2D" id="GradientTexture2D_s3frb"]
|
||||
gradient = SubResource("Gradient_0ankm")
|
||||
width = 256
|
||||
height = 256
|
||||
fill = 1
|
||||
fill_from = Vector2(0.5, 0.5)
|
||||
|
||||
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_8laxd"]
|
||||
transparency = 1
|
||||
vertex_color_use_as_albedo = true
|
||||
albedo_texture = SubResource("GradientTexture2D_s3frb")
|
||||
roughness = 0.0
|
||||
billboard_mode = 3
|
||||
billboard_keep_scale = true
|
||||
particles_anim_h_frames = 1
|
||||
particles_anim_v_frames = 1
|
||||
particles_anim_loop = false
|
||||
|
||||
[sub_resource type="QuadMesh" id="QuadMesh_hh0er"]
|
||||
material = SubResource("StandardMaterial3D_8laxd")
|
||||
size = Vector2(24, 14)
|
||||
|
||||
[node name="Vignette" type="GPUParticles3D"]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, -0.5)
|
||||
amount = 1
|
||||
lifetime = 3.0
|
||||
fixed_fps = 60
|
||||
process_material = SubResource("ParticleProcessMaterial_a08vp")
|
||||
draw_pass_1 = SubResource("QuadMesh_hh0er")
|
15
camera-effects/chromatic-aberration.gdshader
Normal file
15
camera-effects/chromatic-aberration.gdshader
Normal file
@ -0,0 +1,15 @@
|
||||
shader_type canvas_item;
|
||||
|
||||
// inspired by: https://www.youtube.com/watch?v=aVzY6n3e19A
|
||||
|
||||
uniform sampler2D SCREEN_TEXTURE : hint_screen_texture, filter_linear;
|
||||
uniform float ca_strength = 20.0;
|
||||
|
||||
void fragment() {
|
||||
vec2 ca_offset = vec2(ca_strength, 0.0) * SCREEN_PIXEL_SIZE;
|
||||
float red = texture(SCREEN_TEXTURE, SCREEN_UV - ca_offset).r;
|
||||
float green = texture(SCREEN_TEXTURE, SCREEN_UV).g;
|
||||
float blue = texture(SCREEN_TEXTURE, SCREEN_UV + ca_offset).b;
|
||||
|
||||
COLOR = vec4(red, green, blue, 1.0);
|
||||
}
|
15
camera-effects/chromatic_aberration.tscn
Normal file
15
camera-effects/chromatic_aberration.tscn
Normal file
@ -0,0 +1,15 @@
|
||||
[gd_scene load_steps=3 format=3 uid="uid://sy04esp2kle1"]
|
||||
|
||||
[ext_resource type="Shader" path="res://camera-effects/chromatic-aberration.gdshader" id="1_qe316"]
|
||||
|
||||
[sub_resource type="ShaderMaterial" id="ShaderMaterial_bla75"]
|
||||
shader = ExtResource("1_qe316")
|
||||
shader_parameter/ca_strength = 20.0
|
||||
|
||||
[node name="Chromatic Aberration" type="ColorRect"]
|
||||
material = SubResource("ShaderMaterial_bla75")
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
@ -1,9 +1,14 @@
|
||||
shader_type spatial;
|
||||
shader_type canvas_item;
|
||||
|
||||
uniform sampler2D SCREEN_TEXTURE : hint_screen_texture, filter_linear;
|
||||
uniform int pixel_size = 1;
|
||||
uniform float alpha = 0.1;
|
||||
|
||||
// inspired by https://www.youtube.com/watch?v=77F4ZjmQ07U
|
||||
|
||||
void fragment() {
|
||||
vec2 VIEWPORT_SIZE = 1.0 / SCREEN_PIXEL_SIZE;
|
||||
|
||||
// a variant of nearest neighbour fragment shader_type
|
||||
float x = float(int(FRAGCOORD.x) % pixel_size);
|
||||
float y = float(int(FRAGCOORD.y) % pixel_size);
|
||||
@ -12,6 +17,5 @@ void fragment() {
|
||||
y = FRAGCOORD.y + floor(float(pixel_size) / 2.0) - y;
|
||||
|
||||
// set albedo value on the current coordinate based on vec2(x,y) / viewport_size
|
||||
ALBEDO = texture(SCREEN_TEXTURE, vec2(x, y) / VIEWPORT_SIZE).xyz;
|
||||
ALPHA = alpha;
|
||||
COLOR = vec4(texture(SCREEN_TEXTURE, vec2(x, y) / VIEWPORT_SIZE).xyz, alpha);
|
||||
}
|
||||
|
16
camera-effects/pixelate.tscn
Normal file
16
camera-effects/pixelate.tscn
Normal file
@ -0,0 +1,16 @@
|
||||
[gd_scene load_steps=3 format=3 uid="uid://djsrvtsqjesw8"]
|
||||
|
||||
[ext_resource type="Shader" path="res://camera-effects/pixelate.gdshader" id="1_boqib"]
|
||||
|
||||
[sub_resource type="ShaderMaterial" id="ShaderMaterial_1p2bf"]
|
||||
shader = ExtResource("1_boqib")
|
||||
shader_parameter/pixel_size = 4
|
||||
shader_parameter/alpha = 1.0
|
||||
|
||||
[node name="Pixelate" type="ColorRect"]
|
||||
material = SubResource("ShaderMaterial_1p2bf")
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
20
camera-effects/vignette.gdshader
Normal file
20
camera-effects/vignette.gdshader
Normal file
@ -0,0 +1,20 @@
|
||||
shader_type canvas_item;
|
||||
|
||||
uniform vec4 color : source_color;
|
||||
uniform float speed = 3.0;
|
||||
uniform float pulse_strength = 0.1;
|
||||
uniform float internal_radius = 0.4;
|
||||
uniform float external_radius = 0.7;
|
||||
|
||||
float smoothCircle(vec2 center, float positioner, vec2 uv)
|
||||
{
|
||||
float d = distance(center, uv);
|
||||
return smoothstep(internal_radius + positioner, external_radius + positioner, d);
|
||||
}
|
||||
|
||||
void fragment() {
|
||||
vec2 CENTER = vec2(0.5, 0.5);
|
||||
float position = sin(TIME * speed) * pulse_strength;
|
||||
float circle = smoothCircle(CENTER, position, UV);
|
||||
COLOR = vec4(color.rgb, circle);
|
||||
}
|
19
camera-effects/vignette.tscn
Normal file
19
camera-effects/vignette.tscn
Normal file
@ -0,0 +1,19 @@
|
||||
[gd_scene load_steps=3 format=3 uid="uid://ds5xw2us1br3q"]
|
||||
|
||||
[ext_resource type="Shader" path="res://camera-effects/vignette.gdshader" id="1_pj8nl"]
|
||||
|
||||
[sub_resource type="ShaderMaterial" id="ShaderMaterial_p1c5j"]
|
||||
shader = ExtResource("1_pj8nl")
|
||||
shader_parameter/color = Color(0.411765, 0, 0, 1)
|
||||
shader_parameter/speed = 3.0
|
||||
shader_parameter/pulse_strength = 0.1
|
||||
shader_parameter/internal_radius = 0.4
|
||||
shader_parameter/external_radius = 0.7
|
||||
|
||||
[node name="Vignette" type="ColorRect"]
|
||||
material = SubResource("ShaderMaterial_p1c5j")
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
BIN
docs/images/camera-effects/chromatic-aberration.png
Normal file
BIN
docs/images/camera-effects/chromatic-aberration.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 123 KiB |
Binary file not shown.
Before Width: | Height: | Size: 109 KiB After Width: | Height: | Size: 29 KiB |
Binary file not shown.
Before Width: | Height: | Size: 252 KiB After Width: | Height: | Size: 182 KiB |
@ -15,11 +15,6 @@ run/main_scene="res://particle-effects.tscn"
|
||||
config/features=PackedStringArray("4.2", "Forward Plus")
|
||||
config/icon="res://icon.svg"
|
||||
|
||||
[display]
|
||||
|
||||
window/size/viewport_width=2304
|
||||
window/size/viewport_height=1296
|
||||
|
||||
[dotnet]
|
||||
|
||||
project/assembly_name="godot-visual-effects"
|
||||
|
59
shaders/shapes.gdshaderinc
Normal file
59
shaders/shapes.gdshaderinc
Normal file
@ -0,0 +1,59 @@
|
||||
/**
|
||||
* Draws a filled circle
|
||||
*/
|
||||
float drawFullCircle(vec2 center, float radius, vec2 uv)
|
||||
{
|
||||
return (distance(center, uv) <= radius) ? 1. : 0.;
|
||||
}
|
||||
|
||||
/**
|
||||
* Draws a line circle
|
||||
*/
|
||||
float drawLineCircle(vec2 center, float radius, float stroke, vec2 uv)
|
||||
{
|
||||
return drawFullCircle(center, radius, uv)
|
||||
- drawFullCircle(center, radius - stroke, uv);
|
||||
}
|
||||
|
||||
/**
|
||||
* Draws a line from p1 to p2
|
||||
*
|
||||
* @param p1 vec2
|
||||
* @param p2 vec2
|
||||
* @param stroke float
|
||||
* @param uv float to receive uv from fragment function
|
||||
* @param one_px float This param should receive SCREEN_PIXEL_SIZE.x, as SCREEN_PIXEL_SIZE
|
||||
* cannot be accessed from outside fragment() function
|
||||
*/
|
||||
float drawLine(vec2 p1, vec2 p2, float stroke, vec2 uv, float one_px)
|
||||
{
|
||||
float r = 0.;
|
||||
|
||||
// get dist between points
|
||||
float d = distance(p1, p2);
|
||||
|
||||
// get dist between current pixel and p1
|
||||
float duv = distance(p1, uv);
|
||||
|
||||
//if point is on line, according to dist, it should match current uv
|
||||
r = 1.-floor(1.-(stroke * one_px)+distance (mix(p1, p2, clamp(duv/d, 0., 1.)), uv));
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
/**
|
||||
* Draws a triangle
|
||||
*/
|
||||
float drawTriangle(vec2 p1, vec2 p2, vec2 p3, float stroke, vec2 uv, float one_px) {
|
||||
return drawLine(p1, p2, stroke, uv, one_px)
|
||||
+ drawLine(p2, p3, stroke, uv, one_px)
|
||||
+ drawLine(p3, p1, stroke, uv, one_px);
|
||||
}
|
||||
|
||||
/**
|
||||
* Draws a triangle with no baseline
|
||||
*/
|
||||
float drawArrowTriangle(vec2 p1, vec2 p2, vec2 p3, float stroke, vec2 uv, float one_px) {
|
||||
return drawLine(p1, p2, stroke, uv, one_px)
|
||||
+ drawLine(p3, p1, stroke, uv, one_px);
|
||||
}
|
53
shaders/trigonometry.gdshaderinc
Normal file
53
shaders/trigonometry.gdshaderinc
Normal file
@ -0,0 +1,53 @@
|
||||
const vec2 CENTER = vec2(.5, .5);
|
||||
|
||||
/**
|
||||
* Rotate a shape
|
||||
*/
|
||||
vec2 rotate(vec2 origin, vec2 destination, float angleInDegrees) {
|
||||
float angle_radians = radians(angleInDegrees);
|
||||
float cosTheta = cos(angle_radians);
|
||||
float sinTheta = sin(angle_radians);
|
||||
|
||||
// Translate the destination to be relative to the origin
|
||||
vec2 translatedDestination = destination - origin;
|
||||
|
||||
// Apply the rotation transformation
|
||||
vec2 rotatedDestination;
|
||||
rotatedDestination.x = cosTheta * translatedDestination.x - sinTheta * translatedDestination.y;
|
||||
rotatedDestination.y = sinTheta * translatedDestination.x + cosTheta * translatedDestination.y;
|
||||
|
||||
// Translate the rotated destination back to its original position
|
||||
rotatedDestination += origin;
|
||||
|
||||
return rotatedDestination;
|
||||
}
|
||||
|
||||
/**
|
||||
* atan2 as defined in other languages
|
||||
*/
|
||||
float atan2(float y, float x) {
|
||||
if (x > 0.0) {
|
||||
return atan(y / x);
|
||||
} else if (x < 0.0) {
|
||||
if (y >= 0.0) {
|
||||
return atan(y / x) + PI;
|
||||
} else {
|
||||
return atan(y / x) - PI;
|
||||
}
|
||||
} else {
|
||||
if (y > 0.0) {
|
||||
return PI / 2.;
|
||||
} else if (y < 0.0) {
|
||||
return -PI / 2.;
|
||||
} else {
|
||||
return 0.0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Angle in degrees between 2 coords
|
||||
*/
|
||||
float getAngleInDegrees(vec2 v1, vec2 v2) {
|
||||
return atan2(v1.y - v2.y, v1.x - v2.x) * (180. / PI);
|
||||
}
|
Loading…
Reference in New Issue
Block a user