INF633 - Advanced 3D Graphics (2022-2023) - Lab Course
Github Repository | Github Page | Moodle |
Welcome to the official page for the lab sessions of INF633 for the course 2022-2023!
The goal of this project is to create, starting from a flat empty terrain, one with interesting features, objects and dynamic, living creatures all interacting together. For this, multiple edition tools will be created and basic principles of animation and crowd simulation used to give life to the virtual world.
In this page, you will find the same introduction to install and setup Unity that you can find also in the README.md of the repository at https://github.com/edualvarado/inf633-2022-2023, and also the description and content for each lab session.
Link to download: https://unity.com/download
Before even importing the lab project, let’s have a general overview of Unity. First, we first need to associate Unity with our chosen C# editor.
Edit > Preferences > External Tools
External Script Editor
to your preferred text editor.Unity uses a modular window system. That means, that each part of the interface can be reorganized and placed as you like. By default, Unity will show a similar appearance to this one:
Scene
is the parent element that serves as a environment where you place all your Game Object.In the Scene view:
Some fast tips regarding the overall workflow in Unity:
public
will be visible in the interface and editable in real-time. Although during this lab we will not focus on the code syntax and efficiency, normally it is a good practice to declared the variable as private
and serialized it to make it visible in the Unity inspector using [SerializeField]
.Open > Add project from disk
Assets
or Packages
.In this session, you will be designing and implementing brushes, allowing you to dynamically edit the terrain. Your brushes will extend the base class TerrainBrush and control terrain modifications in a restricted area around the mouse, by implementing the method draw.
The base code for this session can be found in the following file, and shows how to set all cells in the current region to a defined height.
public class SimpleBrush : TerrainBrush {
public float height = 5;
public override void draw(int x, int z) {
for (int zi = -radius; zi <= radius; zi++) {
for (int xi = -radius; xi <= radius; xi++) {
terrain.set(x + xi, z + zi, height);
}
}
}
Code Snippet 1: "Scripts/01Terrain Brushes/SimpleBrush.cs"
Copy this file to use it as a base for your own brushes, by changing the name of the copied file and class name.
// Get or set the height of the terrain at (x, z)
float terrain.get(int x, int z);
void terrain.set(int x, int z, float height);
// Get the terrain normal at (x, z)
Vector3 terrain.getNormal(float x, float z);
// Get the terrain size
Vector3 terrain.gridSize();
// Reset the whole terrain to height=0
void terrain.reset();
// Print to the Unity console (next to game tab)
print("message");
// Print to game viewport (top left)
terrain.debug.text = "message";
Code Snippet 2: Useful functions
// Access the underlying terrain
CustomTerrain terrain;
// Radius of the brush
int radius;
int terrain.brush_radius;
Code Snippet 3: Useful variables
In this second session, you will design brushes that place objects on the terrain. Here it will be trees, but you can search for or provide other objects.
The base code for this session shows how to instantiate one object where the user clicked, and four at the corners of the drawing region. Note that these types of brushes extend the InstanceBrush
class instead of the TerrainBrush one used in the previous session.
public class SimpleInstanceBrush : InstanceBrush {
public override void draw(float x, float z) {
spawnObject(x, z);
spawnObject(x - radius, z - radius);
spawnObject(x - radius, z + radius);
spawnObject(x + radius, z - radius);
spawnObject(x + radius, z + radius);
}
}
Code Snippet 4: "Scripts/02\_Instance Brushes/SimpleInstanceBrush.cs"
Like in the previous session, you can use this file as a base for your own brushes by copying it and changing the file and class names.
You can set the object that will be instantiated by drag-and-dropping a model in the Object_prefab
parameter of the terrain at run-time. A few models of trees are already in the project, in 04 - Terrain Assets > Environment > Trees
and then the file with a tree icon in one of the three sub-folders.
To remove objects, you can use the default tools provided in the original terrain editor by Unity. For this, select the terrain and go to the terrain > Paint trees
tab (in the inspector). You can then shift-click on the terrain to remove objects around your cursor. Note that this works outside of play mode in the Scene view (not in the Game view!), as opposed to the brushes you implement.
> x
free spaceh
// Spawns an object at coordinates (x, z)
void spawnObject(float x, float z);
// Returns the interpolated height of the terrain
float terrain.getInterp(float x, float z);
// Returns the steepness of the terrain
float terrain.getSteepness(float x, float z);
// Return the number of objects instantiated
int terrain.getObjectCount();
// Return the location of an object
Vector3 terrain.getObjectLoc(int index);
// ADVANCED - Return an instantiated object
// See Unity manual of TreeInstance for more details
TreeInstance terrain.getObject(int index);
Code Snippet 5: Useful functions
// Object to instantiate by default
GameObject terrain.object_prefab;
// Min and max size of the instantiated objects
float terrain.min_scale;
float terrain.max_scale;
Code Snippet 6: Useful variables
In this session, you will learn about some important concepts such as Inverse Kinematics, and learn about how to use this technique in order to fully animate one character using procedural methods.
Many animation techniques exist: Virtual characters are often animated using direct kinematics - animation clips, described by keyframes for each bone of character, that are normally played in a state machine among other clips. However, other many methods exist. Inverse Kinematics are often used to animate limbs in the other way around: Given a particular condition, IK calcutates the position and rotation of the limbs to satisfy such requirement. It is used very often as a second-layer for direct kinematic animations, for example, to place the feet of the characters correctly to the irregular ground, or to move the arm when the character takes a mug from a table.
However, you can use IK to animate whole characters as well. Procedural animations define an equation or any other parametrized system which must be followed by the animation, instead of following individual keyframes in the kinematic clips. A ball that falls due to gravity, is just another procedural animation - in this case, the ball follows a physics equation. This techniques are normally denominated as physically-based animations.
In this session, we will learn to convert this…
…to this…
All the necessary files will be in the folder 03 - Character Animation
. Let’s start!
First, let’s go to 03 - Character Animation > 00 - IK Demonstration
. In the scene, you will learn how to build a simple Fast IK algorithm. The script FabricIK.cs
contains several code snippets that you will need to complete. All the information that you need to understand the code is already included as comments along the script.
This IK technique is called Fabric IK. A forward and backward pass are used to place an end-effector on a target, while a pole is used to define one of the multiple solutions that one target may contain.
Now, we can use this knowledge to animate our quadruped:
In the hierarchy, you will find the character in Controllable Characters > Quadruped - (Procedural). It contains the hierarchy of bones, with root at Hips and the family of objects to apply IK inside IK.
All the necessary files are in 03 - Character Animation > 01 - Quadruped with full IK
.
FabricIKQuadruped.cs
: This script is in charge of implementing IK for each leg of the quadruped, given a target and a pole, exactly as before. It can be found for each leg inside IK in the character hierarchy. Once you have completed FastIK.cs
in the previous exercise, just copy-paste the code snippets to this one. The funtionality is the same. If it is correctly implemented, try moving the targets (red spheres) and the poles (yellow spheres) to see if it works!FootStepper.cs
: This file will describe the behavior of the leg. IK only says how the leg should be placed given a particular target. But when (and how) do we move the leg? Here it comes the procedural function that the legs will follow. By moving the target, in the correct moment and in a particular way, the character will be able to adapts its feet and legs given a particular motion (which is introduced in the next script).QuadrupedProceduralMotion.cs
: Once we have the legs implemented using the previous two files, all that is left is moving the character in our scene! And it will be pretty simple. Since the animation is carried out completely by IK, the only thing we need to do is to move our character like if it would be a single “floating object” though the scene - IK will do the rest. For this script, the quadruped will follow a goal, will track his head to always look at it and adapt is body (and therefore, its legs) when there is a differente in height and slope on the terrain.At the beginning, moving the goal will make the character to follow it without any animation, floating around and going inside the terrain when it gets higher. Your task is to make this character fully responsive when moving the goal. Again, these scripts contain code snippets that you will need to complete. All the information that you need to understand the code is already included as comments along the scripts.
Once you have it, think about particular applications. Characters that follow autonomously a piece of food, or animals that run away from “anti-goals” such as predators. Could we even use some learning-based approach to teach them to follow certain assets? There are many options! We will see more in the next session.
If you have time, you can try to set a character controllable by Forward Kinematics, and using IK just to adapt its feet on the ground. This character is already included in the hierarchy, Controllable Characters > Human - (Controller + IK). In the Game window, you can change the display to Display 2 to set a third-person view. You can control the humanoid using the keyboard arrows or WASD keys.
Inside 03 - Character Animation > 02 - Biped Controller with IK
you will find the script PlayerControllerIKFeetPlacementTask.cs
. All the information about this task can be found in the repo in a separated document, under the Docs
folder.
During this session, you will be given a basic setup containing simple creatures with eyes, a brain, and steering capabilities, as well as a primitive evolution method. With these, the creatures are able to gradually develop from generation to generation in order to learn how to efficiently reach their food. Your goal is to change parts of this base to extend it to your liking. Ideas of such extensions will be given below.
In order to activate the module of this session, select the terrain and activate the checkbox next to the Genetic Algo
component. When you launch the project, you should see capsules (representing animals) appearing and moving on the terrain. Their color changes from white to black as their hunger increases, and they die after some time if they do not manage to reach food in time. After letting the simulation run for a few generations, you should see the animal counter drastically increase, when some animals become smart enough to efficiently get their food.
The animals created in this session are basic creatures, made of three main components:
Animal.UpdateVision()
function, from the file 04 - Crowds and Evolution/Animal.cs
.Animal.Update()
.[5, 3, 3, 1]
passed to its constructor will generate a network with 5 input neurons, 1 output neuron, and 2 hidden layers with 3 neurons each. The details of this class are in the 04 - Crowds and Evolution/NeuralNet.cs
file.A genetic algorithm
is used to teach the animals how to steer towards their food, after multiple generations. This works by allowing the animals that reach food to reproduce, thus spreading their genes (parameters of their brain), with minor random modifications to allow evolution. The animals with inefficient genes are unable to reach food before dying, thus encouraging only the spread and evolution of successful animals. This is implemented in the file 04 - Crowds and Evolution/GeneticAlgo.cs
.
The animals simulated here are designed to be herbivores, that eat by passing over a source of food. Grass grows randomly over the terrain, creating a competition between the different animals to access the resources.
Debug.DrawRay()
to show how vision works, or lines to connect the animal to the targeted food. // Update the genetic algo simulation
void GeneticAlgo.step();
void GeneticAlgo.updateResources();
// Animal main update function
void Animal.Update();
// Animal receptors computations
void Animal.updateVision();
// Brain class of the animals
class NeuralNet;
// Mutate a brain to simulate evolution
void NeuralNet.mutate(...);
Code Snippet 7: Useful functions
The senior students of this course did some great projects. You can find videos of some of them at this link. May they inspire you!
Besides, other intersting links that can be useful: