Showing posts with label Unity3D. Show all posts
Showing posts with label Unity3D. Show all posts

Tuesday, April 05, 2016

Performant Stylized Shaders in Unity - Shader Basics

This tutorial is for people who wish to know the basics of fast shaders, and are already somewhat familiar with programming and working with 3D content!

A shader is a piece of code that runs on your graphics card, and determines how triangles are drawn on the screen! Typically, a shader is composed of a vertex shader, for placing vertices in their final location the screen, and a pixel shader (generally referred to as a ‘fragment shader’) to choose the exact color on the screen! While shaders can often look very confusing at a first glance, they’re ultimately pretty straightforward, and can be a really fun blend of math and art!

Creating your own shaders, while not particularly necessary with all the premade ones running about, can lend your game a really unique and characteristic look, or enable you to achieve beautiful and mesmerizing effects! In this series, I’ll be focusing on shaders for mobile or high-performance environments, with a generally stylized approach. Complicated lighting equations are great and can look really beautiful, but are often out of reach when targeting mobile devices, or for those without a heavy background in math!

In Unity, there are two types of shaders, vertex/fragment shaders, and surface shaders! Surface shaders are not normal shaders, they’re actually “magic” code! They take what you write, and wrap it in a pile of extra shader code that you never really get to see. It generally works spectacularly well, and ties straight into all of Unity’s internal lighting and rendering systems, but this can leave a lot to be desired in terms of control and optimization! For these tutorials, we’ll stick to the basic vertex/fragment shaders, which are generally /way/ better for fast, un-lit, or stylized graphics!

You can create a simple vertex/fragment shader in Unity by right clicking in your Project window->Create->Shader->Unlit Shader! It gives you most of the basic stuff that you need to survive, but here I’m showing an even more stripped down shader, without any of the Unity metadata attached! This won’t compile by itself, it does need the metadata in order to work, but if you put this in between the CGPROGRAM and ENDCG tags, you should be good to go! (Check here for how the full .shader file should look!)

#pragma vertex   vert
#pragma fragment frag

#include "UnityCG.cginc"

struct appdata {
 float4 vertex :POSITION;
};
struct v2f {
 float4 vertex :SV_POSITION;
};

v2f vert (appdata data) {
 v2f result;
 result.vertex = mul(UNITY_MATRIX_MVP, data.vertex);
 return result;
}

fixed4 frag (v2f data) :SV_Target {
 return fixed4(0.5, 0.8, 0.5, 1);
}


Breaking it down:
#pragma vertex   vert
#pragma fragment frag

The first thing happening here, we tell CG that we have a vertex shader named vert, and a pixel/fragment shader named frag! It then knows to pass data to these functions automatically when rendering the 3D model. The vertex shader will be called once for each vertex on the mesh, and the data for that vertex is passed in. The fragment shader will be called once for each pixel on the triangles that get drawn, and the data passed in will be values interpolated from the 3 vertex shader results that make its triangle!

struct appdata {
 float4 vertex :POSITION;
};
struct v2f {
 float4 vertex :SV_POSITION;
};

One of the great thing about shaders is that they’re super customizable, so we can specify exactly what data we want to work with! All function arguments and return values have ‘semantics’ tags attached to them, indicating what type of data they are. The :POSITION semantic indicates the vertex position from the mesh, just as :TEXCOORD0, :TEXCOORD1, :COLOR0 would indicate indicate texture coordinate data for channels 0 and 1, and color data for the 0 channel. You can find a full list of semantics options here! These structures are then used as arguments and return values for the vertex and fragment shaders!

v2f vert (appdata data) {
 v2f result;
 result.vertex = mul(UNITY_MATRIX_MVP, data.vertex);
 return result;
}

The vertex shader itself! We get an appdata structure with the mesh vertex position in it, and we need to transform it into 2D screen coordinates that can then be easily rendered! Fortunately, this is really easy in Unity. UNITY_MATRIX_MVP is a value defined by Unity that describes the Model->View->Projection transform matrix for the active model and camera. Multiplying a vector by this matrix will transform it from its location in the source model (Model Space), into its position in the 3D world (World Space). From there, it’s then transformed to be in a position relative to the camera view (View Space), as though the camera was the center of the world. Then it’s projected onto the screen itself as a 2D point (Screen Space)!



The MVP matrix is a combination of those three individual transforms, and it can often be useful to do them separately or manually for certain effects! For example, if you want a wind effect that happens based on its location in the world, you would need the world position! You could do something like this instead:

v2f vert (appdata data) {
 v2f result;
 
 float4 worldPos = mul(_Object2World, data.vertex);
 worldPos.x += sin((worldPos.x + worldPos.z) + _Time.z) * 0.2;
 
 result.vertex = mul(UNITY_MATRIX_VP, worldPos);
 return result;
}


Which transforms the vertex into world space with the _Object2World matrix, does a waving sin operation on it, and then transforms it the rest of the way through the View and Projection matrices using UNITY_MATRIX_VP! For a list of more built-in shader variables that you can use (like _Time!) check out this docs page!

fixed4 frag (v2f data) :SV_Target {
 return fixed4(0.5, 0.8, 0.5, 1);
}

And last, there’s the fragment shader! This takes the data from the vertex shader, and decides on a Red, Green, Blue, Alpha color value. Alpha being transparency, but we’ll need to dig through Unity’s metadata for that! In this case, we’re just returning a simple color value, so it’s a solid value throughout! Here, you may notice the return value is a fixed4 rather than a structure, but note that it still uses the :SV_Target semantic for the return type at the end of the function definition! If you’re wondering the difference between float4 and fixed4, it’s basically 128 bits of accuracy vs 32 bits, more on that later, but we only really need to return 32 bits of color data back to the graphics card! 

Again though, we can do cool math with this too! In this case, just attach the x and y axes to the R and G channels of the color!

fixed4 frag (v2f data) :SV_Target {
 return fixed4(
  (data.vertex.x/_ScreenParams.x)*1.5, 
  (data.vertex.y/_ScreenParams.y)*1.5, 
  0.5, 
  1);
}

So that’s the basics of the syntax for creating shaders in Unity! You can do a fair bit with this already, but the real fun bits come in when you tie into Unity’s inspector, using textures for color and data, and a bit of graphics specific math! We’ll get into that next time, but for now, here are the final shader files with their Unity metadata wrapper which were used to create the screenshots you see here!

Friday, May 16, 2014

ScriptableObjects in Unity and how/why you should use them!

I relatively recently discovered Unity's ScriptableObjects, and fell in love with them immediately. For some reason, you don't see them around a whole lot, yet they solve a number of important problems, like... How do you store data in Unity? I assure you, my early attempts were quite horrific.

The problem

Lets imagine you have a shooting game. Guns are an integral part of a game like this, and so you need lots of guns in your game! In theory, you could just prefab all your guns, and work with them that way, and in a simple world, this generally works pretty well! But in this game, you need to interact with these guns in a lot of different ways, selecting them, customizing them, stat tweaking them, admiring their detail, shooting them, etc. Having all that in a prefab becomes ridiculous quite quickly!

I did mention lots of guns, right?

Soo, the next intuitive leap is to do some sort of data file, XML, JSON, plain text, or whatever. The problem with this is zero tie-in with the Unity editor itself! At least, not without a substantial amount of extra work. That might be acceptable after enough work, but ScriptableObjects provide a significantly better alternative!

The solution

ScriptableObject is a class, very much like the MonoBehaviour, without a lot of the component specific items. It gets serialized, saved, and loaded automatically through Unity, as well as standard inspector window support! You can easily spot Unity using ScriptableObjects in the Edit->Project Settings menu, and other similar locations. Unfortunately, Unity didn't make it quite as simple to use, you really won't find anything mentioned about ScriptableObjects in the UI.

So how do we use 'em? Lucky for us, it's relatively trivial~
Step 1. Inherit from ScriptableObject
Step 2. Treat it like a MonoBehaviour without events.
Step 3. Tie it into the editor.

Tying it into the editor can be fun, but here is where you get some handy-dandy code =D Unity treats ScriptableObjects the same way it does everything else, so you have to create an asset file, and add it to the file system!

The code

This is a little function I use that will create an asset for any ScriptableObject that I throw at it. I usually keep it around in a tiny little utility file I call SOUtil.cs (click here to download it!) It looks like this:
#if UNITY_EDITOR
    public static void CreateAsset(System.Type aType, string aBaseName) {
        ScriptableObject style = ScriptableObject.CreateInstance(aType);

        string path = UnityEditor.AssetDatabase.GetAssetPath(UnityEditor.Selection.activeInstanceID);
        if (System.IO.Path.GetExtension(path) != "") path = System.IO.Path.GetDirectoryName(path);
        if (path                              == "") path = "Assets";

        string name = path + "/" + aBaseName + ".asset";
        int    id   = 0;
        while (UnityEditor.AssetDatabase.LoadAssetAtPath(name, aType) != null) {
            id  += 1;
            name = path + "/" + aBaseName + id + ".asset";
        }

        UnityEditor.AssetDatabase.CreateAsset(style, name);
        UnityEditor.AssetDatabase.SaveAssets();

        UnityEditor.EditorUtility.FocusProjectWindow();
        UnityEditor.Selection    .activeObject = style;
    }
#endif

As you can see, this is a pretty darned generic function. It creates an object, finds a path for it, gives it a default name, saves it, and then sets it as the user's focus! One thing to keep in mind when working with ScriptableObject, you shouldn't create them using the 'new' keyword. Always use ScriptableObject.CreateInstance.

Ok, cool! That's the generic part, now, what about actually tying in specific objects? Well, here's a super simple object to give you a good idea about that.
public class SOString : ScriptableObject {
    public string stringValue = "";

#if UNITY_EDITOR
    const string editorMenuName = "String";
    [UnityEditor.MenuItem("GameObject/Create Scriptable Object/Create " + editorMenuName, false, 0  ), 
     UnityEditor.MenuItem("Assets/Create/Create Scriptable Object/"     + editorMenuName, false, 101)]
    public static void CreateAsset() {
        SOUtil.CreateAsset(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType, editorMenuName);
    }
#endif
}

Not that hard. A couple of menu tie-ins, for the GameObject menu, and the project window context menu, and then a call to our CreateAsset method, automatically picking type through reflection, and providing a default asset name! Now you can drag and drop these little suckers all around your inspectors, just like any other GameObject or MonoBehaviour!

Editor code is darned cool.
Enjoy =D