Wednesday 4 January 2017

Procedural Generation Tutorial Checker Board Texture - Part 3

Procedural Generation Tutorial Checker Board Texture - Part 3


The end result of the full tutorial.



At the end of Part 10 we will have produced this (See image 1.0).
Image 1


Before you start you should know these things:

You should have gone through and completed ‘Part 2’ of this tutorial 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.



https://joseph-easter.blogspot.com/2016/12/procedural-generation-tutorial-checker_81.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.

Links to Parts 1 - 3 of this tutorial series:

Part 1

Part 2

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.


This tutorial will be split up into several parts. In this part we will continue from part two by:

  • Changing the number of squares on the texture independent of texture size
  • Adjust the size of the blocks dynamically independent of texture size
  • Change several pixels in one go using SetPixels32
  • Learn how to position said squares correctly using the width and height of the squares
In ‘Part 2’ we learnt how to set the texture to the material and how to make the texture less blurry round the edges at low resolutions. In this part we will learn how to make the texture more dynamic and flexible independent of texture size.

Step 1: -Difference Between SetPixel and SetPixels and SetPixels32:

In this step we will recode our ‘CreatePattern’ method in order to make it more flexible. At the moment the texture is ok and we have the foundations sorted. However, what if we want a texture that is bigger than 8 by 8? The pattern will repeat because each square is only a Pringles pixel, each square will get smaller and the pattern will appear grey. We want to be able to see the squares at higher resolutions.

We can do this by changing how our methods work a little bit. First off we need to be able to change large groups of pixels quickly in one go. Now we can do this using ‘SetPixel’ but this is slow and inefficient. However there are two alternative functions we can use, ‘SetPixels’ and ‘SetPixels32’; these are the block versions of ‘SetPixel’. These functions are virtually the same apart from ‘SetPixels32’ a bit more efficient and faster. As we want our texture to render as quickly as possible we will use this one.

Step 2: – Implementing SetPixels32 and setting block size


‘SetPixels32’ works like ‘SetPixel’ where we state the ‘X’ and ‘Y’ position, but after this we need to set the width, and height of the block itself. We add these variables after setting the position of the block but before we set the colour. For ease of use we will create and use two int variables ‘blockWidth’ and blockHeight’ and set them to a value of ‘4’.

Code:


public int blockWidth = 4;
public int blockHeight = 4;

void CreatePattern ()
{
    for (int i = 0; i < mainTexWidth; i++)
    {
       for (int j = 0; j < mainTexWidth; j++)
       {
          if ()
          {
             mainTexture.SetPixels(i, j, 
                     blockWidth, blockHeight,
                     Color.black);
          }
          else
          {
             mainTexture.SetPixels(i, j, 
                     blockWidth, blockHeight,
                     Color.white);
          }
       }
    }
    ...
}


If we go back to Unity and let the code compile we will get four errors, but two of them are the same because we have repeated part of the code. Out of the errors the ones highlighted are the ones to worry about. This is because the not highlighted ones is saying what line the problem is on and is telling us how the function works but not the specific error. (See figure 1.0)

Figure 1.0

The hilighted ones are telling us what the error actually is. It is because ‘SetPixels’ uses a colour array and not just a single. This seems a little odd but when you know how it works it makes sense. We will go through this in the next step.

Step 3: – Setting the colours using arrays

When using ‘SetPixels32’ we need to use a colour array to colour the block of pixels. This is because set pixels works by putting each pixel selected into a temporary array (that you don’t see) and it starts from the bottom left to the top right pixel. It sorts through each pixel one by one, and to do this quickly it works with a for loop, assigning one pixel a single colour.


Now you might be thinking, why not set it to just one colour variable. That’s because it is designed to also work with GetPixels32 (I won’t go into more detail in another tutorial). Therefore, if you are grabbing pixels from one image and want to past it to another, it’s useful to hold all of those pixels an another array and sort through. If you wanted to you could create your own version of set pixels but I won’t cover that here because it’s very single use.

We need to do is change our code a little and create two new variables. We need to create two ‘Color32’ arrays, one called ‘colour0Arr’ and ‘colour1Arr’ under the other variables.

Code:


public Color32 colour0Arr;
public Color32 colour1Arr;

void CreatePattern ()
{
    for (int i = 0; i < mainTexWidth; i++)
    {
       for (int j = 0; j < mainTexWidth; j++)
       {
          if ()
          {
             mainTexture.SetPixels(i, j, 
                     blockWidth, blockHeight,
                     colour0Arr);
          }
          else
          {
             mainTexture.SetPixels(i, j, 
                     blockWidth, blockHeight,
                     colour1Arr);
          }
       }
    }
    ...
}

Now when we let our code compile we will notice two things. When we run our code we get an error and both arrays are set to zero meaning they have no elements. (See figure 1.1)

Figure 1.1

This basically means there are not enough elements in the array to colour each pixel because we have not resized it yet, it’s still set to ‘0’ elements. We can fix this using a little trick.

We will create a new function below ‘CreatePattern’ called ‘ConvertColourToArray’. The value we parse the function will be used to set it’s new length for both arrays (we will parse ‘blockWidth’ times ‘blockHeight’ to get the value we need). (See Figure 1.2 for the results in the inspector)

Code:

void ConvertColourToArray (int arrSize)
{
    colourArr0 = new Color32[arrSize];
    colourArr1 = new Color32[arrSize];
}

Figure 1.2

We need to do one more thing, if we run the code we will still get and error. (See figure 1.3)


Figure 1.3


This is happening because the iterators ‘i’ and ‘j’ are counting every pixel in the texture. However when we get near to the top right of the texture pixel count (remember SetPixels32 works from bottom left to top right) we start running out of pixels to cover and it can’t colour pixels that don’t exist.

To fix this we need to count how many squares we want on the texture itself both on the ‘X’ and ‘Y’ axis using the iterators ‘i’ and ‘j’ in the for loops. We will do this by creating two new variables ‘squaresX’ and ‘squaresY’ and replace ‘mainTexWidth’ with ‘squaresX’ and ‘mainTexHeight’ with ‘squaresY’.

Code:


public int squaresX = 4;
public int squaresY = 4;

void CreatePattern ()
{
    for (int i = 0; i < squaresX; i++)
    {
       for (int j = 0; j < squaresY; j++)
       {
          if ()
          {
             mainTexture.SetPixels(i, j, 
                     blockWidth, blockHeight,
                     colour0Arr);
          }
          else
          {
             mainTexture.SetPixels(i, j, 
                     blockWidth, blockHeight,
                     colour1Arr);
          }
       }
    }
    ...
}

When we compile and run the code, this is what we get. (See figure 1.4)

Figure 1.4


The next things to do are colour the arrays. We could do this manually in the ‘Inspector’ but this would be boring, take too long and defeat the point.

For this reason we will do this using a for loop and create to new variables, which we’ll assign to the colour arrays.

Also when we reference the function, we need to parse it the ‘blockWidth’ multiplied by ‘blockHeight’. This with give us the area of pixels or the total number of pixels per block.

Code:

public Color32 colour0;
public Color32 colour1;

void ConvertColourToArray (int arrSize)
{
    colourArr0 = new Color32[arrSize];
    colourArr1 = new Color32[arrSize];

    for (int i = 0; i < arrSize; i++)
    {
       colourArr0[i] = color0;
       colourArr1[i] = color1;
    }
}

Grate we have created a function to automate this process for us. Now if we let the code compile and we set both ‘colour0’ and ‘colour1’ to two different colours and then save and run we will see the results. (See figure 1.5)

Figure 1.5: Far left image has the properties of the far right image. The centre image shows the one square per pixel.

What we have is ok but only if you want one square per pixel, anything other than that and you get the image in figure 1.5.1. We will fix this in the next (and final step). Now we need to do one more things and that’s it (for now).


Step 4:- Fixing the spacing

We have this overlapping effect. This is because it is still positioning each square one pixel apart but keeping the dimensions, therefore overlapping them. This is a straight forward fix.

Even though the for loops are counting the squares in each iteration ‘SetPixels32’ Is being parsed ‘i’ and ‘j’ for the starting X and Y position a bit like it’s counting the pixels of the texture. This is why the squares are drawn one pixel apart on the bottom left on the X and Y coordinates. This is also partly why there is the wired over lapping effect with the upper and right most edges and why the squares only appear to work properly at one pixel high and wide or we can’t have the same amount of pixels as squares with larger pixels.

We need to do two things, first we need to reposition the blocks correctly. When we do this, they need to be one blocks worth of pixels apart from each other on both the X and Y axis. We could replace the I and I iterations with the ‘blockWidth’ and ‘blockHeight’ vars but then the blocks would stack on top of each other. We need to multiply these values by the correct amount each time to space them out and by a varying degree each time.

We do this by modifying the function a little. Because we already have an integrator for both axis we multiply them by the appropriate variable, ‘i’ by the width ‘blockWidth’ and ‘j’ by the height ‘blockHeight’.

Code:

void CreatePattern ()
{
    for (int i = 0; i < squaresX; i++)
    {
       for (int j = 0; j < squaresY; j++)
       {
          if ()
          {
             mainTexture.SetPixels(i * 
                    blockWidth, j * 
                    blockHeight
                    blockWidth, blockHeight,
                    colour0Arr);
          }
          else
          { 
             mainTexture.SetPixels(i * 
                     blockWidth, j * 
                     blockHeight
                     blockWidth, blockHeight,
                     colour1Arr);
          }
       }
    }
    ...
}

Now enter ‘Play’ mode and see the results. (See Figure 1.6)

Figure 1.6

For your project you can adjust the variables for you needs but it’s pretty satisfying to look at. (Quick note: If a square exceeds the pixel count of the texture, it will not be drawn and you will get another ‘SetPixels32’ array error, but the rest will draw).

Great, part 3 and the checkerboard is finished.

We have learnt how to:
  • Use SetPixels to set a large group of pixels at once
  • Learnt how to resize and recolour a colour32 array quickly and easily
  • How to set block positions that don’t over lap each other using width times the integrator


If you would like more tutorial leave a comment below. If you want any extra refinements to this tutorial let me know and I will add more to it.

Download resources and project files.

4 comments:

  1. Thanks for making this tutorial. My confidence for creating texturing using scripting has gone up and has encouraged me to find out more and experiment with what I have learned here. It is good how it is explained in simple terms SetPixels32 works, the benefits of using it as to SetPixels, how to recode how the loops work to adjust the block size regardless of the texture's resolution. I look forward to reading more of your tutorials in future.

    ReplyDelete
  2. I enjoyed going through this tutorial. In this part I liked how it leaves in errors and goes into debugging at the right speed. This is good because it teaches how to avoid bad code (and good code used in the wrong places and contexts) and how to fix it correctly. The only improvement I can think of is when an abstract concept is being explained it would be nice to see a diagram or see it being used directly when possible. I apreasheate this can't be done with everything, but it would be nice when possible. Maybe you could do a separate post on said thing e.g. the modulus operator and leave a link to it in the appropriate place so those who want to learn more about it can read up on it. Apart from those suggestions I enjoyed this a lot. Cannot wait to read your other tutorials.

    ReplyDelete
  3. This tutorial wrapped up well. I've learnt a lot from it. The debugging process was straight forward and clearly explained. Reworking the code was simple. The theory at the start was as interesting as it was informative. The post introduces new functions and concepts as they are needed in order to make these digestible. It explains what the differences are and how something is used in different scenarios. I.E. SetPixel vs SetPixels32. One last thing, each post has a quick summary of what will be covered in the post. I like this because it gives you a good idea of what you will be doing before hand. Where needed each step is titled relevant to what it covers. This gives you the gist at a glance before you continue. I would recommend this!

    ReplyDelete
  4. please reupload projekt^^

    ReplyDelete