Light, Shadow, Sprinting and TAA
Table of contents
GitHub repository and specific commit corresponding to this blog post:
https://github.com/Zacrain/a-fancy-tree commit hash: da31658.
If you've followed my blog so far, you know that the world around the fancy tree was still a bit boring and ugly. Furthermore, there were no shadows cast onto the ground even though we had light. So, I made some quick improvements, hoping this would alleviate these issues. Meanwhile, I've also added two more user inputs, one for sprinting and the other for quitting the game.
Better Sky & Light
My first idea to create a better sky and environment was to add a sky dome, which was a semi-sphere painted blue with some clouds on it. However, it still looked bad after I added it. Furthermore, I encountered difficulties with the lighting of that dome. So, I discarded it and looked for a different way. But at least I learned something new about Godot and its interaction with Blender: it is possible to let Godot automatically add collision shapes to an imported Blender model. To do that, you have to add some text suffixes to the meshes in your model. I need to emphasize this: don't add the text suffixes to the filename, but to the mesh names in your Blender scene! For example, like this for a cube:
Here, I added just a "-col" to the mesh's name. This will automatically create a collision object in the shape of the mesh (in this case, a cube). There are plenty of more options that help to tailor the Blender import to your needs in Godot. Luckily for us, this was documented in Godot's manual:
https://docs.godotengine.org/en/stable/tutorials/assets_pipeline/importing_3d_scenes/node_type_customization.html
After I discarded the sky-dome idea, I assumed that it must be possible to get a similar sky like in the editor view. And of course, this is possible in Godot. It's actually pretty easy.
I found the crucial information again in Godot's documentation here:
https://docs.godotengine.org/en/stable/tutorials/3d/environment_and_post_processing.html
I went with the recommended way of instantiating a WorldEnvironment
node together with a DirectionalLight3D
node by using the preview settings of the three-dots menu above the editor viewport. Then I tweaked the colours of the light and environment a bit. Specifically, I tinted the ground blue to create the illusion that the player is surrounded by water (which is a long-term goal of this project anyway). Also, I used "filmic" tone mapping for a vivid appearance of colours. Getting shadows is as easy as toggling a checkbox in the directional light node on:
The results looked really good already. I'll provide some screenshots further down. Still, the edges in the image looked jagged. And most gamers probably already know what that means: we need anti-aliasing. For that, I enabled temporal anti-aliasing (TAA) in the anti-aliasing section under "Rendering" in my project settings.
When hovering over the TAA option, I learned that this feature is currently still experimental. However, until I encounter problems, I'll use it for now as I like the visual results while having a rather low performance impact (not the lowest though). As you can see in the image, there are also other options available.
These are the results:
I noticed that the trees look distorted, stretched diagonally depending on the view. My suspicion is that this has to do with the field of view. Definitely something to look into in the future.
Sprinting and Quitting Controls
Similar to how I've implemented ground control movements before, I added the ability to sprint. This grew from the need to traverse the map much faster as I've enlarged the ground dimensions (which was a byproduct of adding a sky dome before).
Right in the _physics_process()
method of my FPVPlayer
class, I added the following lines to toggle sprinting on or off:
if (input.is_action_pressed(action_name_move_sprint)) // Toggle sprinting on/off.
move_sprint_toggle = 1.0;
else
move_sprint_toggle = 0.0;
This is a multiplicative factor of type double
and is used when computing the translational velocity on the ground:
// Set Ground movement velocity.
target_vel.x = move_direction.x * (move_speed + move_sprint_toggle * move_sprint_speed);
target_vel.z = move_direction.z * (move_speed + move_sprint_toggle * move_sprint_speed);
As you can see, the move_sprint_speed
(which is bound so that it becomes configurable from the inspector) gets added to the move_speed
whenever move_sprint_toggle
is 1. Otherwise, the result of the multiplication will just be 0, and in that case, only the default move speed is used.
Last but not least, it was a bit tedious to quit the game before because I had to hit ALT+F4 every time since my mouse cursor was hidden. (Technically, you could also focus on another window and move your mouse cursor to the "x" of the game's window, but that's even more tedious.) This became simpler by just hitting the escape key, which I've also mapped via the input map of the project settings. Processing this input is done in my MainNode
though and not in the FPVPlayer
. There is a simple command to quit the game by quitting the scene tree:
void MainNode::_process(double delta) {
// TODO: preliminary polling? Use input events instead?
if (input.is_action_just_pressed(action_name_control_quit)) {
SceneTree* scene_tree = get_tree();
scene_tree->quit();
}
}
As you can see in the comment of the method, I think it's not the best way to just poll the player input. Using input events should be much more efficient. But for now this will suffice. In order for this to work you have to include the definitions of the scene tree:
#include <godot_cpp/classes/scene_tree.hpp>
Next Steps
I have various ideas on how to proceed next, and if I could, I would do them all simultaneously. But I can't, so I have to prioritise. These are some ideas for the first next steps:
Make the tree more fancy: create a new and better model.
Make the ground more interesting: create a hill on an island and add some textures. Maybe there is even some sort of map editor so that I wouldn't have to do it in Blender.
Add some life to the scene by making a simple bird and animating it. Program its movement and behaviour so that it flies around the scene, sometimes lands, and walks. This shouldn't be too hard to do given my current set of skills.
Create an ocean surrounding the island. For that, I suppose I have to learn a lot about shaders and maybe procedural generation, as I don't want to just "place" water but let it be added automatically depending on the player's position for better performance.
After thinking about it, I chose to go with the first point. After all, the tree shall become fancy, shall it not?
However, I've just created a donut and the Blender Guru advised to learn more and follow some more tutorials. And I guess it's a good idea to learn more about 3D modeling, texturing etc., as making a more detailed tree surely is more difficult due to the complex geometry and surface / material.
Therefore, I will do that.
Until next time!
— Zacryon