Procedural Generation Tutorial Basic Cell Pattern Texture - Part 3
The end result of the full tutorial.
At the end of Part 3 we will have produced this (See image 1.0).
Image 1.0
Before you start you should know these things:
- Create Texture variables
- Manipulate textures using SetPixels
- Create a basic texture pattern
- What noise is and hot to use it with a texture pattern
You should have gone through these tutorials before going further:
- Checker Board Texture
- Brick Pattern Texture
- Brick Pattern Noise Texture
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.
Part 2:
http://joseph-easter.blogspot.com/2017/04/procedural-generation-tutorial-basic.html
Checker Board Pattern Texture http://joseph-easter.blogspot.nl/2016/12/procedural-generation-tutorial-checker.html
Brick Texture Tutorial http://joseph-easter.blogspot.co.uk/2017/01/procedural-generation-tutorial-brick.html
Brick Noise Texture Tutorial http://joseph-easter.blogspot.nl/2017/02/procedural-generation-tutorial-brick_8.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 3 and the completed project file of Part 3.
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 creating a very simple pattern using a cellular texture using code. This tutorial will use and build upon what we have learnt from the previous texture tutorials, by creating two textures, one for the background, and one for the foreground, dividing the foreground into cells and adding an image into those cells then combining those textures. Later we will add variation to it by deciding whether to show a cell based on it noise value in the texture space, then using that noise to position it with in the cell itself using bombing.
We will:
- Explain the theory
- Understand alpha composition
- How to use alpha composition when combining two textures
- Use 'Color32.Lerp' interpolation of achieve this effect
- Normalise the alpha value correctly with 32 bit variables
- Understand antialiasing a little bit to remove jaggys
Step 1: The theory
We will do this by, creating two temporary ‘Color32’ arrays, one for the background texture and one for the top layer texture, and store the texture’s pixel colour information in them. After this we will create a temporary blank colour array called ‘combinedColours’ and set it to the same length and the other two arrays. The next step is to loop through this array using a ‘for’ loop. Inside the for loop we create a temporary ‘Color32’ and called it ‘main’ for the background texture. We will assign this a pixel from the background texture colour array and index it using the iterator in the ‘for’ loop. We will do the same for the top layer texture temporary colour array.
After doing this we will assign the current element from ‘combinedColours’ a new colour. We will use Color32.Lerp to interpolate between the current pixel for the background and top layer arrays. The value we will use for this is a normalized version of the alpha from the top layer because we want to decide how much of the background is visible based on the transparency of the top layer pixels.
Then after doing this we will create a new texture which we will use to place these new pixel colours. It will cleanly show the results of combining both textures using alpha compositing to perform a similar function to a ‘Multiply’ or ‘Overlay’ operation.
Step 2: Creating the Combined Texture function & The Combined Texture
In this step, we will need to do two things, create a new ‘Texture2D’ variable for the combined texture and modify the ‘AddNoiseToTexture’ method. You might be thinking. “Why not directly add the top layer to the background texture?”. You could do this if you wanted to, I prefer to keep everything as separate textures so I can concentrate on one thing at a time, and if something goes wrong with one texture, it’s isolated there.
First thing, create a new Texture2D variable called “combinedTexture’ if you have not already done so. Then in ‘Start’ uncomment the ‘AddNoiseToTexture’ reference. Then uncomment the entire function itself.
Code:
public Texture2D combinedTexture;
...
void Start ()
{
SetMainTextureSize();
CreateBackgroundColour(mainTexture);
ConvertColourToArray(blockWidth *
blockHeight);
CreatePattern();
AddNoiseToTexture();
}
...
void AddNoiseToTexture()
{
...
}
Code:
void AddNoiseToTexture()
{
Color32[] tempMainPixels =
mainTexture.GetPixels32();
Color32[] tempPixels =
tempNoiseTex.GetPixels32();
for (int i = 0; i < tempMainPixels.Length;
i++)
{tempMainPixels[i].r += tempPixels[i].r;
tempMainPixels[i].g += tempPixels[i].g;
tempMainPixels[i].b += tempPixels[i].b;
tempMainPixels[i].a += tempPixels[i].a;
}
mainTexture.SetPixels32(0, 0, mainTexWidth,
mainTexHeight, tempMainPixels);
mainTexture.Apply();
}
Code:
void AddNoiseToTexture()
{
Color32[] tempMainPixels =
mainTexture.GetPixels32();
Color32[] tempTopLayerPixels =
topLayerTexture.GetPixels32();
for (int i = 0; i < tempMainPixels.Length;
i++)
{tempMainPixels[i].r += tempPixels[i].r;
tempMainPixels[i].g += tempPixels[i].g;
tempMainPixels[i].b += tempPixels[i].b;
tempMainPixels[i].a += tempPixels[i].a;
}
mainTexture.SetPixels32(0, 0, mainTexWidth,
mainTexHeight, tempMainPixels);
mainTexture.Apply();
}
Code:
void Start ()
{
...
AddTexturesTogether();
}
...
void AddTexturesTogether()
{
...
}
Step 3: Combining the textures together using Interpolation aka Alpha Composition theory
We can use several different algorithms and formulas to fix this alpha composition problem. Although these mostly work with ‘Color’ and rely on multiplying the background colour and dividing the top layer alpha colour to interpolate the alpha values. This works well with ‘Color’ but ‘Colour32’ works a little differently using 32 bit values. If we were to add two colours together we couldn’t just add to at will, you need to add together the RGBA bit value separately. Using the formula this way would very complex. The effect we want to achieve wouldn’t be up to scratch without sever modification getting the edge colours to be the right colour and they would have a different alpha value. Plus, the code would not be that reusable. It would take too long and cause a headache.
Step 4: Putting theory into practice with Color32 Interpolation
There is another, simpler approach though. We can cycle through every pixel in the ‘combinedTexture’ with a ‘for’ loop. Then in the loop we create a new temporary ‘Color32’ variable and we assign it a new ‘Color32’. We will name this ‘tempCombinedColour’. This new Color32 will interpolate between the background colour elements and the ‘topLayer’ colour elements. For this we will use ‘Color32.Lerp’. With this we do not need to mess about with affecting the RGBA values individually as this is taken care of for us with ‘Color32.Lerp’.
For the ‘t’ used in the ‘Color32.Lerp’ function we will use the alpha value of the ‘topLayerTexture’ elements. In other words, when the alpha of ‘topLayerTexture’ element is low, the background texture will show through appropriately, and when the alpha is high the top layer will be more visible. Anything in between will be translucent to some degree.
Code:
void AddNoiseToTexture()
{
Color32[] tempMainPixels =
mainTexture.GetPixels32();
Color32[] tempTopLayerPixels =
topLayerTexture.GetPixels32();
for (int i = 0; i < tempMainPixels.Length;
i++)
{Color32 tempCombinedColor =
Color32.Lerp(
tempMainPixels[i],
tempTopLayerPixels[i],
tempTopLayerPixels[i].a);
}
mainTexture.SetPixels32(0, 0, mainTexWidth,
mainTexHeight, tempMainPixels);
mainTexture.Apply();
}
Image 1.1
This is quite straight forward. We create a float called ‘normaliser’ in the ‘for’ loop above the ‘Color32’ interpolation. Then we assign it the values of a simple formula. This formula is like working out the percentage left of something. We want to know how much of the current alpha is of the maximum alpha. So if it’s one tenth of the maximum alpha available the values returned will be ‘0.1’. This fits in nicely with our interpolation method. We work this out by getting the alpha value of the current pixel element and dividing it by the maximum alpha value possible which is 255. Then we replace 'tempTopLayer[i].a' with 'normalizer'.
Code:
void AddNoiseToTexture()
{
Color32[] tempMainPixels =
mainTexture.GetPixels32();
Color32[] tempTopLayerPixels =
topLayerTexture.GetPixels32();
for (int i = 0; i < tempMainPixels.Length;
i++)
{float normalizer =
tempTopLayerPixels[i].a / 255;
Color32 tempCombinedColor =
Color32.Lerp(
tempMainPixels[i],
tempTopLayerPixels[i],
normalizer);
}
mainTexture.SetPixels32(0, 0, mainTexWidth,
mainTexHeight, tempMainPixels);
mainTexture.Apply();
}
Code:
void AddNoiseToTexture()
{
Color32[] tempMainPixels =
mainTexture.GetPixels32();
Color32[] tempTopLayerPixels =
topLayerTexture.GetPixels32();
Color32[] combinedTexturePixels =
new Color32[tempMainPixels.Length];
for (int i = 0; i < tempMainPixels.Length;
i++)
{float normalizer =
tempTopLayerPixels[i].a / 255;
Color32 tempCombinedColor =
Color32.Lerp(
tempMainPixels[i],
tempTopLayerPixels[i],
normalizer);
combinedTexturePixels[i] =
tempCombinedColor;
}
mainTexture.SetPixels32(0, 0, mainTexWidth,
mainTexHeight, tempMainPixels);
mainTexture.Apply();
}
While we are at it change all the remaining references from ‘mainTexture’ to ‘combinedTexture’ in the function and assign ‘combinedTexture’ as our ‘mainTexture’ for the material in the function so we can more easily see it. Also in ‘SetPixels32’ change ‘tempMainPixels’ to ‘combinedTexturePixels’.
Code:
void AddNoiseToTexture()
{
Color32[] tempMainPixels =
mainTexture.GetPixels32();
Color32[] tempTopLayerPixels =
topLayerTexture.GetPixels32();
Color32[] combinedTexturePixels =
new Color32[tempMainPixels.Length];
for (int i = 0; i < tempMainPixels.Length;
i++)
{float normalizer =
tempTopLayerPixels[i].a / 255;
Color32 tempCombinedColor =
Color32.Lerp(
tempMainPixels[i],
tempTopLayerPixels[i],
normalizer);
combinedTexturePixels[i] =
tempCombinedColor;
}
combinedTexturePixels.SetPixels32(0, 0,
mainTexWidth,
mainTexHeight,
combinedTexturePixels);
combinedTexturePixels.Apply();
GetComponent<Renderer>().material.
mainTexture = combinedTexturePixels;
}
Code:
...
void SetMainTextureize()
{
mainTexture = new Texture2D(
mainTexWidth,
mainTexHeight);
topLayerTexture = new Texture2D(
mainTexWidth,
mainTexHeight);
combinedTexture = new Texture2D(
mainTexWidth,
mainTexHeight);
...
}
When we run our code, we get this.
Image 1.2
In ‘Step 4’ we will convert everything in this line to float type variables and explicitly convert the byte value to a float value.
Step 5: Refining the jaggys with psudo-antialaising
Code:
void AddNoiseToTexture()
{
...
for (int i = 0; i < tempMainPixels.Length;
i++)
{float normalizer =
(float)tempTopLayerPixels[i].a / 255f;
...
}
...
}
Image 1.3: No jaggys.
Image 1.4
Great, part three is finished. We have laid down more foundations.
We have learnt how to:
- Understand alpha composition
- How to use alpha composition when combining two textures
- Use 'Color32.Lerp' interpolation of achieve this effect
- Normalise the alpha value correctly with 32 bit variables
- Understand antialiasing a little bit to remove jaggys
Go to Part 4, click here.
If you enjoyed this tutorial and would like me to add some extra content to it, like and share this tutorial on here and social media and leave a comment below. If you didn’t like this tutorial please leave a comment below saying why.
If you enjoyed this tutorial and would like me to add some extra content to it, like and share this tutorial on here and social media and leave a comment below. If you didn’t like this tutorial please leave a comment below saying why.
No comments:
Post a Comment