Wednesday 22 March 2017

Procedural Generation Tutorial Brick Noise Texture - Part 6 Extra Configurable Noise Type

Procedural Generation Tutorial Brick Noise Texture - Part 6 Extra


Before you start you should know these things:
  • Create Texture variables
  • Manipulate textures using SetPixels

You should have gone through and completed all the ‘Checker Board Texture’ and 'Brick Pattern Texture' tutorials before going any further. If you find yourself not understanding some of the terminology or code of the tutorial I would also recommend going through the previous tutorial to get up to speed. I will leave a link to it below to go through at your leisure.

http://joseph-easter.blogspot.com/2017/03/procedural-generation-tutorial-brick_15.html

If you follow this tutorial and find I am moving too fast or if you don’t know the things in the list above, I would recommend getting up to speed and then come back to this tutorial when you are ready.

With this tutorial if you want to convert this into JavaScript by all means do so but it may be easier for you to follow this in C# and stick with the language of the tutorial to make it easier.

PROJECT RESOURCES: At the bottom of the post there is a download link to a zip file. This files includes all files needed to follow Part 6 Extra and the completed project file of Part 6 Extra.

In this tutorial series, we will be adding to the brick pattern texture using code by adding noise to the texture image.


In this tutorial series, we will be adding to the brick pattern texture using code by adding noise to the texture image.

We will:
  • Explain the theory
  • Create a delegate
  • Create a delegate array
  • Store methods in said array
  • Make the UI more user friendly
  • Use delegate to store a function to call later
  • The extreme basics of delegates

Step 1: The theory


In today’s tutorial, we will cover how change our code to make it more configurable so we can easily change what type of Noise (1D, 2D or 3D) we use when creating the noise pattern. At the moment, in our code when we need to change the noise function from one 1D to 2D, we change it directly in our script and we have to wait for the code to compile. It would be a lot easier if we could just have one variable that can hold a functions for us and we can easily swap out one function for another as we need it based on another variable.

This is very quick to implement, and will save us time in the long run and will make it easier for the designers in your team (and the developers).

As a quick out line, we will create a type called a delegate in the ‘NoiseLibrary’ script called ‘NoiseLibraryDelegate’ and have it return a type ‘float’. We will also give it a few parameters to pass it. These will be the same parameters we pass ‘RandomValue1D’. This delegate will hold a reference to any one of our ‘RandomValue’ function we assign it.

Then we will create a public array of type ‘NoiseLibrary’ called ‘randomValueFunctions’ which will hold references to every ‘RandomValue’ function we have. We will order them by their dimensions (1D, 2D and 3D) for when configure them in the inspector later.

After this we will create a public integer variable called ‘noiseDimentions’ which will range between 1 to 3. This will have a range slider, because we only want it to be between values 1 to 3 to correspond to the dimensions of the noise (don’t worry about the array starting at ‘0’ we will sort this out when we cross that bridge).

Then in the ‘Noise’ script in ‘CreateNoise’ function we will create a tempory variable of type ‘NoiseLibraryDelegate’ called 'noiseDimentionType’ and assign it an element from the ‘randomValueFunctions’ array and chose an element using the ‘noiseDimentions’ variable. This will chose the reference to the ‘randomValueNoise’ function we need. Then when we set the pixel to the random colour intensity we replace ‘’ with ‘noiseDimentionType’.


Step 2: Creating the Delegate


First we need to create a delegate called ‘NoiseLibraryDelegate’ which will return a type ‘float’ and we will parse it a Vector3 called ‘point’ and a float called ‘patternAlternation’ speed. This delegate is like a variable but it holds functions instead. With a delegate though you can only assign it a function that has the same parameters that the delegate has. When a delegate is called, it carries out that function, it can hold multiple functions some times and the delegate can change the assigned function at runtime.

After this we will need to create a public static array of type ‘NoiseLibrary’ called ‘randomValueFunctions’. This will hold a reference to every ‘RandomValue’ function in each element. Essential it’s an array of functions, or function references. We will order them as follows ‘RandomValue1D’, ‘RandomValue2D’and ‘RandomValue3D’.


Code:


using UnityEngine;

public static float NoiseDelegate (Vector3 
point, float patternAlternationSpeed);

public static class NoiseLibrary()
{
   public static NoiseDelegate[] randomValueFunctions =
   {
      RandomValue1D,
      RandomValue2D,
      RandomValue3D
   };
   
   ...
   
   public static float RandomValue1D (Vector3 
point, float patternAlternationSpeed)
   {
      ...
   }
   
   public static float RandomValue2D (Vector3 
point, float patternAlternationSpeed)
   {
      ...
   }

   public static float RandomValue3D (Vector3 
point, float patternAlternationSpeed)
   {
      ...
   }
}

Step 3: Adding the configurations


We have the main bits set up. We need to do a few more things and we are done. We need a way to choose what noise method we will use. This needs to be easy to change in the inspector for both the designers and the developers. We will use this integer as the indexer for the ‘randomValueFunction’ array to get the correct ‘randomValueNoise’ function reference. We will put this in the ‘Noise’ class script Now you might be thinking ‘arrays start at zero, why don’t we set the range to zero to 2?’. We do this because we want it to be easier for the designer, and they won’t necessarily understand code or how an array works, and it’s more logical to think ‘1’ refers to 1D noise or the first function in this array. In other words we are making it more user friendly, but we will use some simple maths behind the scenes to make this work properly.


Code:


using UnityEngine;
using System.Collections;


public class Noise : MonoBehaviour
{
   ...
   [Range(1,3)]
   public int noiseDimentions = 3;
   ...
}






Now in the ‘CreateNoisePattern’ function we need to assign a function from the ‘randomValueFunction’ delegate array. We do this by declaring an instance of type ‘NoiseDelegate’ called ‘noiseDimention’ and assign it an element from the ‘randomValueFunction’ delegate array from the ‘NoiseLibrary’ class. We will use ‘noiseDimentions’ as an indexer be we will take away one from this to index the array at the correct element.

Code:

void CreateNoisePattern()
{
   NoiseDelegate noiseDimention = 
   NoiseLibrary.randomValueFunction
   [noiseDimentions - 1];
   float normalizer = 1f / resolutionW;
   WorldCoordinates();
   for (int w = 0; w < resolutionW; w++)
   {
      LeftAndRightSides(pointLL, pointLR,
                        pointUL, pointUR, 
                        w, normalizer);
      for (int h = 0; h < resolutionH; h++)
      {
         CalculatePoint(leftSide, rightSide,
                        h, normalizer);
         noiseTexture.SetPixel(w, h, 
              Color.grey * 
              noiseDimention(point
              patternAlternationSpeed));
      }
   }
   noiseTexture.Apply();
}

Now if we run our code this is what we get.

Resolution 236, patternAlternationSpeed 64.


If we look closely, our game object’s noise texture looks pretty similar when it’s set to 2D or 3D at the default position and rotation. The only way to tell it’s different is by looking at the hash value because it’s calculated (or hashed) an additional time when 3D is selected.



Resolution 256, patternAlternationSpeed 64.

However, when the object is rotated, this is no longer the case and the extra noise calculations can be seen. There is another layer of complexity to the texture’s noise pattern.



Resolution 256, patternAlternationSpeed 64, 
rotation X: 30, Y: -28, Z: 19 (for all objects in the image).



Great stuff, we can more quickly and easily change the noise value type in the inspector, generally making it more user friendly.

We have learnt how to:
  • Create a Delegate
  • What a delegate is capable of
  • A delegate can store methods
  • Those methods can be swapped in and out at runtime
  • Delegates can store multiple methods in the form of an array
  • Call a function from a delegate instance to return a value
  • Use a range slider on a public integer
  • Use said variable to make interface more user friendly
  • To set the method in a delegate by creating an instance of said delegate


This tutorial as an added on extra for Part 6. It is not essential but makes things a little more polished. I hope you enjoyed this tutorial as much as I had fun making it. If you enjoyed this and want more follow my blog, 
share it on social media and leave a comment below saying what you liked. If you didn’t like it or think it’s needs improving leave a comment below saying what and why. I have one new tutorial part out every Wednesday at 1PM (GMT) so stay tuned.

Keep an eye on my blog as I have more coming up soon, I might soon release an extra bit for this tutorial, and have a follow up for this as well.

No comments:

Post a Comment