Преглед на файлове

Add screenspace example (#49)

* Add screenspace example

* Add camera controls to screenspace example
Artsiom Trubchyk преди 11 месеца
родител
ревизия
f0cca946bd

BIN
assets/images/pattern_circle.png


+ 28 - 0
assets/models/kenney_prototype-kit/License.txt

@@ -0,0 +1,28 @@
+	
+
+	Prototype Kit (1.0)
+
+	Created/distributed by Kenney (www.kenney.nl)
+	Creation date: 28-08-2024 09:59
+	
+			------------------------------
+
+	License: (Creative Commons Zero, CC0)
+	http://creativecommons.org/publicdomain/zero/1.0/
+
+	You can use this content for personal, educational, and commercial purposes.
+
+	Support by crediting 'Kenney' or 'www.kenney.nl' (this is not a requirement)
+
+			------------------------------
+
+	• Website : www.kenney.nl
+	• Donate  : www.kenney.nl/donate
+
+	• Patreon : patreon.com/kenney
+	
+	Follow on social media for updates:
+
+	• Twitter:   twitter.com/KenneyNL
+	• Instagram: instagram.com/kenney_nl
+	• Mastodon:  mastodon.gamedev.place/@kenney

BIN
assets/models/kenney_prototype-kit/Textures/colormap.png


BIN
assets/models/kenney_prototype-kit/crate-color.glb


+ 1 - 1
examples/_main/examples.lua

@@ -15,7 +15,7 @@ examples["gui"] = {
 	"healthbar"
 }
 examples["input"] = { "move", "text", "down_duration", "mouse_and_touch" }
-examples["material"] = { "vertexcolor", { name = "unlit", nobg = true }, "uvgradient", "noise" }
+examples["material"] = { "vertexcolor", { name = "unlit", nobg = true }, "uvgradient", "noise", { name = "screenspace", nobg = true } }
 examples["particles"] = { "confetti", "particlefx", "modifiers", "fire_and_smoke" }
 examples["sound"] = { "music", "fade_in_out", "panning" }
 examples["render"] = { "camera", "screen_to_world" }

+ 6 - 0
examples/_main/loader.go

@@ -448,3 +448,9 @@ embedded_components {
   data: "collection: \"/examples/gui/healthbar/healthbar.collection\"\n"
   ""
 }
+embedded_components {
+  id: "material/screenspace"
+  type: "collectionproxy"
+  data: "collection: \"/examples/material/screenspace/screenspace.collection\"\n"
+  ""
+}

+ 93 - 0
examples/material/screenspace/screenspace.collection

@@ -0,0 +1,93 @@
+name: "screenspace"
+scale_along_z: 1
+embedded_instances {
+  id: "camera"
+  data: "components {\n"
+  "  id: \"main\"\n"
+  "  component: \"/examples/material/screenspace/screenspace.script\"\n"
+  "}\n"
+  "embedded_components {\n"
+  "  id: \"camera\"\n"
+  "  type: \"camera\"\n"
+  "  data: \"aspect_ratio: 1.0\\n"
+  "fov: 0.7854\\n"
+  "near_z: 0.1\\n"
+  "far_z: 1000.0\\n"
+  "auto_aspect_ratio: 1\\n"
+  "\"\n"
+  "}\n"
+  ""
+  position {
+    y: 0.25
+  }
+  rotation {
+    x: -0.25881904
+    w: 0.9659258
+  }
+}
+embedded_instances {
+  id: "root"
+  children: "crate"
+  children: "crate_selected"
+  data: ""
+}
+embedded_instances {
+  id: "crate"
+  data: "embedded_components {\n"
+  "  id: \"model\"\n"
+  "  type: \"model\"\n"
+  "  data: \"mesh: \\\"/assets/models/kenney_prototype-kit/crate-color.glb\\\"\\n"
+  "name: \\\"{{NAME}}\\\"\\n"
+  "materials {\\n"
+  "  name: \\\"colormap\\\"\\n"
+  "  material: \\\"/examples/material/screenspace/screenspace.material\\\"\\n"
+  "  textures {\\n"
+  "    sampler: \\\"texture0\\\"\\n"
+  "    texture: \\\"/assets/models/kenney_prototype-kit/Textures/colormap.png\\\"\\n"
+  "  }\\n"
+  "  textures {\\n"
+  "    sampler: \\\"texture_pattern\\\"\\n"
+  "    texture: \\\"/assets/images/pattern_circle.png\\\"\\n"
+  "  }\\n"
+  "}\\n"
+  "\"\n"
+  "}\n"
+  ""
+  position {
+    x: 0.5
+  }
+  rotation {
+    y: 0.6087614
+    w: 0.7933533
+  }
+}
+embedded_instances {
+  id: "crate_selected"
+  data: "embedded_components {\n"
+  "  id: \"model\"\n"
+  "  type: \"model\"\n"
+  "  data: \"mesh: \\\"/assets/models/kenney_prototype-kit/crate-color.glb\\\"\\n"
+  "name: \\\"{{NAME}}\\\"\\n"
+  "materials {\\n"
+  "  name: \\\"colormap\\\"\\n"
+  "  material: \\\"/examples/material/screenspace/screenspace.material\\\"\\n"
+  "  textures {\\n"
+  "    sampler: \\\"texture0\\\"\\n"
+  "    texture: \\\"/assets/models/kenney_prototype-kit/Textures/colormap.png\\\"\\n"
+  "  }\\n"
+  "  textures {\\n"
+  "    sampler: \\\"texture_pattern\\\"\\n"
+  "    texture: \\\"/assets/images/pattern_circle.png\\\"\\n"
+  "  }\\n"
+  "}\\n"
+  "\"\n"
+  "}\n"
+  ""
+  position {
+    x: -0.5
+  }
+  rotation {
+    y: 0.6087614
+    w: 0.7933533
+  }
+}

+ 65 - 0
examples/material/screenspace/screenspace.fp

@@ -0,0 +1,65 @@
+#version 140
+
+// Inputs should match the vertex shader's outputs.
+in vec2 var_texcoord0;
+in vec4 var_screen_texcoord;
+
+// The color texture.
+uniform lowp sampler2D texture0;
+// The pattern texture.
+uniform lowp sampler2D texture_pattern;
+
+// The user defined uniforms.
+uniform user_fp
+{
+    // pattern_opts.x - alpha, default 1.0 (set 0.0 to disable the screen space effect).
+    // pattern_opts.y - scale, default 30.0.
+    // pattern_opts.z - offset by x, default 0.0.
+    // pattern_opts.w - rotation in radians.
+    vec4 pattern_opts;
+
+    // The screen size, used to calculate the aspect ratio.
+    vec4 screen_size;
+};
+
+// The final color of the fragment.
+out lowp vec4 final_color;
+
+// Rotate 2D vector "v" by the "a" angle in radians
+vec2 rotate(vec2 v, float a)
+{
+    float s = sin(a);
+    float c = cos(a);
+    return mat2(c, s, -s, c) * v;
+}
+
+void main()
+{
+    // Sample the color texture at the fragment's texture coordinates.
+    vec4 color = texture(texture0, var_texcoord0.xy);
+
+    // Counteract the perspective correction and scale the coords.
+    vec2 pattern_coord = (var_screen_texcoord.xy / var_screen_texcoord.w) * pattern_opts.y;
+    // + Correct the aspect ratio
+    float aspect = screen_size.x / screen_size.y;
+    pattern_coord.x *= aspect;
+    // + Offset the grid horizontally
+    pattern_coord.x += pattern_opts.z;
+    // + Rotate
+    pattern_coord = rotate(pattern_coord, pattern_opts.w);
+
+    // Output the sampled color
+    if (pattern_opts.x > 0.0)
+    {
+        // Sample the pattern at the screen space texture coordinates.
+        vec4 pattern_color = texture(texture_pattern, pattern_coord);
+
+        // Blend the colors: (sRGBA*1) + (dRGBA*(1-sA))
+        final_color = pattern_color * pattern_opts.x + color * (1.0 - (pattern_color.a * pattern_opts.x));
+    }
+    else
+    {
+        // No pattern, just output the color.
+        final_color = color;
+    }
+}

+ 49 - 0
examples/material/screenspace/screenspace.material

@@ -0,0 +1,49 @@
+name: "screenspace"
+tags: "model"
+vertex_program: "/examples/material/screenspace/screenspace.vp"
+fragment_program: "/examples/material/screenspace/screenspace.fp"
+vertex_space: VERTEX_SPACE_LOCAL
+vertex_constants {
+  name: "mtx_world"
+  type: CONSTANT_TYPE_WORLD
+}
+vertex_constants {
+  name: "mtx_view"
+  type: CONSTANT_TYPE_VIEW
+}
+vertex_constants {
+  name: "mtx_proj"
+  type: CONSTANT_TYPE_PROJECTION
+}
+fragment_constants {
+  name: "pattern_opts"
+  type: CONSTANT_TYPE_USER
+  value {
+    x: 0.5
+    y: 30.0
+  }
+}
+fragment_constants {
+  name: "screen_size"
+  type: CONSTANT_TYPE_USER
+  value {
+    x: 1920.0
+    y: 1080.0
+  }
+}
+samplers {
+  name: "texture0"
+  wrap_u: WRAP_MODE_CLAMP_TO_EDGE
+  wrap_v: WRAP_MODE_CLAMP_TO_EDGE
+  filter_min: FILTER_MODE_MIN_LINEAR
+  filter_mag: FILTER_MODE_MAG_LINEAR
+  max_anisotropy: 0.0
+}
+samplers {
+  name: "texture_pattern"
+  wrap_u: WRAP_MODE_REPEAT
+  wrap_v: WRAP_MODE_REPEAT
+  filter_min: FILTER_MODE_MIN_LINEAR
+  filter_mag: FILTER_MODE_MAG_LINEAR
+  max_anisotropy: 0.0
+}

+ 22 - 0
examples/material/screenspace/screenspace.md

@@ -0,0 +1,22 @@
+---
+name: Screenspace (3D)
+title: Screenspace
+brief: This example shows how to create a custom material with two textures that blend together to create a pattern effect using screen space coordinates.
+scripts: screenspace.script, screenspace.vp, screenspace.fp
+---
+
+In this example, we create a new material for 3D models in which we convert vertex coordinates to screenspace to get a special effect. It may be called "surface fill", "screenspace fill" and is used, most often in combination with outlines, to highlight objects in 3D games or indicate their status. 
+
+We added two game objects and two models to which we assigned our new `screenspace` material. The material is based on [`unlit`](/examples/material/unlit/), but in it:
+- vertex shader: we added a conversion of the clip space position to the screen position to pass that value to the fragment shader.
+- fragment shader: we added sampling the color based on screenspace coordinates and blending into the final output color.
+- material properties: we added a new sampler to set a second texture to be used as a pattern, and user-defined uniforms to control the fragment shader.
+
+Then the script setups a perspective camera, activates it with the `acquire_camera_focus` message. The last important thing is to pass the screen size to the shader to adjust the aspect ratio:
+
+```lua
+local w, h = window.get_size()
+go.set("#model", "screen_size", vmath.vector4(w, h, 0, 0))
+```
+
+The shaders are written in GLSL 1.40, which is available from Defold 1.9.2. The model used in this example is from Kenney's [Prototype Pack](https://kenney.nl/assets/prototype-kit), licensed under CC0.

+ 52 - 0
examples/material/screenspace/screenspace.script

@@ -0,0 +1,52 @@
+local ZOOM_SPEED = 0.2
+local ROTATION_SPEED = 0.5
+
+function init(self)
+	msg.post("@render:", "use_camera_projection")
+	msg.post(".", "acquire_input_focus")
+
+	self.yaw         = go.get(".", "euler.y") -- for camera rotation
+	self.pitch       = go.get(".", "euler.x") -- for camera rotation
+	self.offset      = go.get_position()
+	self.zoom        = 3 -- default zoom
+	self.zoom_offset = 0 -- modification from default zoom
+	self.time        = 0 -- for pattern animation
+
+	-- The model with the pattern - we enabled the effect, 0.5 is the intensity (alpha)
+	go.set("/crate_selected#model", "pattern_opts.x", 0.5)
+	-- + add 70 degrees to the rotation
+	go.set("/crate_selected#model", "pattern_opts.w", math.rad(70))
+
+	-- The normal model - the 0.0 value disables the effect
+	go.set("/crate#model", "pattern_opts.x", 0)
+end
+
+function update(self, dt)
+	-- Camera controls
+	local camera_yaw      = vmath.quat_rotation_y(math.rad(self.yaw))
+	local camera_pitch    = vmath.quat_rotation_x(math.rad(self.pitch))
+	local camera_rot      = camera_yaw * camera_pitch
+	local camera_position = self.offset + vmath.rotate(camera_rot, vmath.vector3(0, 0, self.zoom + self.zoom_offset))
+	go.set_position(camera_position)
+	go.set_rotation(camera_rot)
+
+	-- Animate the pattern by changing the z value
+	self.time = self.time - dt
+	go.set("/crate_selected#model", "pattern_opts.z", self.time)
+
+	-- The shader uses the screen size to calculate the aspect ratio.
+	-- In a real game, you'd set this in the render script globally for all materials.
+	local w, h = window.get_size()
+	go.set("/crate_selected#model", "screen_size", vmath.vector4(w, h, 0, 0))
+end
+
+function on_input(self, action_id, action)
+	if action_id == hash("touch") then
+		self.yaw   = self.yaw   - action.dx * ROTATION_SPEED
+		self.pitch = self.pitch + action.dy * ROTATION_SPEED
+	elseif action_id == hash("wheel_up") then
+		self.zoom_offset = self.zoom_offset - ZOOM_SPEED
+	elseif action_id == hash("wheel_down") then
+		self.zoom_offset = self.zoom_offset + ZOOM_SPEED
+	end
+end

+ 47 - 0
examples/material/screenspace/screenspace.vp

@@ -0,0 +1,47 @@
+#version 140
+
+// The model's vertex position and texture coordinates.
+in vec4 position;
+in vec2 texcoord0;
+
+// The projection, view and world matrices.
+uniform general_vp
+{
+    mat4 mtx_world;
+    mat4 mtx_view;
+    mat4 mtx_proj;
+};
+
+// The output of a vertex shader are passed to the fragment shader.
+// The texture coordinates of the vertex.
+out vec2 var_texcoord0;
+
+// The screen texture coordinates of the vertex.
+out vec4 var_screen_texcoord;
+
+// Converts the clip space position to the screen position.
+vec4 clip_to_screen(vec4 pos)
+{
+    // Position is [-w,w], convert to [-0.5w,0.5w]
+    vec4 o = pos * 0.5;
+
+    // Convert from [-0.5w + 0.5w,0.5w + 0.5w] to [0,w]
+    o.xy = vec2(o.x, o.y) + o.w;
+
+    // Keep "zw" as it is
+    o.zw = pos.zw;
+    return o;
+}
+
+void main()
+{
+    // Pass the texture coordinates to the fragment shader.
+    var_texcoord0 = texcoord0;
+
+    // Transform the vertex position to clip space.
+    vec4 vertex_pos = mtx_proj * mtx_view * mtx_world * vec4(position.xyz, 1.0);
+    gl_Position = vertex_pos;
+
+    // Convert the clip space position to the screen position and pass the value to the fragment shader.
+    var_screen_texcoord = clip_to_screen(vertex_pos);
+}

+ 1 - 0
examples/material/unlit/unlit.md

@@ -1,4 +1,5 @@
 ---
+name: Unlit (3D)
 title: Unlit
 brief: This example demonstrates how to create and apply an custom non-lit material to a 3D model.
 scripts: unlit.script, unlit.vp, unlit.fp

+ 8 - 0
input/game.input_binding

@@ -26,6 +26,14 @@ mouse_trigger {
   input: MOUSE_BUTTON_LEFT
   action: "touch"
 }
+mouse_trigger {
+  input: MOUSE_WHEEL_UP
+  action: "wheel_up"
+}
+mouse_trigger {
+  input: MOUSE_WHEEL_DOWN
+  action: "wheel_down"
+}
 touch_trigger {
   input: TOUCH_MULTI
   action: "multitouch"