Indigo Shader Language Tutorial

ISL stands for Indigo Shader Language, the language for creating procedural materials in Indigo. Since it's a functional language, it can be a bit tricky to create some patterns that are easy enough in an imperative language. For example, lets say you want to write a shader to make a polka-dot pattern. In an imperative language you might write some code like

def fillWithColour(background_colour):
	for(int x=0; x<W; ++x) {
		for(int y=0; y<H; ++y) {
			drawDot(x, y, dot_colour);
		}
	}

But in ISL, you can't do things this way. You have to write a function that returns the colour, and that depends only on the position and/or UV-coordinates of the current surface point being shaded, e, g:

def getColourForPoint(u, v):
	if( the point (u, v) is inside a dot ){
		return dot_colour
	} else {
		return background_colour
	}

This tutorial shows you how to use such a functional technique to create regularly repeating patterns, like polka-dots, with ISL.

Lets start by discussing the fract function.

As described in the Indigo Renderer Manual, fract takes a single real number, and returns a real number:

 

fract(x) = x - floor(x)

 

The useful thing about this function, is that it repeats regularly across the real number line, with period 1. We can use this function to create more complicated behaviour.

 

So lets say we want to create some stripes, such that they alternate in the U direction of the UV coordinates. Suppose we have a foreground and background colour.

 

Using the fract function above, we can can assign the foreground colour when fract(x) < C, and the background colour when fract(x) >= C, where C is some constant between 0 and 1. If C is 0.5, the stripes will have the same width as the background stripes.

 

Let's see how that looks in real ISL:

<material>
	<name>previewmaterial</name>
	<diffuse>
		<albedo>
			<shader><shader><![CDATA[
def eval(vec3 pos) vec3 :
	if(
		fract(doti(getTexCoords(0))) < 0.5,
		vec3(0.9, 0.0, 0.0), # Red
		vec3(0.2, 0.2, 0.2) # Dark Grey
	)
			]]></shader></shader>
		</albedo>
	</diffuse>
</material>

And the resulting render:

The shader with fract(x), so slowly repeating stripes.

In this example the foreground colour is red, and the background colour is dark grey. This example looks a bit weird because the stripes are large compared to the model. We can solve this problem by multiplying the UV coordinates by a number greater than one before we pass the value to fract, e.g we could use something like fract(10 * x)

The ISL is then:


def eval(vec3 pos) vec3 :
	if(
		fract(doti(getTexCoords(0)) * 10.0) < 0.5,
		vec3(0.9, 0.0, 0.0), # Red
		vec3(0.2, 0.2, 0.2) # Dark Grey
	)

 

And the resulting render is:

The shader with fract(10 * x) so more repeating stripes.

So, at this point in the tutorial, we have more-or-less solved the problem of how to create regularly-repeating patterns, at least with respect to the U coordinate (e.g. in one direction).