Browse Source

Improve dynamic split screen demo (#815)

Hugo Locurcio 2 years ago
parent
commit
682b933dac

+ 7 - 10
viewport/dynamic_split_screen/README.md

@@ -3,12 +3,9 @@
 This sample project showcases an implementation of dynamic
 split screen, also called Voronoi split screen.
 
-Language: [GDSL](https://docs.godotengine.org/en/latest/tutorials/shaders/shader_reference/shading_language.html) and GDScript
+Language: [Godot shader language](https://docs.godotengine.org/en/latest/tutorials/shaders/shader_reference/shading_language.html) and GDScript
 
-Renderer: GLES 2
-
-Note: An HTML5 export is testable
-[here](https://benjaminnavarro.github.io/godot_dynamic_split_screen/index.html).
+Renderer: Forward Mobile
 
 Check out this demo on the asset library: https://godotengine.org/asset-library/asset/541
 
@@ -38,14 +35,14 @@ distance otherwise.
 
 ## How to use it
 
-Open and launch the project inside the Godot engine and then
-you can use WASD keys to move the first player and IJKL keys
-to move the second one.
+Open and launch the project inside the Godot engine, then
+use WASD to move the first player (in red) and IJKL (or arrow keys)
+to move the second player (in blue).
 
-The `Cameras` node has parameters to tune the distance at
+The `camera_controller.gd` script sets parameters to tune the distance at
 which the screen splits and also the width and color of
 the splitting line.
 
 ## Screenshots
 
-![Screenshots](screenshots/splitscreen.png)
+![Screenshots](screenshots/dynamic_split_screen.webp)

+ 1 - 0
viewport/dynamic_split_screen/camera_controller.gd

@@ -32,6 +32,7 @@ extends Node3D
 @onready var camera1 = viewport1.get_node(^"Camera1")
 @onready var camera2 = viewport2.get_node(^"Camera2")
 
+var viewport_base_height = ProjectSettings.get_setting("display/window/size/viewport_height")
 
 func _ready():
 	_on_size_changed()

+ 1 - 1
viewport/dynamic_split_screen/player.gd

@@ -3,7 +3,7 @@ extends CharacterBody3D
 # Moves the player
 
 @export_range(1, 2) var player_id: int = 1
-@export var walk_speed: float = 2.5
+@export var walk_speed: float = 2
 
 
 func _physics_process(_delta):

+ 2 - 1
viewport/dynamic_split_screen/project.godot

@@ -82,6 +82,7 @@ common/physics_ticks_per_second=120
 
 [rendering]
 
-anti_aliasing/quality/msaa_3d=2
+renderer/rendering_method="mobile"
 environment/defaults/default_clear_color=Color(1, 1, 1, 1)
+anti_aliasing/quality/msaa_3d=2
 environment/default_environment="res://default_env.tres"

BIN
viewport/dynamic_split_screen/screenshots/dynamic_split_screen.webp


BIN
viewport/dynamic_split_screen/screenshots/splitscreen.png


+ 27 - 28
viewport/dynamic_split_screen/split_screen.gdshader

@@ -1,18 +1,17 @@
 shader_type canvas_item;
 render_mode unshaded;
 
-uniform vec2 viewport_size;         // size in pixels of the viewport. Cannot be access from the shader in GLES2
+uniform vec2 viewport_size;          // size in pixels of the viewport
 uniform sampler2D viewport1 : source_color;
 uniform sampler2D viewport2 : source_color;
-uniform bool split_active;          // true: split screen, false: use view1
-uniform vec2 player1_position;      // position of player 1 un UV coordinates
-uniform vec2 player2_position;      // position of player 2 un UV coordinates
-uniform float split_line_thickness; // width of the split boder
-uniform vec4 split_line_color;      // color of the split border
-
+uniform bool split_active;           // true: split screen, false: use view1
+uniform vec2 player1_position;       // position of player 1 un UV coordinates
+uniform vec2 player2_position;       // position of player 2 un UV coordinates
+uniform float split_line_thickness : hint_range(0, 10, 0.1);  // width of the split boder
+uniform vec3 split_line_color : source_color;       // color of the split border
 
 // from https://stackoverflow.com/questions/15276454/is-it-possible-to-draw-line-thickness-in-a-fragment-shader
-float distanceToLine(vec2 p1, vec2 p2, vec2 point) {
+float distance_to_line(vec2 p1, vec2 p2, vec2 point) {
 	float a = p1.y - p2.y;
 	float b = p2.x - p1.x;
 	return abs(a * point.x + b * point.y + p1.x * p2.y - p2.x * p1.y) / sqrt(a * a + b * b);
@@ -27,8 +26,8 @@ void fragment() {
 
 	if (split_active) {
 		vec2 dx = player2_position - player1_position;
-		float split_slope;
 
+		float split_slope;
 		if (dx.y != 0.0) {
 			split_slope = dx.x / dx.y;
 		} else {
@@ -38,29 +37,29 @@ void fragment() {
 		vec2 split_origin = vec2(0.5, 0.5);
 		vec2 split_line_start = vec2(0.0, height * ((split_origin.x - 0.0) * split_slope + split_origin.y));
 		vec2 split_line_end = vec2(width, height * ((split_origin.x - 1.0) * split_slope + split_origin.y));
-		float distance_to_split_line = distanceToLine(split_line_start, split_line_end, vec2(UV.x * width, UV.y * height));
 
-		// Draw split border if close enough
-		if (distance_to_split_line < split_line_thickness) {
-			COLOR = split_line_color;
-		} else {
-			float split_current_y = (split_origin.x - UV.x) * split_slope + split_origin.y;
-			float split_player1_position_y = (split_origin.x - player1_position.x) * split_slope + split_origin.y;
+		float split_current_y = (split_origin.x - UV.x) * split_slope + split_origin.y;
+		float split_player1_position_y = (split_origin.x - player1_position.x) * split_slope + split_origin.y;
 
-			// Check on which side of the split UV is and select the proper view
-			if (UV.y > split_current_y) {
-				if (player1_position.y > split_player1_position_y) {
-					COLOR = vec4(view1, 1.0);
-				} else {
-					COLOR = vec4(view2, 1.0);
-				}
+		// Check on which side of the split UV is and select the proper view.
+		if (UV.y > split_current_y) {
+			if (player1_position.y > split_player1_position_y) {
+				COLOR = vec4(view1, 1.0);
 			} else {
-				if (player1_position.y < split_player1_position_y) {
-					COLOR = vec4(view1, 1.0);
-				} else {
-					COLOR = vec4(view2, 1.0);
-				}
+				COLOR = vec4(view2, 1.0);
 			}
+		} else {
+			if (player1_position.y < split_player1_position_y) {
+				COLOR = vec4(view1, 1.0);
+			} else {
+				COLOR = vec4(view2, 1.0);
+			}
+		}
+
+		float distance_to_split_line = distance_to_line(split_line_start, split_line_end, vec2(UV.x * width, UV.y * height));
+		if (distance_to_split_line < split_line_thickness) {
+			// Draw antialiased split line.
+			COLOR.rgb = mix(split_line_color, COLOR.rgb, distance_to_split_line / split_line_thickness);
 		}
 	} else {
 		COLOR = vec4(view1, 1.0);

+ 46 - 6
viewport/dynamic_split_screen/split_screen.tscn

@@ -1,4 +1,4 @@
-[gd_scene load_steps=60 format=3 uid="uid://dksa68cph6y4b"]
+[gd_scene load_steps=63 format=3 uid="uid://dksa68cph6y4b"]
 
 [ext_resource type="Script" path="res://camera_controller.gd" id="2"]
 [ext_resource type="Shader" path="res://split_screen.gdshader" id="3"]
@@ -15,21 +15,26 @@ sky_material = SubResource("ProceduralSkyMaterial_16la2")
 [sub_resource type="Environment" id="Environment_vdrvu"]
 background_mode = 2
 sky = SubResource("Sky_i64ko")
+ambient_light_source = 2
+ambient_light_color = Color(0.79, 0.8775, 1, 1)
+ambient_light_sky_contribution = 0.0
+ambient_light_energy = 0.33
 tonemap_mode = 2
 glow_enabled = true
 
 [sub_resource type="ShaderMaterial" id="1"]
 shader = ExtResource("3")
+shader_parameter/viewport_size = null
+shader_parameter/split_active = false
 shader_parameter/player1_position = null
 shader_parameter/player2_position = null
-shader_parameter/split_active = null
-shader_parameter/split_line_color = null
-shader_parameter/split_line_thickness = null
-shader_parameter/viewport_size = null
+shader_parameter/split_line_thickness = 10.0
+shader_parameter/split_line_color = Vector3(0, 1, 0)
 
 [sub_resource type="CapsuleMesh" id="2"]
 radius = 0.375
 height = 1.75
+rings = 4
 
 [sub_resource type="StandardMaterial3D" id="3"]
 albedo_color = Color(0.933333, 0.0784314, 0.0784314, 1)
@@ -38,9 +43,24 @@ albedo_color = Color(0.933333, 0.0784314, 0.0784314, 1)
 radius = 0.375
 height = 1.75
 
+[sub_resource type="TorusMesh" id="TorusMesh_abtrc"]
+inner_radius = 0.4
+outer_radius = 0.6
+ring_segments = 6
+
+[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_63nwq"]
+albedo_color = Color(0.5, 0.5, 0.5, 1)
+emission_enabled = true
+emission = Color(1, 0, 0, 1)
+
 [sub_resource type="StandardMaterial3D" id="5"]
 albedo_color = Color(0.0784314, 0.411765, 0.933333, 1)
 
+[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_wi7e2"]
+albedo_color = Color(0.5, 0.5, 0.5, 1)
+emission_enabled = true
+emission = Color(0.12549, 0.501961, 1, 1)
+
 [sub_resource type="StandardMaterial3D" id="6"]
 
 [sub_resource type="PlaneMesh" id="7"]
@@ -183,12 +203,15 @@ albedo_color = Color(0.791675, 0.946163, 0.317723, 1)
 
 [node name="DirectionalLight3D" type="DirectionalLight3D" parent="."]
 transform = Transform3D(-0.866025, -0.433013, 0.25, 0, 0.5, 0.866025, -0.5, 0.75, -0.433013, 0, 0, 0)
+light_energy = 0.75
 shadow_enabled = true
+shadow_bias = 0.03
+shadow_blur = 2.0
 directional_shadow_mode = 0
 directional_shadow_split_3 = 0.25
 directional_shadow_blend_splits = true
 directional_shadow_fade_start = 1.0
-directional_shadow_max_distance = 25.0
+directional_shadow_max_distance = 12.0
 
 [node name="WorldEnvironment" type="WorldEnvironment" parent="."]
 environment = SubResource("Environment_vdrvu")
@@ -235,6 +258,14 @@ surface_material_override/0 = SubResource("3")
 [node name="CollisionShape3D" type="CollisionShape3D" parent="Player1"]
 shape = SubResource("4")
 
+[node name="MeshInstance3D" type="MeshInstance3D" parent="Player1"]
+transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -0.6, 0)
+mesh = SubResource("TorusMesh_abtrc")
+surface_material_override/0 = SubResource("StandardMaterial3D_63nwq")
+
+[node name="OmniLight3D" type="OmniLight3D" parent="Player1"]
+light_color = Color(1, 0, 0, 1)
+
 [node name="Player2" type="CharacterBody3D" parent="."]
 transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 4.184, 0.875, 3.019)
 script = ExtResource("5")
@@ -247,6 +278,15 @@ surface_material_override/0 = SubResource("5")
 [node name="CollisionShape3D" type="CollisionShape3D" parent="Player2"]
 shape = SubResource("4")
 
+[node name="MeshInstance3D2" type="MeshInstance3D" parent="Player2"]
+transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -0.7, 0)
+mesh = SubResource("TorusMesh_abtrc")
+skeleton = NodePath("../../Player1")
+surface_material_override/0 = SubResource("StandardMaterial3D_wi7e2")
+
+[node name="OmniLight3D" type="OmniLight3D" parent="Player2"]
+light_color = Color(0.12549, 0.501961, 1, 1)
+
 [node name="Ground" type="StaticBody3D" parent="."]
 
 [node name="Mesh" type="MeshInstance3D" parent="Ground"]