Friday 13 September 2013

Putting all together to generate a procedural tile pattern

Taking all that we have learned from mod( ), odd( ), even( ), whichTile( ) and so on, we can try building a procedural pattern such as this:


















The codes for generating this tile pattern is:

#include "D:\SweeKim\rsl\src\include\zin\zinMacro.h"

surface zinTest()
{
float pulseU, pulseV;
color ColA = (0,0,1), ColB = (0,1,0);
float pulseFreq = 5;

float uu = zinRepeat(u,pulseFreq);
float vv = zinRepeat(v,pulseFreq);

float col = zinWhichTile(u,pulseFreq);
float row = zinWhichTile(v,pulseFreq);

pulseU = (step(0.4,uu) - step(0.6,uu));
pulseV = (step(0.4,vv) - step(0.6,vv));

if(zinOdd(row))
{
if(zinEven(col))
{
ColA = (0.6,0.2,0.7); //purple
ColB = (0.3,0.5,0.7); //light blue
}
}
else
{
if(zinOdd(col))
{
ColA = (0.6,0.2,0.7); //purple
ColB = (0.3,0.5,0.7); //light blue
}
}

Oi = Os;
Ci = mix(ColA,ColB,pulseU * pulseV);
}

First of all, I have injected a #include line to read in the zinMacro.h header file. This header has the macros we need in the following codes, such as zinRepeat, zinWhichTile, zinOdd and zinEven.

float pulseU, pulseV;
- Created two float variables. The intention is to store the horizontal and vertical stripe pattern.

















_________________________________________________________________________________

color ColA = (0,0,1), ColB = (0,1,0);
- Created two color variables. Initialize them to blue and green. These two colors will be served as base colors.


_________________________________________________________________________________
float pulseFreq = 5;
- Created a float variable that decides how many times the pattern repeated. In this case, 5.


_________________________________________________________________________________
float uu = zinRepeat(u,pulseFreq);
float vv = zinRepeat(v,pulseFreq);
- Created another two float variables. The two variables will be used to store the result of repeating the pattern in u and v directions.
- zinRepeat function is defined as following in the zinMacro header file:
#define zinRepeat(x,freq)    (mod((x) * (freq), 1.0))

please refer to the previous post to understand how zinRepeat works.


















_________________________________________________________________________________

float col = zinWhichTile(u,pulseFreq);
float row = zinWhichTile(v,pulseFreq);
- The zinWhichTile function simply return the tile number. If the pulseFreq is 5, the returning value is 0,1,2,3,4. Tow float variables are created to store the value.
- The definition of zinWhichTile is:
 #define zinWhichTile(x,freq) (floor((x) * (freq)))

For more details explanation of how it works please refer to the previous post.

















_________________________________________________________________________________

pulseU = (step(0.4,uu) - step(0.6,uu));
pulseV = (step(0.4,vv) - step(0.6,vv));
- The pulseU and pulseV variables is making a stripe pattern starting from 0.4 to 0.6 from the range 0 to 1. With uu and vv previous set to repeat 5 times, the final result will look like this:
- Please note that even though the images presented here are in color, they are in fact in float values, meaning grayscale images.

















_________________________________________________________________________________

if(zinOdd(row))
{
     if(zinEven(col))
     {
          ColA = (0.6,0.2,0.7); //purple
          ColB = (0.3,0.5,0.7); //light blue
     }
}
else
{
     if(zinOdd(col))
     {
          ColA = (0.6,0.2,0.7); //purple
          ColB = (0.3,0.5,0.7); //light blue
     }
}

- This is the part where we start coloring the tile in different colors based on conditions. We must remember there is already a base color created at the very begining:
color ColA = (0,0,1), ColB = (0,1,0);

Then, check on each row to see if it is in odd number row, using zinOdd function. If so, go onto next condition, if it is in even column, using zinEven function. If both conditions are met, color it accordingly.

Else, if the very first condition returns false, which the result is in even row, go on to next condition to check if it is in odd number column. If so, color it accordingly.

- What we are trying to do here is, if the checking conditions are met, then we use a different color, else just use the default base color.


_________________________________________________________________________________

Ci = mix(ColA,ColB,pulseU * pulseV);
- Final part would be putting all these information together using mix function.





Wednesday 11 September 2013

using mod function to find odd and even number

It is possible to simple use mod function to find odd or even number.

odd(x)     (mod(x,2) = = 1)
even(x)    (mod(x,2) = = 0)


How does modulo works? It is simply take the first number divide by the second number and get the remaining value.

example:
mod(23,4) = 3;

4 x 5 = 20

and the remaining is 3. Therefore the answer is 3.

So, we can do use 2 as the number to divide to, and the remaining will be either 1 or 0. The value of 1, would of course be odd number, and 0 is even.

Please note that this will only work is x is a whole number.


whichtile and floor

To understand the use of whichtile function:

float freq = 5;
column = whichtile(u, freq);
row = whichtile(v,freq);

The definition of whichtile is:

floor(x * freq);
floor function will return the integer value.

example:
0.1 * 5 = 0.5;
floor(0.5) = 0;

0.3 * 5 = 1.5;
floor(1.5) = 1;

floor(2.1) = 2;

floor(6.9) = 6;

and so on....

So the concept is, u and v usually goes from 0 to 1. In whichtile function will take 0 and multiply it to 5 (freq value) then floor function it. Thus:
0.0 * 5 = 0.0;
floor(0.0) = 0;

0.1 * 5 = 0.5;
floor(0.5) = 0;

0.2 * 5 = 1;
floor(1) = 1;

0.3 * 5 = 1.5;
floor(1.5) = 1;

so the result is very much like a staircase:























Tuesday 10 September 2013

mod

Use mod( ) to achieve periodic pattern:

surface zinTest()
{
float pulseFreq = 5;
float pulseUU = (step(0.3,mod(u*pulseFreq,1)) - step(0.6,mod(u*pulseFreq,1)));

Oi = Os;
Ci = mix(color(1,0,0),color(0,1,0),pulseUU);
}























Monday 9 September 2013

smoothstep

Demonstration of smoothstep( ) :

surface zinTest()
{
float sStep = smoothstep(0.3,0.85,u);
Oi = Os;
Ci = mix(color(1,0,0),color(0,1,0),sStep);
}



abs

Example of using abs( ):


step( )

Using step function in RSL as some form of condition operator such as using if .... else...

surface zinTest()
{
float pulseU = (step(0.3,u) - step(0.5,u));
float pulseV = (step(0.3,v) - step(0.5,v));
Oi = Os;
Ci = mix(color(1,0,0),color(0,1,0),(pulseU * pulseV));
}

------------------------------------------------------------------
Ci = mix(color(1,0,0),color(0,1,0),(pulseU + pulseV));




















--------------------------------------------------------------------

Ci = mix(color(1,0,0),color(0,1,0),(pulseU - pulseV));





















--------------------------------------------------------------------
Ci = mix(color(1,0,0),color(0,1,0),(pulseU * pulseV));





















------------------------------------------------------------------
Ci = mix(color(1,0,0),color(0,1,0),(pulseU / pulseV));


?: ternary opertor

?: in programming language is a ternary operator. In a nutshell it means:

condition ? value_if_true : value_if_false

use in the min and max function in C:

float min(float a, float b)
{
       return (a<b?a:b);
}

float max(float a, float b)
{
      return (a<b?b:a);
}

Sunday 8 September 2013

faceforward

Definition from RSL documentation:

vector faceforward(vector N, I [,Nref = Ng])
{
          return sign(-I.Nref) * N;
}

This function is taking I, the incident ray from camera to the shading point, inverse it so that the ray direction is from the shading point back to camera. Then perform a dot product with Ng (surface geometric normal). Any shading point that is facing away from the camera view (the green lines on the image) will have the dot product to return a negative value. The sign function simply return -1 if the dot product calculation returns a negative value. If it is a positive value, the sign function will return positive 1.  Multiply again with the input N, (surface shading normal), the faceforward function eventually return a normal vector that is in negative value if it is facing away from camera.




















Sunday 1 September 2013

Texture Spaces

Current space
- is the one in which shading calculations are normally done. In most renderers,currentspace will turn out to be either cameraspace or worldspace, but you shouldn’t depend on this.

World Space
- is the coordinate system in which the overall layout of your scene is defined. It is the starting point for all other spaces.

Object Space
- is the one in which the surface being shaded was defined. For instance, if the shader is shading a sphere, theobjectspace of the sphere is the coordinate system that was in effect when theRiSpherecall was made to create the sphere. Note that an object made up of several surfaces all using the same shader might have different object spaces for each of the surfaces if there are geometric transformations between the surfaces.

Shader Space
-  is the coordinate system that existed when the shader was invoked (e.g., by anRiSurfacecall). This is a very useful space because it can be attached to a user-defined collection of surfaces at an appropriate point in the hierarchy of the geometric model so that all of the related surfaces share the sameshaderspace.

Cross Product

When performing a cross product operation, the result will be a vector that is perpendicular to both supplied vectors with a length equal to the area of the parallelogram they span.

The most common use for this operation in the computer graphics is to find the rendering normal of surfaces and to find the normal to any given plain in space

Dot Product


Normalize

Formula for normalizing a vector:


Length of a vector










Length of a vector can also be written as square root of dot product v (v.v)

Trigonometry

Some Trigonometry basics:


Math and Geometry Symbols

Some more definitions:


log( ) or natural logarithm

Defination of log( ):













Sometimes you will see a logarithm written without a base, like this:

log(100)

this usually means that the base is really 10.

Natural Lgarithms: Base "e"
Another base that is often used is e (Euler's Number) which is approximately 2.71828.

This is called a "natural logarithm". Mathematicians use this one a lot.

On a calculator it is the "ln" button.

loge(7.389) ≈ 2

Because 2.718282 ≈ 7.389

coefficient exponent and power

Just a little defination:

Saturday 31 August 2013

Remap

This operation can also be referred to as a fit operation because it takes the low and high value of x (referred as a1and b1) and fits them in a linear fashion into a new low and high value (referred to as a2 and b2). If you use the call remap(x,0,1,1,0), the value of x will be inverted. Setting the function to remap(x,0,1,0.25,0.75) will remap the output to 0.25–0.75

float remap(float x,float a1,float b1,float a2,float b2) {
                   return (x*(b2-a2) - a1*b2 + b1*a2) / (b1-a1);


s with a remap() of (0,1,1,0) inverts the image



















remap() of (0,1,0.25,0.75) limits the image to 0.25 - 0.75.

Expand

The expand() function does the exact opposite of compress(): It remaps the values by pushing the values to a higher position in a value graph.

float expand(float x, float lo, float hi) {
                     float retval = 0;
                     if (lo == hi)
                         retval = x < lo ? 0 : 1;
                    else
                         retval = (x-lo) / (hi-lo);
                    

                    return retval;
}



s with no compression



















Applying an expand() function with the values expand(s,0.5,1) will leave 0 and 1 untouched, remapping 0.5 to 0. This means that the value of 0 is shifted up to the position of 0.5, therefore remapping 0.5 to 0.

s with (0.5,1) expanded. what used to be 0.5 is now 0



















Using the values expand(s,0,2) will leave 0 untouched while remapping 1 to 0.5, because the value of 1 will be shifted up to the position of 2.

s with (0,2) expanded. what used to be 1 is now 0.5






Compress

The compress() functions allow you to manipulate the dynamic range of the 0 to 1 values.

The compress() function allows you to tighten the dynamic range of the texture. It adheres to the following syntax:

compress(value,lo,hi) 

lo represents the lowest value that will be remapped to and hi the highest.

s with no compression


















Setting the compress() values to compress(s,0,2) will remap a value of 1 to 2 and 0.5 to 1 while leaving 0 unchanged.

s with (0,2) compression. what used to be 1 is now 2


















Changing the values to compress(s,0.5,1) will remap the value of 0 to 0.5 and 0.5 to 0.75 while leaving 1 untouched.

s with (0.5,1) compression. what used to be 0 is now 0.5


Gain

The gain() function (also known as a contrast function) has very different behavior. It leaves the values at 0, 0.5, and 1 unchanged and manipulates the values in the first segment (0 to 0.5) and the second segment (0.5, 1). Internally, it is calculated as two bias() operations applied to the first and second segments. The visual result of the gain()function is the increase or decrease of the value’s contrast.

Here is the function followed by Figure 5.7 and Figure 5.8, which are graphs of gain() at 0.25 and 0.85.

float gain(float val,g){
              return 0.5 * ((val < 0.5) ? bias(2 * val,1 - g) :
              (2 - bias(2 - 2 * val,1 - g)));
}



Bias

The bias() function behaves like a weight modifier: it allows you to control which part of the 0 to 1 range has more weight. Here is the bias() function, followed by Figures 5.5 and 5.6, which illustrate the output of applying the values 0.25 and 0.85 to the bias() function.


Parametrizing a value

Sometimes you will need the user to enter a value range 20 - 40 without telling user. You set this formula:

#define parametrizeVal(x,lo,hi) lo+x*(hi-lo)
float specVal = parametrizeVal(specParam, 20 ,40);

This way the user will have the input between 0 -1 and remap it to 20 -40

Divide by zero

Sometimes you will run into "divide by zero" error. To avoid this, you can:

float ss = s/txscale;
float tt = t/txscale;

//return a value either 0.00001 or greater)
float scaler = max(1/txscale, 0.00001);

float ss = s * scaler;
float tt = s * scaler;

As you develop your shaders, you will sometimes need to use a certain value within your shaders, but that value would make no sense at all as a shader parameter. One such example is when you are scaling a texture. Remember that texture spaces (s, t) are usually within the 0 to 1 range. To scale the texture, you could take a shader input txscaleand divide by the s or t value.

The problem with this approach is that you will run into a “divide by zero” error when the texture coordinate is 0 (you can’t divide a value by 0). The following code will more than likely generate rendering errors:
float ss = s/txscale;
float tt = t/txscale;


To get around this problem, we will invert our approach and add the logic to avoid division by zero. To do this, we will multiply the sand tvalues by 1/txscale. What this does is scale down the texture coordinates, resulting in the texture scaling up.

To prevent division by zero, we will use the max()shadeop to restrict the value to drop below a very small number. Here is the modified code.
float scaler = max(1/txscale,0.000001);
float ss = s * scaler;
float tt = t * scaler;

Signing a value

y = 2(x) -1

Turn 0 - 1 into -1 - 1.

Sometimes, you have a 0 to 1 range that you need to turn into –1 to 1. This operation is known as signing. To sign a 0 to 1 value, you need to multiply the value by 2 and then subtract 1 from it, such as y = (2 x) –1;. Signing can be very useful when dealing with noise values. noise()returns values that average around 0.5. Sometimes, you need the noise values to average between –0.5 and 0.5. In such cases, all you need to do is sign the noise value.

Inverting a value

Inverting a value
If x is in the range of 0 - 1:
y = 1 - x

If x is in the range of -1 - 1:
y = -1(x)