Procedural Generation Tutorial Basic Cell Pattern Texture - Part 2
The end result of the full tutorial.
At the end of Part 2 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 1:
http://joseph-easter.blogspot.com/2017/03/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 2 and the completed project file of Part 2.
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
- Divide a texture into cells
- Create a tiled pattern using the cells
- Make a texture have transparency
- Adjust an images position in a texture's cell
Step 1: The theory
In this part, we will be creating the pattern of the texture, specifically a repeating tiled texture which we can use later to modify to make more interesting and less predictable patterns.
We will do this by, creating another texture variable with the same resolution of the background texture and divide it into cells of equal width and height. The we will store the image we want to use in a ‘Texture2D’ variable, get its pixel information and store it in a ‘Color32’ array. We will sort through the cells of the texture using a for loop to get their ‘X’ and ‘Y’ position. Then we will set the pixels of the cell in the top layer texture to those of the image we just obtained the pixel information we got.
Step 2: Creating the Pattern Texture and Dividing it into Cells
Code:
public Texture2D mainTexture;
public Texture2D combinedTexture;
public Texture2D topLayerTexture;
...
void SetMainTextureSize ()
{
mainTexture = new Texture2D(mainTexWidth,
mainTexHeight);
topLayerTexture = new Texture2D(
mainTexWidth,
mainTexHeight);
int bW = mainTexWidth / squaresX;
int bH = mainTexHeight / squaresY;
SetBlockSize(bW, bH);
}
public Texture2D combinedTexture;
public Texture2D topLayerTexture;
...
void SetMainTextureSize ()
{
mainTexture = new Texture2D(mainTexWidth,
mainTexHeight);
topLayerTexture = new Texture2D(
mainTexWidth,
mainTexHeight);
int bW = mainTexWidth / squaresX;
int bH = mainTexHeight / squaresY;
SetBlockSize(bW, bH);
}
Step 3: Creating the Tilled Pattern
When we create our cellular pattern, it will be similar in structure to the ‘CreatePattern’ function from the Checkerboard and brick texture projects. In fact, because we won’t use as alternating pattern it will be more straight forward (unless you need that for your project of course). We will use this method and tweak it somewhat to save us some work since we have already done this in a previous project.
Uncomment the reference to ‘CreatePattern’ in the ‘Start’ method. Then in the code uncomment the function itself.
void Start ()
{
SetMainTextureSize();
CreateBackgroundColour(mainTexture);
ConvertColourToArray(blockWidth *
blockHeight);
CreatePattern();
//AddTexturesTogether();
}
...
void CreatePattern()
{
...
}
Now we will create the pattern and set it to ‘topLayerTexture’. We do not need to touch the nested ‘for’ loops since they are already doing their job cycling through the cells of the texture variable on the X and Y axis. What you need What we need to do here is copy and paste the line where we set the pixels to the ‘mainTexture’ and place it above and outside the ‘if else’ statement but still in the ‘for’ loop. After this delete the ‘if else’ statement (or comment out) the ‘if else’ statement inside the ‘for’ loop because we are not alternating between cells, like a checker board pattern (unless this is the effect you are after).
Then with the line of code you just copied, change ‘mainTexture’ to ‘topLayerTexture’ and change ‘colourArr0’ to ‘colourArr1’. After this change all the lines in this function where ‘mainTexture’ is referenced and change them to ‘topLayerTexture’ since this the texture we are modifying. We set our ‘topLayerTexture’ to the ‘mainTexture’ of the renderer for debugging purposes and more easily spot any problems.
void CreatePattern()
{
for (int i = 0; i < squaresX; i++)
{
for (int j = 0; j < squaresY; j++)
{
topLayerTexture.SetPixels32(
i * blockWidth,
j * blockHeight,
blockWidth, blockHeight,
colour1);
}
}
topLayerTexture.Apply();
GetComponent<Renderer>().material.
mainTexture = topLayerTexture;
topLayerTexture.wrapMode =
TextureWrapMode.Clamp;
topLayerTexture.filterMode =
FilterMode.Point;
}
When we run our code, this happens. (See image 1.1).
This is because ‘colourArr1’ is set to the same colour as ‘colour1’ in ‘ConvertColourToArray’. ‘Colour1’ has not been set to anything yet and its default is completely black. Now this is not very interesting. We want to replace this with an image of our own.
Step 4: Setting the Tilled Pattern
We already have an image to use, it is in the ‘Art’ folder and called 'compact-disk (1)'. Select the image and make sure ‘Alpha is tranparency’ is set to true. At the top of our code, create a new public Texture2D variable called ‘imageTexture’. Then drag the PNG file into the variable in the inspector.
public Texture2D mainTexture;
public Texture2D combinedTexture;
public Texture2D topLayerTexture;
public Texture2D imageTexture;
Before we can set the pixels to anything we need to get the pixels of the texture, and store the colour information into a ‘Color32’ array. We create a new colour array called ‘imagePixels’ in the ‘CreatePattern’ method and we assign it the ‘imageTexture’ colour information for every pixel using ‘GetPixels32’. ‘GetPixels32’ resizes the array for us. After this in the ‘for’ loop where we set the pixels in ‘topLayerTexture’ change the colour array from ‘colourArr1’ to ‘imagePixels’.
void CreatePattern()
{
Color32[] imagePixels =
imageTexture.GetPixels32();
for (int i = 0; i < squaresX; i++)
{
for (int j = 0; j < squaresY; j++)
{
topLayerTexture.SetPixels32(
i * blockWidth,
j * blockHeight,
blockWidth, blockHeight,
imagePixels);
}
}
topLayerTexture.Apply();
GetComponent<Renderer>().material.
mainTexture = topLayerTexture;
topLayerTexture.wrapMode =
TextureWrapMode.Clamp;
topLayerTexture.filterMode =
FilterMode.Point;
}
This is good but we need to do one more thing. If we run our code we get an error telling us ‘SetPixels32 failed’ because the colour array we are using is not the right size to cover the area we want. In our case, we do not have enough texture space pixels but this can also apply to an area that is too large.
When we create our cellular pattern, it will be similar in structure to the ‘CreatePattern’ function from the Checkerboard and brick texture projects. In fact, because we won’t use as alternating pattern it will be more straight forward (unless you need that for your project of course). We will use this method and tweak it somewhat to save us some work since we have already done this in a previous project.
Uncomment the reference to ‘CreatePattern’ in the ‘Start’ method. Then in the code uncomment the function itself.
Code:
void Start ()
{
SetMainTextureSize();
CreateBackgroundColour(mainTexture);
ConvertColourToArray(blockWidth *
blockHeight);
CreatePattern();
//AddTexturesTogether();
}
...
void CreatePattern()
{
...
}
Now we will create the pattern and set it to ‘topLayerTexture’. We do not need to touch the nested ‘for’ loops since they are already doing their job cycling through the cells of the texture variable on the X and Y axis. What you need What we need to do here is copy and paste the line where we set the pixels to the ‘mainTexture’ and place it above and outside the ‘if else’ statement but still in the ‘for’ loop. After this delete the ‘if else’ statement (or comment out) the ‘if else’ statement inside the ‘for’ loop because we are not alternating between cells, like a checker board pattern (unless this is the effect you are after).
Then with the line of code you just copied, change ‘mainTexture’ to ‘topLayerTexture’ and change ‘colourArr0’ to ‘colourArr1’. After this change all the lines in this function where ‘mainTexture’ is referenced and change them to ‘topLayerTexture’ since this the texture we are modifying. We set our ‘topLayerTexture’ to the ‘mainTexture’ of the renderer for debugging purposes and more easily spot any problems.
Code:
void CreatePattern()
{
for (int i = 0; i < squaresX; i++)
{
for (int j = 0; j < squaresY; j++)
{
topLayerTexture.SetPixels32(
i * blockWidth,
j * blockHeight,
blockWidth, blockHeight,
colour1);
}
}
topLayerTexture.Apply();
GetComponent<Renderer>().material.
mainTexture = topLayerTexture;
topLayerTexture.wrapMode =
TextureWrapMode.Clamp;
topLayerTexture.filterMode =
FilterMode.Point;
}
When we run our code, this happens. (See image 1.1).
Image 1.1
Step 4: Setting the Tilled Pattern
We already have an image to use, it is in the ‘Art’ folder and called 'compact-disk (1)'. Select the image and make sure ‘Alpha is tranparency’ is set to true. At the top of our code, create a new public Texture2D variable called ‘imageTexture’. Then drag the PNG file into the variable in the inspector.
Code:
public Texture2D combinedTexture;
public Texture2D topLayerTexture;
public Texture2D imageTexture;
Before we can set the pixels to anything we need to get the pixels of the texture, and store the colour information into a ‘Color32’ array. We create a new colour array called ‘imagePixels’ in the ‘CreatePattern’ method and we assign it the ‘imageTexture’ colour information for every pixel using ‘GetPixels32’. ‘GetPixels32’ resizes the array for us. After this in the ‘for’ loop where we set the pixels in ‘topLayerTexture’ change the colour array from ‘colourArr1’ to ‘imagePixels’.
Code:
void CreatePattern()
{
Color32[] imagePixels =
imageTexture.GetPixels32();
for (int i = 0; i < squaresX; i++)
{
for (int j = 0; j < squaresY; j++)
{
topLayerTexture.SetPixels32(
i * blockWidth,
j * blockHeight,
blockWidth, blockHeight,
imagePixels);
}
}
topLayerTexture.Apply();
GetComponent<Renderer>().material.
mainTexture = topLayerTexture;
topLayerTexture.wrapMode =
TextureWrapMode.Clamp;
topLayerTexture.filterMode =
FilterMode.Point;
}
This is easy, we replace ‘blockWidth’ and ‘blockHeight’ where we tell it to set the pixel area with ‘imageTexture.width’ and ‘imageTexture.height’. We have just accessed the textures width and height properties here, information embedded in the date type, so we don’t have to change our code every time we use an image with different dimensions.
Code:
void CreatePattern()
{
Color32[] imagePixels =
imageTexture.GetPixels32();
for (int i = 0; i < squaresX; i++)
{
for (int j = 0; j < squaresY; j++)
{
topLayerTexture.SetPixels32(
i * blockWidth,
j * blockHeight,
imageTexture.width,
imageTexture.height,
imagePixels);
}
}
...
}
Image 1.2: Resolution 256, squaresX: 4, squaresY: 4.
Step 5: Fixing the Transparency
Now, even though the image file has the alpha channels set as transparency, the same is not the same for the ‘Texture2D’ we created. By default, the colour is set to white or gray, with an alpha of ‘255’. When you add two colour values together, their RGBA values (specifically Alpha) are added together. With ‘SetPixels32’ only the Alpha values are added together. Therefore, if you have an image with an alpha of 128, and you write an image to it with an alpha value of 127, the total alpha value will equal 255.
Code:
...
public Color32 transparentColour;
...
public Color32[] transparentColourArr;
...
void ConvertColourToArray(int arrSize)
{
colour0Arr = new Color32[arrSize];
transparentColour Arr =
new Color32[arrSize];
for (int i = 0; i < arrSize; i++)
{
colour0Arr[i] = colour0;
transparentColourArr[i] =
transparentColour;
}
}
Code:
void Start ()
{
SetMainTextureSize();
CreateBackgroundColour(mainTexture);
CreateBackgroundColour(topLayerTexture);
ConvertColourToArray(blockWidth *
blockHeight);
CreatePattern();
//AddTexturesTogether();
}
...
void CreateForegroundColour(Texture2D tex)
{
ConvertColourToArray(tex.width *
tex.height);
tex.SetPixels32(0, 0, tex.width,
tex.height,
transparentColourArr);
tex.Apply();
GetComponent<Renderer>().material.
mainTexture = tex;
}
Image 1.3: Resolution 128. The result is the same because
we need to do more to it in Part 3. It sets up the
ground work for it though.
Image 1.3: Resolution 256.
Hexadecimal value for colour used: 41CEFD00
Step 6: Fixing the position within the Cell
Code:
void CreatePattern()
{
Color32[] imagePixels =
imageTexture.GetPixels32();
int middleOfCellWidth = blockWidth / 4;
int middleOfCellHeight = blockHieght / 4;
for (int i = 0; i < squaresX; i++)
{
for (int j = 0; j < squaresY; j++)
{
topLayerTexture.SetPixels32(
i * blockWidth,
j * blockHeight,
imageTexture.width,
imageTexture.height,
imagePixels);
}
}
...
}
Code:
void CreatePattern()
{
Color32[] imagePixels =
imageTexture.GetPixels32();
int middleOfCellWidth = blockWidth / 4;
int middleOfCellHeight = blockHieght / 4;
for (int i = 0; i < squaresX; i++)
{
for (int j = 0; j < squaresY; j++)
{
topLayerTexture.SetPixels32(
i * blockWidth + middleOfCellWidth,
j * blockHeight + middleOfCellHeight,
imageTexture.width,
imageTexture.height,
imagePixels);
}
}
...
}
Image 1.5
We have learnt how to:
- Divide a texture into cells
- Create a tiled pattern using the cells
- Make a texture have transparency
- Adjust an images position in a texture's cell
Go to Part 3, 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