mirror of
https://github.com/jcreek/CosmicClash.git
synced 2026-09-10 16:04:04 +00:00
feat(rendering): add cinematic post processing
This commit is contained in:
@@ -1,9 +1,35 @@
|
||||
[gd_scene load_steps=2 format=3]
|
||||
[gd_scene load_steps=5 format=3]
|
||||
|
||||
[ext_resource type="Script" path="res://scripts/ship_camera.gd" id="1_rig"]
|
||||
[ext_resource type="Shader" path="res://shaders/post_process.gdshader" id="2_post"]
|
||||
|
||||
[sub_resource type="CameraAttributesPractical" id="CameraAttributes_gameplay"]
|
||||
dof_blur_far_enabled = true
|
||||
dof_blur_far_distance = 24.0
|
||||
dof_blur_far_transition = 14.0
|
||||
dof_blur_near_enabled = true
|
||||
dof_blur_near_distance = 1.25
|
||||
dof_blur_near_transition = 0.75
|
||||
dof_blur_amount = 0.08
|
||||
|
||||
[sub_resource type="ShaderMaterial" id="ShaderMaterial_post"]
|
||||
shader = ExtResource("2_post")
|
||||
|
||||
[node name="ShipCameraRig" type="Node3D"]
|
||||
script = ExtResource("1_rig")
|
||||
|
||||
[node name="Camera3D" type="Camera3D" parent="."]
|
||||
current = true
|
||||
attributes = SubResource("CameraAttributes_gameplay")
|
||||
|
||||
[node name="PostProcess" type="CanvasLayer" parent="."]
|
||||
layer = 0
|
||||
|
||||
[node name="PostFX" type="ColorRect" parent="PostProcess"]
|
||||
material = SubResource("ShaderMaterial_post")
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
mouse_filter = 2
|
||||
|
||||
@@ -36,6 +36,7 @@ var target: Ship:
|
||||
var ball_cam_enabled := true
|
||||
|
||||
@onready var camera: Camera3D = $Camera3D
|
||||
@onready var post_material: ShaderMaterial = $PostProcess/PostFX.material as ShaderMaterial
|
||||
|
||||
var _ball: Node3D
|
||||
# Smoothed horizontal direction from ball to ship; the ball-cam orbits along
|
||||
@@ -43,6 +44,7 @@ var _ball: Node3D
|
||||
# raw position (which whips when ball and ship are close together).
|
||||
var _orbit_dir := Vector3.BACK
|
||||
var _turbo_blend := 0.0
|
||||
var _speed_blend := 0.0
|
||||
var _effective_distance := 8.0
|
||||
var _shake_strength := 0.0
|
||||
var _shake_noise := FastNoiseLite.new()
|
||||
@@ -175,11 +177,17 @@ func _update_speed_feel(delta: float) -> void:
|
||||
var feel_t := 1.0 - exp(-feel_smoothing * delta)
|
||||
var turbo_target := 1.0 if target.is_turbo_active() else 0.0
|
||||
_turbo_blend = lerpf(_turbo_blend, turbo_target, feel_t)
|
||||
var target_fov := base_fov + target.get_speed_ratio() * speed_fov_add + _turbo_blend * turbo_fov_kick
|
||||
_speed_blend = lerpf(_speed_blend, target.get_speed_ratio(), feel_t)
|
||||
var target_fov := base_fov + _speed_blend * speed_fov_add + _turbo_blend * turbo_fov_kick
|
||||
camera.fov = lerpf(camera.fov, target_fov, feel_t)
|
||||
_effective_distance = lerpf(
|
||||
_effective_distance, camera_distance + _turbo_blend * turbo_distance_kick, feel_t
|
||||
)
|
||||
post_material.set_shader_parameter(
|
||||
"motion_blur_amount", _speed_blend * _speed_blend * 0.006 + _turbo_blend * 0.006
|
||||
)
|
||||
post_material.set_shader_parameter("chromatic_aberration", _turbo_blend * 0.007)
|
||||
post_material.set_shader_parameter("vignette_strength", 0.22 + _turbo_blend * 0.16)
|
||||
|
||||
|
||||
func _on_target_ball_contact(intensity: float, _world_position: Vector3) -> void:
|
||||
@@ -224,7 +232,17 @@ func begin_goal_cut(goal_position: Vector3) -> void:
|
||||
camera.global_position = _goal_cut_position
|
||||
camera.fov = 58.0
|
||||
camera.look_at(_goal_cut_look_at, Vector3.UP)
|
||||
# A hard, stationary cinematic camera must not inherit the scoring frame's
|
||||
# ship-speed smear or turbo chroma. Keep only a deliberate cinematic edge.
|
||||
post_material.set_shader_parameter("motion_blur_amount", 0.0)
|
||||
post_material.set_shader_parameter("chromatic_aberration", 0.0)
|
||||
post_material.set_shader_parameter("vignette_strength", 0.3)
|
||||
|
||||
|
||||
func end_goal_cut() -> void:
|
||||
_goal_cut_active = false
|
||||
_speed_blend = 0.0
|
||||
_turbo_blend = 0.0
|
||||
post_material.set_shader_parameter("motion_blur_amount", 0.0)
|
||||
post_material.set_shader_parameter("chromatic_aberration", 0.0)
|
||||
post_material.set_shader_parameter("vignette_strength", 0.22)
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
shader_type canvas_item;
|
||||
render_mode unshaded;
|
||||
|
||||
uniform sampler2D screen_texture : hint_screen_texture, filter_linear_mipmap;
|
||||
uniform float motion_blur_amount : hint_range(0.0, 0.03) = 0.0;
|
||||
uniform float chromatic_aberration : hint_range(0.0, 0.02) = 0.0;
|
||||
uniform float vignette_strength : hint_range(0.0, 1.0) = 0.22;
|
||||
|
||||
|
||||
vec4 motion_sample(vec2 uv) {
|
||||
// A restrained centre-directed velocity smear. The camera script scales it
|
||||
// with ship speed/turbo; mipmapped screen reads soften the outer samples.
|
||||
if (motion_blur_amount < 0.0001) {
|
||||
return textureLod(screen_texture, uv, 0.0);
|
||||
}
|
||||
vec2 step_uv = (uv - vec2(0.5)) * motion_blur_amount;
|
||||
vec4 color = textureLod(screen_texture, uv, 0.0) * 0.36;
|
||||
color += textureLod(screen_texture, uv - step_uv * 0.35, 0.5) * 0.25;
|
||||
color += textureLod(screen_texture, uv - step_uv * 0.7, 1.0) * 0.21;
|
||||
color += textureLod(screen_texture, uv - step_uv, 1.5) * 0.18;
|
||||
return color;
|
||||
}
|
||||
|
||||
|
||||
void fragment() {
|
||||
vec2 radial = UV - vec2(0.5);
|
||||
vec2 chroma = radial * chromatic_aberration;
|
||||
vec4 center = motion_sample(UV);
|
||||
vec3 color = center.rgb;
|
||||
// The branch is driven by one uniform for the whole draw, so non-turbo
|
||||
// frames avoid eight redundant screen taps without causing warp divergence.
|
||||
if (chromatic_aberration > 0.0001) {
|
||||
color.r = motion_sample(UV + chroma).r;
|
||||
color.b = motion_sample(UV - chroma).b;
|
||||
}
|
||||
|
||||
// Aspect-correct vignette, always subtle and tightened slightly on turbo.
|
||||
vec2 aspect_radial = radial;
|
||||
aspect_radial.x *= SCREEN_PIXEL_SIZE.y / SCREEN_PIXEL_SIZE.x;
|
||||
float vignette = smoothstep(0.38, 0.78, length(aspect_radial));
|
||||
color *= 1.0 - vignette * vignette_strength;
|
||||
COLOR = vec4(color, 1.0);
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
uid://cx1lpjodunjhx
|
||||
@@ -23,7 +23,7 @@ The largest gap between this and a AAA-feeling product is presentation, not code
|
||||
- [x] Local lighting / dynamic GI — four local pitch lights plus SDFGI provide bounce and contact lighting for the runtime-generated arena shell; the shader's `hull_fill` stand-in is retired. (The shell is generated in `_ready`, so it cannot participate in an editor LightmapGI bake.)
|
||||
- [x] Dress `arena_03` — its asteroid field now has mining stations, debris clusters and a distant planet.
|
||||
- [x] Custom font + a real `Theme` resource for the HUD. Packaged Orbitron typography, shared label styling and themed procedural-instrument text replace `ThemeDB.fallback_font`.
|
||||
- [ ] Post-processing beyond glow: DoF, motion blur, vignette, chromatic aberration on turbo.
|
||||
- [x] Post-processing beyond glow: camera DoF plus speed-scaled motion blur, a cinematic vignette and turbo-driven chromatic aberration.
|
||||
|
||||
## Multiplayer (long term)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user