Materials pretty much determine how your scene will appear. You can think of Materials as a bucket of visual effects that determine how an object should be rendered for the cameras. They use a shader to render maps. By adding or changing the maps, you change how the object appears.
Textures are basically images like jpg
or png
but with special metadata attached to them that tells Unity how to treat them in a scene.
You can think of materials as a way to manage the appearance of one or more objects.
You can find more details specifications in the Unity Materials and Textures documentation
Now that you've got the basic idea, we'll add a more realistic texture to the ground. Professionally made textures will include not just a simple image but several image types that handle different types of image processing. The classical way of doing this included a 'normal', 'specular', and 'diffuse' layer. Unity's current method gives you 'albedo', 'metallic', 'normal map', 'height map', and 'occlusion'.
Create a new material by going to the menu: Assets > Create > Material
(or right click in the Project window) and name it FloorMaterial
. In the Inspector, you should see that it's using Shader: Standard (we'll cover these more shortly). Now you've got a basic material that we can attach more information to.
Find WoodPlanks028_2k
(there are even higher quality versions up to 6k) in the pre_assets directory drag it into your Assets/Textures
directory. This contains images in the format they are provided by Poliigon - with a suffix describing different layers. By dragging from the Project window to the Inspector, you can assign different images to different maps of the material. If you click the circle next to the word, it will pull up a menu you can use to search everything in your assets directory.
COL2
imageAlbedo Alpha
NRM
image and click the Fix Now
button to make sure the image gets processed as a 'normal'DISP
image (not DISP16
)AO
imageWe can customize the pillar in a similar way, but this artist has provided a more complex texture. Find pillar_greekMat.mat
, move it to your Assets/Materials
directory and examine it in the Inspector. You'll see that it has Albedo, Specular, Normal Map, and a Height Map assigned. If you look at each of those images in the inspector individually, you'll see that they have a series of regions. Those regions correspond to different parts of the mesh and are mapped there during rendering. If you open it up in Krita or another image editing program, you could add graffiti or change its texture. For now, drag that material to the pillar to attach it.
Now let's add some background scenery. A common way to provide distant scenery is to use a 'skybox'. As StreetView, photosphere apps, and 360/180 cameras become more popular, there are a lot of resources available for backgrounds. We're going to use a static image captured in HDR: misty_pines_2k
.
./Assets
directory.Window > Lighting > Settings
menu and under the scene tab, drag your cubemap material onto the skybox material slot. You should see it appear in the background of your Scene ViewIf you need to adjust how close or how high the photosphere appears, you can tweak those settings using one of the first two the options described in this project which provides a great summary of the three main ways to set up backgrounds in Unity. While 'inverted normals' might sound a little complicated as you're getting started, it basically means setting up the sphere to draw the texture on the inside rather than the outside, so if you make it big enough to fit your whole scene, you can drop a standard photosphere (i.e. an equirectangular image) like you might see in Google's StreetView or could take with a smartphone app.
Shaders are specialized scripts that determine how textures will be applied across a mesh.
One of the best introductions to shaders is http://www.alanzucconi.com/2015/06/10/a-gentle-introduction-to-shaders-in-unity3d/
If we go back to our fadeCube, that can help make sense of how shader interact with scripts and give you some basic constructs that will help when you're ready for fancier stuff.
In the pre_asets directory, go into Shaders and Copy the SwordShadersProgresssion into your assets folder along with lava.png
and Displacements.png
from the textures folder. Each of these shaders adds a little more functionality to the previous one. In order to apply a shader to an object, it first needs a material to work with, so right click in your assets and create a new material. By dragging the shader onto the material, and then dragging the material onto the object, we can see what our shader is doing.
The first shader, 01SolidColorShader is about as basic as a shader can be. For every pixel where the object is located, it always returns the same color.
Shader "Custom/SolidColor" {
SubShader {
Pass {
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
struct vertInput {
float4 pos : POSITION;
};
struct vertOutput {
float4 pos : SV_POSITION;
};
vertOutput vert(vertInput input) {
vertOutput o;
o.pos = mul(UNITY_MATRIX_MVP, input.pos);
return o;
}
half4 frag(vertOutput output) : COLOR {
return half4(1.0, 0.0, 0.0, 1.0);
}
ENDCG
}
}
}
With the next shader, 02TextureShader, we can apply a texture to the material using what is called UV maps. (The U and V are the names of the axis. They are simply the X and Y coordinates of the texture we are using.). You'll notice at the top of this shader, we have a Properties section. This allows us to give the shader inputs from unity. _MainTex ("Texture", 2D) = "white" {}
means that the variable in our code will be _MainTex, the name shown in the unity inspector is "Texture", which takes in a 2D image, and if no image is supplied, it will use a white image by default. Note that we have to redeclare _MainTex as a sampler2D before we can use it. We can drag lava.png
into the Texture slot of our material to apply the texture to it. So now, we are mapping the object's 3d point to a UV coordinate which we then use to sample the texture that we give it.
While we normally think of textures as Images, to the computer, it's simply a grid of rgba values, which are represented as numbers from 0-1. So instead of passing it a picture, we can pass it a grid of values that we won't use as colors. This Displacement picture that we are using has random noise in both the red and green channels. We can use values in these channels to warp our texture. So instead of sampling the texuture with our normal UV values, we add a displacement value that we get from our Displacement picture. Since the values are always between 0 and 1, we multiply it by 2 and subtract 1 so we get values from -1 to 1, so our displacement will move in all directions. When we apply this shader to our material and drag Displacements.png
to the Warp Texture slot, we can move the Magnitude slider to see how the texure changes as we increase the effect of the warp.
While the warp is cool, it' static once you choose a Magnitude value. The last shader, 04FlowingShader, utilizes the built in _Time
variable to add animation to our shader. So instead of reading from our Displacement texture the same UV value each time, we add _Time.x*_Speed
to it so we get a flowing type effect. _Time has an x, y, and w, but they are all just the time multiplied by a constant. In order they are t/20, t, t*2, t*3. There are other constants like _SinTime
which can give you oscilating values. Information about these and other constants can be viewed here: https://docs.unity3d.com/Manual/SL-UnityShaderVariables.html
In the pre_asets directory, in the materials folder you'll find PowerUpMaterial.mat
, and in the textures folder you'll find lava.png
and Displacements.png
, and in the shader's folder named SwordShaderProgression. Bring them into your Assets directory and create another copy of your sword model in the hierarchy and name it MagmaSword
. Check to make sure the PowerUpMaterial is using the Custom/04FlowingShader
and is assigned lava
as the texture and Displacement
as the warp texture. If you've got everything connected you, should see an animated shader effect in the Scene window. If you click on the PowerUpMaterial, you can also play around with the Magnitude and Speed sliders to see how it affects the texture. It might be easier to see on a plain cube, so you can create a new 3D cube in your scene and drag the material onto it.
To connect this with our two-handed sword powerup effect:
BladeController.cs
script and find your EnablePowerUp
and DisablePowerUp
functionssimpleModel
and powerModel
game objectsBladeController
compenent in the InspectorsimpleModel.SetActive (false);
powerModel.SetActive (true);
simpleModel.SetActive (false);
powerModel.SetActive (true);
Though sometimes sounds are a last thought, they can really add to a sense of immersion in a scene. Unity gives you a lot of power for creating effects by playing the audio at a particular place in the scene. It's volume will depend on how far you are from it in the scene.
Much like images you can load sounds as assets simply by moving supported files such as mp3
or wav
to the Assets directory. Once there, you can attach sounds to objects by adding an Audio Source
component (or you can reference them directly with a script as an AudioClip
you invoke at a certain time). Sounds that are played using PlayClipAtPoint
will play from a position in the scene and can be a very effective technique for drawing a users attention to a particular place in the scene.
Open up your BladeController script and add the following variable declaration:
[SerializeField]
AudioClip cutSound;
Then drag the cut sound audio clip to your script in the Inspector.
In the OnCollisionEnter
function, play the sound.
AudioSource.PlayClipAtPoint (cutSound, gameObject.transform.position);
Sliceable
and a Strong
objectAs a bonus, if you're looking for some additional effects to add to your sword, Unity has a powerful engine for adding particles and trailing lines to objects. To add a Trail to your sword swing, right-click the sword parent in the Hierarchy and select Effects > Trail
(or Particle, or Line). Then you'll get a child object with the relevant renderer attached. You can change its origin just like everything else in the scene by moving its Transform.
There's lots of fun options you explore within the effects menus. These often come with a performance cost but used carefully can provide powerful enhancements to your experience.
If you made it all the way through - congratulations! If you want one more challenge, see if you can write a script that will throw new sliceable objects towards the pillar. This process of generating objects is often referred to as a 'spawner'. You can use spatial audio to signal to the user that an object is about to fly towards them.
As a hint, you'll probably want some function that chooses which prefab to use and then makes sure that each prefab has a Rigidbody and has been tagged as Sliceable
GameObject sliceable = (GameObject)Instantiate (ChoosePrefab (), transform.position, Quaternion.identity);
sliceable.tag = "Sliceable";
Vector3 heading = target.transform.position - transform.position;
Rigidbody rb = sliceable.GetComponent<Rigidbody> ();
if (!rb) rb = sliceable.AddComponent<Rigidbody> ();
rb.velocity = transform.TransformDirection (heading/heading.magnitude * power);
rb.useGravity = true;
rb.drag = 1f;