Simple Brick OSL port of RSL Brick Texture on Suzanne |
With some self-courage, I am attempting to write an introduction post on OSL, to make more sense of Open Shading Language (OSL) that has been implemented inside Blender for over a year today.
I know it myself that this is not going to be easy, but somewhat I have big interest in this area and some of you are too.
I found shader writing to be really abstract, just like learning to read and write a program or when thinking of high level Math. Very elegant and exotic area. None of them makes sense at first. Even when someone attempting to explain it to you, it takes time until you get it or ... maybe you will never quite get it, unless, you push yourself to actually write the code and slowly see what happens.
Although Shader Writing may seems hard and complicated, there is a joy in it and guaranteed a big discovery to be found. I personally feel that shader writing and programming are very free-ing, just like drawing and sketching. Kind of meditative in the way.
In shader writing, we are able to define our own rules and parameters, we can be experimental (highly recommended to do so), and we may get outcome that sometimes accidental, but mostly the way we want it.
Shader Writing is probably not much different than trying to control Particles and Volume with Force.
I advice you to look around first for some available materials that talk about OSL. They are around, although things are all over the place. You will definitely find some simple OSL ... and then big gap that show you complex OSL codes.
Start from here:
http://www.openshading.com/
I think Dingto of Blender developer/artist is one person you need to channel your brain into when it comes to Blender Cycles and OSL implementation.
Once again, like a good absorbing sponge, I will try to absorb some wealth of information from other blogs and forums, and try to restructure and filter the contents and write it down here.
Hopefully, you can get something from this. Have a believe that by writing the unknown, one will learn something along the way.
NOTE: I promise that I am going to write down the sources of every OSL codes that I collects and rewrite in here, with some additional notes of my own. All credits of OSL source belong to the person posting it at the forum.
This post will just be a basic of OSL. Probably at the beginning, it is quite an achievement to create Sine Curve. Maybe some day, we can create fancy screensaver Shader ala MacOS or maybe creating all sort of PATTERNS like Ukrainian patterns, Batik, etc, etc.
Without further a due, let's get started.
WHAT IS OSL?
In my own word:OSL is another written shading languages just like RSL (Renderman Shading Language), GLSL, etc .... basically a collection of functions and parameter values to describe how mesh surface (pixel) should look when rendered.
OSL is dependant on Closure for light --> I might not be 100% here correct, but anyways, for now, just think if you want Diffuse, Glass, Metalic look, you simply assign and pass to available Closure. You could write your own Closure, but that is an advanced OSL area.
Read more about OSL here, find out where it is originated (Sony Picture Imageworks):
https://github.com/imageworks/OpenShadingLanguage
Let's create an analogy, imagine a modern photography + post-processing today that is using Smartphone + endless Apps.. there are SO MANY filters available that enable smartphone users to capture an image and post-process it in realtime/not so realtime.
OSL allows that kind of ability, giving user power to write and code a shader from scratch or mix and match it the way you like.
For general users, it is totally fine to just use the existing Nodes in Cycles and you probably never have to write or code in OSL. There are plenty already, and you can create nice shaders. There is Uber Shader in Cycles for example (that can certainly be re-written as OSL shader).
Node network creation of Shader is fine, but the problem with visual programming using nodes is that at some point, it gets really complex that actually things will be better written as code. This is when Shader Writing like OSL may come in handy.
You can actually translate any Blender Cycles Node network and combine it into a single OSL Shader. It is not unlike collapsing a complex Blender Cycles network into "easy to use node". More or less the same in that context, simplifying a complex network of nodes into a single node with INPUT(s) and OUTPUT(s).
However, OSL can do a lot more:
Quip + Quipcam:
http://vadrouillegraphique.blogspot.fr/2013/06/quip-and-quipcam-fast-fx-shaders.html
OSL Interior Mapping:
http://fbbdev.wordpress.com/2012/10/12/cycles-osl-interior-shader/
OSL goes further at low level, the unknown area, the complexity.
Note: OSL is also a language used by Arnold, another good commercial rendering engine, I heard.
WHAT IS MY BACKGROUND IN SHADER WRITING?
Not much, actually *cough*.OSL is my first time attempt to do shader writing seriously. So, I am sorry if I am not really an expert. Being really honest, I am probably as newbie in shader writing as many of you. Experts in OSL may laugh at what I wrote.
In the past, I used to be very into Maya's Hypershade and connecting nodes to create Texture and Shader. I still am, although not as often --- unless required. I like some of Maya procedural shader nodes. Not long ago, Maya got something called Substance node, which is pretty handy node that takes a pre-package Substance materials:
http://www.allegorithmic.com/products/substance-designer
The Substance workflow is kind of nice allowing streamlined procedural creation and also baking textures into Diffuse Map, Specular Map, Normal Map, and so on.
Maya also has a nice Pixar Renderman (PRMan) integration at some point, that allows user to use Renderman Shader using a node.
Few years ago, I actually tried to learn RSL (Renderman Shading Language)... I did read some books about RSL and flicked through some of them. The basics, I sort of understand, but it gets seriously complicated and the problem is the available materials are too hard to follow for me at the time.
Next, I am also exploring some VEX shading nodes in Houdini Mantra. Houdini has quite a powerful render engine and complex shader network creations. Blender renderer Cycles is slowly started to look like Houdini Mantra. Especially with OSL being developed.
http://www.tokeru.com/t/bin/view/Maya/MayaToHoudini
And I also studied some Processing language. This somewhat helps in visualizing code that creates Texture.
I have not much experimenting with Blender Cycles Node Networking.
All those above actually helps me to get where I am now: Writing OSL code in Blender.
Maybe one day I get back to RSL again when required. However right at this moment, I am happy to be able to get into OSL Shader Writing in Blender, on any computer I could find whether I am at Library or on friend's computer. All we need is a computer and Internet and Blender.
The advantage of this Blender environment is that we are all learning it together and this great learning tool is available for everyone and can be used in production right away.
I personally think there is a huge gap between:
THE BASIC ----> THE VERY COMPLEX OF THINGS.
However, nothing should stop you taking the first step.
NOTE: Do not believe everything I wrote in here, I am trying to figure out a lot of thing about OSL magic. Maybe you will figure it out yourself ;)
BLENDER SETUP FOR OSL
In order to start using OSL, you need to set Blender environment.1. Set Render Engine to Cycles.
2. Enable OSL.
OSL only works in CPU mode at the moment.
3. Set the Layout
You can arrange the layout as you like, but I like to use the Compositing Layout and modify it myself like below:
4. Use Script Node (the PyNode in Blender)
Everytime you point the Script node into the path of OSL Shader, you will "compile" the OSL Shader. It turns bunch of Text codes into a single useful NODE with INPUT and OUTPUT slots.
5. Name it.
I recommend that you always name your OSL like this: *.osl inside the Text Editor. Doing this, you let Blender know what you are writing and you have OSL highlighting and also special OSL update button:
6. You want to always see the changes you made with OSL code (everytime you click on Script Node Update), so you need to enable Texture Render real time thing in Blender Cycles: Viewport Shading - Rendered.
You can certainly COPY and PASTE OSL code examples below to see the resulting Texture.
However, I want you to retype every single code below so you can start to see the PATTERN and understand the SYNTAX and make a lot of mistakes and fixes the mistakes.
You need to be able to write the code from top of your head, then can eventually start changing and modifying the code for your own need.
THE BASICS STRUCTURE OF OSL
#include "stdosl.h"shader NAME_OF_SHADER
(
// HEADER_DECLARATION_OF_INPUT_VARIABLES
varType varName1 = value,
varType varName2 = value,
varType varName3 = value,
output color Col = (0.8)
)
{
// BODY, FUNCTIONS
Col = .... ;
}
An Empty OSL Shader looks like this: (one of OSL templates provided with Blender)
shader name()
{
}
A good idea is to start with simple functions, see the pattern and get really familiar with it, before going to complex OSL. Use a flat plane mesh to check the shadings, probably work in 2D first.
At this moment, I think I will just focus on OSL Shader TEXTURE creation. Not so much on other complex stuffs that OSL can do. I am happy to be able to
Let's do a step by step exploration of OSL from the easiest OSL Shader Texture we could create.
THE INVISIBLES: INTERNAL AND GLOBAL VARIABLES
“Use the Force, Luke” - Yoda.
NOTE: I will start to use some Star Wars and The Matrix analogy and quotes here to make OSL makes more sense.
There are some variables in OSL that is not visible, but they are there. We must remember that we cannot really change what is already there in nature, but can use them and drive them to create and see the FORCE.
Some of these "INVISIBLES" we will use today:
Point Surface (P) = positions of pixel in XYZ, which as you can see later we can separate into X, Y, Z by accessing P[0], P[1], and P[2].
Normal (N) = this is Object Surface attribute that is normally perpendicular to the surface, this is also some kind of Vector type of 3 values.
u and v
u and v
OSL: Color
shader simpleColor(color myColor = color(1,0,0),
output color Col_Out = color(0.6)
)
{
}
(Above incomplete OSL Shader will compile and we can see INPUTS and OUTPUTS port, but it does nothing much).
You see above how I am defining the "argument variables" for the Shader function. I have 2 variables: myColor and Col_Out.
Those variables were defined as variable type of Color. Those variables need to be assigned a default value. The function color(R,G,B) or color(Greyscale) is required for variable of type Color.
You also notice that I describe Col_Out variable as OUTPUT. This is required. Important: every Output Variables needs default initializer. You can have multiple Outputs.
Ok, now we have minimum INPUT and OUTPUT.
This OSL Shader, if we use it now, won't be much of usage, but it is already working. Color value of 0.6 greyscale value that will be used as Color output when we plug and pass the value into a Closure (Diffuse BSDF node).
The Shader is incomplete though, because we did not tell any connection between "myColor" and the actual output of "Col_Out". I assign a Red color (1,0,0) to myColor, but it is not used, yet.
Let's do that now.
shader simpleColor(
color myColor = color(1,0,0),
output color Col_Out = color(0.6)
)
{
Col_Out = myColor;
}
That is the simplest shader. You made it so far and you did a good job.
OUTPUT CLOSURE
From the above setup, we pass on the OSL Shader (Texture) into a Diffuse BSDF Closure.We could, actually integrate the Closure into the OSL Shader itself, like so:
shader simpleColor(
color myColor = color(1,0,0),
output color Col_Out = color(0.6),
output closure color MyBSDF = diffuse(N)
)
{
Col_Out = myColor;
normal Nn = normalize(N);
MyBSDF = Col_Out * diffuse(Nn);
}
There are many other available Closures that you can use right away:
...
...
OSL: Mixer 2 Colors > 1 Color
After we create the simple Color Shader, next we can re-create Mix node using OSL. We leave the Closure for now and just focusing on Texture Color.NOTE: However, writing code that generate procedural Texture is not easy either, but more visual.
shader mixer(
color ValueA = color(1,0,0),
color ValueB = color(0,0,1),
float Fac = 0.5,
output color Color_Out = color(0.8)
)
{
Color_Out = mix(ValueA, ValueB, Fac);
}
Another simple shader that mixes 2 Color (ValueA and ValueB) into 1 Color based on Fac value.
Next we will use P (point) attribute as mixing factor, which will in fact create a Gradient.
OSL: Gradient X, Y, Z
Gradient or ramp blending can be created by using the floating value of Point position in either of 3 axis (x/y/z). This is the first time we will use the invisibles Global Variable: Point Positions (P). They will start to create THE FORCE.
point Vector = P,
color ValueA = color(1,0,0),
color ValueB = color(0,0,1),
//float Fac = 0.5,
output color Color_Out = color(0.8)
)
{
float x = P[0];
float y = P[1];
float z = P[2];
Color_Out = mix(ValueA, ValueB, x);
}
NOTE: I did something that slightly wrong above, where I am assigning the P (position attribute) inside the functions, while I also already defining Vector attribute that has P values. To be more correct, I should have done:
float x = Vector[0];
float y = Vector[1];
float z = Vector[2];
Here, we are basically separating Vector 3 Values of Point into 3 Floats. At this moment, I have unconnected pipe between the INPUT Vector which is assigned P by default and recreating internal values.
Based on Point X |
Above, based on Pixel Point Position, we create OSL Texture with gradient that spans across 2 value. Instead of using x, we can also use y or z.
Based on Point Y |
Based on Point Z |
OSL: Stripes Gradient Sine X, Y, Z
Here, you could already experiment on using Math function like sin(), cos().shader smooth_stripes(
point Vector = P,
color ValueA = color(1,0,0),
color ValueB = color(0,0,1),
//float Fac = 0.5,
output color Color_Out = color(0.8)
)
{
float x = P[0];
float y = P[1];
float z = P[2];
Color_Out = mix(ValueA, ValueB, sin(x));
}
This is one way you could create Stripe Texture. There are actually many ways to achieve this.
With this method, you will notice that we are getting "Smooth" Stripes. The reason being: function like Sine gives a Smooth Wave Curve that smoothly goes between the 2 Colors back and forth.
What if we want a stripe without smoothing? We can use function floor().
shader stripes(
point Vector = P,
color ValueA = color(1,0,0),
color ValueB = color(0,0,1),
//float Fac = 0.5,
output color Color_Out = color(0.8)
)
{
float x = P[0];
float y = P[1];
float z = P[2];
Color_Out = mix(ValueA, ValueB, floor(sin(x))+1); // I give an offset value of 1 here
}
Other than using floor() function, you may see in other OSL that multiplying it with a constant will turn Smooth force into a Hard-Edge force.
OSL: Single Line
Before we go too far with "repeating texture", we take a few steps back to create just a single line based on the gradient/ramp.
This is quite important for our understanding.
This is quite important for our understanding.
Still based on above OSL code, we can create a line using abs() function which means instead of getting gradient/ramp, we are getting something that is more like a parabolic curve (somewhere in there we have -1 to 1 value, although the lines goes into infinity).
Here, you will see how I am starting to create "The Force". This is probably the first interesting thing you see in OSL. We could call it formula or function, or any other name, but I think I will call it force.
Here, you will see how I am starting to create "The Force". This is probably the first interesting thing you see in OSL. We could call it formula or function, or any other name, but I think I will call it force.
shader line(
point Vector = P,
color ValueA = color(1,0,0),
color ValueB = color(0,0,1),
//float Fac = 0.5,
output color Color_Out = color(0.8)
)
{
float x = P[0];
float y = P[1];
float z = P[2];
float FORCE = 1/abs(x);
Color_Out = mix(ValueA, ValueB, FORCE);
}
You can INVERT the line if you use abs() function on the FORCE. For above, instead of BLUE line, we can have RED line.
/*
SOURCE: http://nccastaff.bournemouth.ac.uk/jmacey/Renderman/
*/
shader Lines (
color LineColor = color(1,0,0),
color MixColor = color(1,1,1),
float fuzz = 0.025,
float LineSize = 0.1,
float Orient = 0,
float offset = 0.5,
output color Col_Out = color(0.2)
)
{
color Ct;
float s = P[0];
float t = P[1];
// we care about texturing only
float inTop;
float dist;
if(Orient==0)
dist = abs(t-offset);
else
dist = abs(s-offset);
float inLine;
inTop = 1 - smoothstep(LineSize/2.0 - fuzz, LineSize/2.0+fuzz, dist);
Ct = mix(MixColor, LineColor, inTop);
Col_Out = Ct;
}
shader line(
point Vector = P,
color ValueA = color(1,0,0),
color ValueB = color(0,0,1),
//float Fac = 0.5,
output color Color_Out = color(0.8)
)
{
float x = P[0];
float y = P[1];
float z = P[2];
float FORCE = abs(x + y);
Color_Out = mix(ValueA, ValueB, FORCE);
}
OSL: Better Line
I need to add this here, this is probably a better function for LINE./*
SOURCE: http://nccastaff.bournemouth.ac.uk/jmacey/Renderman/
*/
shader Lines (
color LineColor = color(1,0,0),
color MixColor = color(1,1,1),
float fuzz = 0.025,
float LineSize = 0.1,
float Orient = 0,
float offset = 0.5,
output color Col_Out = color(0.2)
)
{
color Ct;
float s = P[0];
float t = P[1];
// we care about texturing only
float inTop;
float dist;
if(Orient==0)
dist = abs(t-offset);
else
dist = abs(s-offset);
float inLine;
inTop = 1 - smoothstep(LineSize/2.0 - fuzz, LineSize/2.0+fuzz, dist);
Ct = mix(MixColor, LineColor, inTop);
Col_Out = Ct;
}
OSL: Diagonal Line
Adding the absolute FORCE of X and Y and we get... diagonal force.shader line(
point Vector = P,
color ValueA = color(1,0,0),
color ValueB = color(0,0,1),
//float Fac = 0.5,
output color Color_Out = color(0.8)
)
{
float x = P[0];
float y = P[1];
float z = P[2];
float FORCE = abs(x + y);
Color_Out = mix(ValueA, ValueB, FORCE);
}
OSL: Single Line Sine Wave
# include "stdosl.h"
shader wave_line (
float amplitude = 2.0,
float offset = 0.0,
point Vector = P,
color colorA = color(1,0,0),
output color Col_Out = color(0.8)
)
{
float x = P[0];
float y = P[1];
Col_Out = 1/abs(x + sin(y + offset) * amplitude);
}
LIMITING THE FORCE
“There is No Spoon” - Neo.
I am not quite sure what is happening here, but one day I decided to use clamp() function and the resulting is kind of TRIMMING the force back into invisibility.# include "stdosl.h"
shader wave_line_clamped (
float amplitude = 2.0,
float offset = 0.0,
point Vector = P,
color colorA = color(1,0,0),
output color Col_Out = color(0.8)
)
{
float x = P[0];
float y = P[1];
float FORCE = x + sin(y + offset) * amplitude;
float CLAMPER = clamp (FORCE, 0, 1);
float INVERT = 1/abs(CLAMPER);
Col_Out = INVERT;
}
Another clamping stuff, see how we can BEND the SPOON(or FORCE), like in the Matrix.
# include "stdosl.h"
shader wave_line_clamped (
float amplitude = 2.0,
float offset = 0.0,
point Vector = P,
color colorA = color(1,0,0),
output color Col_Out = color(0.8)
)
{
float x = P[0];
float y = clamp (P[1], -5,5);
float FORCE = x + sin(y + offset) * amplitude;
float INVERT = 1/abs(FORCE);
Col_Out = INVERT;
}
# include "stdosl.h"
shader wave_line (
float amplitude = 2.0,
float offset = 0.0,
point Vector = P,
color colorA = color(1,0,0),
output color Col_Out = color(0.8)
)
{
float x = P[0];
float y = P[1];
float FORCE = x + sin(y + offset) * amplitude;
float CLAMPER = clamp(FORCE,-1, 1);
float INVERT = 1/abs(CLAMPER);
Col_Out = INVERT;
}
# include "stdosl.h"
shader wave_line (
float amplitude = 2.0,
float offset = 0.0,
point Vector = P,
color colorA = color(1,0,0),
output color Col_Out = color(0.8)
)
{
float x = P[0];
float y = P[1];
float FORCE = x + sin(y + offset) * amplitude;
float CLAMPER = smoothstep(FORCE,-1, 1);
float INVERT = 1/abs(CLAMPER);
Col_Out = INVERT;
}
# include "stdosl.h"
shader wave_line (
float amplitude = 2.0,
float offset = 0.0,
point Vector = P,
color colorA = color(1,0,0),
output color Col_Out = color(0.8)
)
{
float x = P[0];
float y = P[1];
float FORCE = x + sin(y + offset) * amplitude;
float CLAMPER = noise(FORCE,y);
float INVERT = 1/abs(CLAMPER);
Col_Out = INVERT;
}
shader line(
point Vector = P,
color ValueA = color(1,0,0),
color ValueB = color(0,0,1),
//float Fac = 0.5,
output color Color_Out = color(0.8)
)
{
float x = P[0];
float y = P[1];
float z = P[2];
y = mod(cos(abs(y)), 2);
float FORCE = abs( x + y * 2 );
Color_Out = mix(ValueA, ValueB, FORCE);
}
shader line(
point Vector = P,
color ValueA = color(1,0,0),
color ValueB = color(0,0,1),
//float Fac = 0.5,
output color Color_Out = color(0.8)
)
{
float x = P[0];
float y = P[1];
float z = P[2];
y = abs(sin(y));
float FORCE = abs( x + y * 2 );
Color_Out = mix(ValueA, ValueB, FORCE);
}
OSL: MODULATION STRIPES
We can add and multiple forces.Here is another way of getting a stripes...
# include "stdosl.h"
shader mutiple_lines(
point Vector = P,
color ValueA = color(1,0,0),
color ValueB = color(0,0,1),
float Fac = 0.5,
output color Color_Out = color(0.8)
)
{
float x = P[0];
float y = P[1];
float z = P[2];
float stripeX = mod(abs(sin(x)), 1);
float stripeY = mod(abs(sin(y)), 1);
float stripeZ = mod(abs(sin(z)), 1);
float FORCE = stripeX * stripeY * stripeZ;
Color_Out = mix(ValueA, ValueB, 1/FORCE);
}
You see how above, I am "multiplying" values of Stripes in X, Y, Z and ended up with a combination. Multiply, Add, etc, works similar to when you are doing Layering and Blending of Texture.
Or, maybe we can do it like below for Stripes along X only:
shader gradient(
int Stripe = 1,
point Vector = P,
color ValueA = color(1,0,0),
color ValueB = color(0,0,1),
float Fac = 0.5,
output color Color_Out = color(0.8)
)
{
float x = P[0];
float y = P[1];
float z = P[2];
float stripeX = mod(abs(sin(x * Stripe * 0.1)), 1);
//float stripeY = mod(abs(sin(y)), 1);
//float stripeZ = mod(abs(sin(z)), 1);
float FORCE = stripeX;
Color_Out = mix(ValueA, ValueB, 1/FORCE);
}
UPDATE 20130802: More on MODULO
I am reading my own writing and testing some of the codes. Probably I need to clean up some of the OSL writing so that it makes more sense. I still do not quite get the MODULO mod() function. It supposed to repeat, but does not always work as I expected it. Lots of Math functions are a little bit strange. So, let's explore how MODULO functions...
1. Starting with Gradient/Ramp/Blend based on X.
shader stripes(
color Color_A = color(1,0,0),
color Color_B = color(0,0,1),
float Fac = 0.5,
point Vector = P,
output color Color_Out = color(0.8)
)
{
float x = Vector[0];
float y = Vector[1];
Color_Out = mix(Color_A, Color_B, Fac + x);
}
Writing the code like above allowing you to add Noise or other node that will Fac into the gradient.
2. Inserting Modulo.
shader stripes(
color Color_A = color(1,0,0),
color Color_B = color(0,0,1),
float Fac = 0.5,
point Vector = P,
output color Color_Out = color(0.8)
)
{
float x = Vector[0];
float y = Vector[1];
float FORCE = mod(x,1);
Color_Out = mix(Color_A, Color_B, Fac + FORCE);
}
Thing does repeat, although I cheated and scale the original object. Probably we need to insert another Variable to control the Scaling.
I also found that the modulation is not quite right.
3. Mixing Sin/Cos with Modulo
shader stripes(
color Color_A = color(1,0,0),
color Color_B = color(0,0,1),
float Fac = 0.5,
point Vector = P,
output color Color_Out = color(0.8)
)
{
float x = Vector[0];
float y = Vector[1];
x = sin(y);
float FORCE = mod(x, 0);
Color_Out = mix(Color_A, Color_B, Fac + FORCE);
}
4. More exploration.
shader stripes(
color Color_A = color(1,0,0),
color Color_B = color(0,0,1),
float Fac = 0.5,
point Vector = P,
output color Color_Out = color(0.8)
)
{
float x = Vector[0];
float y = Vector[1];
x = sin(x) + sin(y);
float FORCE = mod(x, 0);
Color_Out = mix(Color_A, Color_B, Fac + FORCE);
}
5. Adding the Z into calculation of the FORCE.
shader stripes(
color Color_A = color(1,0,0),
color Color_B = color(0,0,1),
float Fac = 0.5,
point Vector = P,
output color Color_Out = color(0.8)
)
{
float x = Vector[0];
float y = Vector[1];
float z = Vector[2];
x = sin(x) + cos(y) + cos(z);
float FORCE = mod(x, 0);
Color_Out = mix(Color_A, Color_B, Fac + FORCE);
}
This is one of those happy surprise, I was not expecting the texture to work like this.
6. Further more... this time I increase the Modulo and also I use Blender built in Noise and ColorRamp function to further refine the texture.
shader stripes(
color Color_A = color(1,0,0),
color Color_B = color(0,0,1),
float Fac = 0.5,
point Vector = P,
output color Color_Out = color(0.8)
)
{
float x = Vector[0];
float y = Vector[1];
float z = Vector[2];
x = sin(x) + cos(y) + cos(z);
float FORCE = mod(x, 1);
Color_Out = mix(Color_A, Color_B, Fac + FORCE);
}
This started to resemble an interesting Tile Design that we may find inside Islamic Mosques.
Although of course this Texture Tile is rather simple, however I am quite happy with the outcome so far. Maybe you could come up with more intricate texture just based on the simple knowledge.
I have plenty of questions such as:
- How to Tile properly?
- How to Rotate and Animate the texture? Is it just via Offset or more?
- How to create Flower or Star pattern?
- What is a good efficient Shader calculation?
Dark Side Suzanne |
I reckoned, it is a good idea to explore some of Islamic Art at some point, which is really originated from clever Mathematical formulas. Maybe my future post.
That could make a nice topic or even a book that is exploring the Islamic Art and Computer Graphics. Or maybe Buddhist Art and Computer Graphics, or Christians and CG. I don't know. I am pretty universal regarding religions and beliefs...
Anyways, basically all kinds of Patterns from around the world: Oriental, Chinese, Japanese, Korean, Thai, Indonesian, Egyptians, Persian, Ukrainian etc.
shader stripes(
color Color_A = color(1,0,0),
color Color_B = color(0,0,1),
float Fac = 0.5,
point Vector = P,
output color Color_Out = color(0.8)
)
{
float x = Vector[0];
float y = Vector[1];
float z = Vector[2];
x = sin(x) + cos(y) * 0.5 ;
float FORCE = mod(x, 0);
Color_Out = mix(Color_A, Color_B, Fac + FORCE);
}
Katamari Ball? |
shader stripes(
color Color_A = color(1,0,0),
color Color_B = color(0,0,1),
float Stripes = 5.0,
float Fac = 0.5,
point Vector = P,
output color Color_Out = color(0.8)
)
{
float x = Vector[0];
float y = Vector[1];
float z = Vector[2];
x = sin(y * Stripes ) + cos(y * Stripes) ;
float FORCE = x;
Color_Out = mix(Color_A, Color_B, Fac + FORCE);
}
OSL: Circle, based on Dingto's YIN YANG OSL Shader
“Be Water, my Friend.” - Bruce Lee.
I think Dingto's YIN YANG is probably one of the most beautiful interesting example. Google for it to get the original OSL.I broke down his Yin Yang OSL into just a Circle. I think we can study further the Yin Yang OSL to understand some more.
Dingto's Yin Yang OSL, keep looking at this shader with sense of wonder... |
However, for now, something like below create a solid Circle.
float circle(float x, float y)
{
float nx = x;
float ny = y;
x = nx*cos(0.2) - ny*sin(0.2);
y = nx*sin(0.2) + ny*cos(0.2);
float h = x*x + y*y;
float b = h - 100;
return b;
}
shader gradient(
point Vector = P,
color ValueA = color(1,0,0),
color ValueB = color(0,0,1),
//float Fac = 0.5,
output color Color_Out = color(0.8)
)
{
float x = P[0];
float y = P[1];
float z = P[2];
float XXX = circle(x,y);
Color_Out = mix(ValueA, ValueB, XXX) ;
}
OSL: Dots (with Size and OffsetX, OffsetY)
float graph2d(float x, float y, float size) {float g = (x * x + y * y) / size;
return g;
}
shader dot(
point Vector = P,
color ValueA = color(1,0,0),
color ValueB = color(0,0,1),
float offsetX = 0.0,
float offsetY = 0.0,
float size = 10.0,
output color Color_Out = color(0.8)
)
{
float x = P[0];
float y = P[1];
float z = P[2];
x += offsetX;
y += offsetY;
float FORCE = graph2d(x,y, size);
Color_Out = mix(ValueA, ValueB, FORCE);
}
float graph2d(float x, float y, float size) {
float g = (x * x + y * y) / size;
return g;
}
shader dot(
point Vector = P,
color ValueA = color(1,0,0),
color ValueB = color(0,0,1),
float offsetX = 0.0,
float offsetY = 0.0,
float size = 10.0,
output color Color_Out = color(0.8)
)
{
float x = P[0];
float y = P[1];
float z = P[2];
x += offsetX;
y += offsetY;
float FORCE = graph2d(x,y, size);
Color_Out = mix(ValueA, ValueB, 1/FORCE);
}
I think I did it wrong with this, but I quite like the result.
OSL: Pie / Watermelon
Based on Gottfried Hofmann's Pie OSL.http://www.blenderartists.org/forum/showthread.php?270332-OSL-Goodness
#include "stdosl.h"
color pie(point p, int Divides, float Angle, float Noise)
{
float angle_new;
angle_new = atan2(p[0],p[1]) + Angle*3.1415926/180.0;
return color(0.5 - 0.5*sin(angle_new * Divides - Noise - 0.5));
}
shader pie(
int Divides = 2,
float Angle = 5.0,
float Noise = 0.0,
point Vector = P,
output color Color = color(0.0, 0.0, 0.0))
{
Color = pie(Vector, Divides, Angle, Noise);
}
OSL: Volley Ball
Kind of similar to above Pie OSL, but somewhat different.
# include "stdosl.h"
shader volleyBall (
float amplitude = 2.0,
float offset = 0.0,
float frequency = 1.0,
point Vector = P,
float Angle = 0.0,
output color Col_Out = color(0.8)
)
{
float x = P[0];
float y = P[1];
float z = P[2];
float angle_intern = atan2(x, y) + Angle * 3.1415926/180.0;
float formula = sin(frequency*angle_intern + offset) * amplitude + cos(frequency*angle_intern+ offset) * amplitude;
Col_Out = 1/abs(formula);
}
OSL: Clockwise Gradient
shader clockwise(
point Vector = P,
color ValueA = color(1,0,0),
color ValueB = color(0,0,1),
float Fac = 0.5,
output color Color_Out = color(0.8)
)
{
float x = P[0];
float y = P[1];
float z = P[2];
float value = smoothstep(x, y, 1);
Color_Out = mix(ValueA, ValueB, value);
}
OSL: Strength of Line
“The Force is strong with this one” - Darth Vader.
Here, we can see how we can control the strength of the Force using our own Constant Variables.
shader lineForce (
int Stripes = 5,
point Pos = P,
color MyColor = color(0.2,0.3,1.0),
float Thickness = 1.0,
float Glow = 1.0,
output color Col = (0.8) // or Out
)
{
float t;
float posX = Pos[0];
float posY = Pos[1];
t = Thickness/abs(posX) / Glow; // inverted absolute of PositionX
for (int i=0; i < Stripes; i++){
posY += i * 5;
t *= 1/abs(posX);
}
//t *= posX - 0.5;
Col = t * MyColor; // assign it to the output slot
}
OSL: Spiral
Work in progress....OSL: Multiple Circles
Work in progress....
OSL: Circle With Border / The Ring
http://bencrowder.net/blog/2013/05/open-shading-language-in-blender/Ben's example of Circle With Border Texture OSL Shader is actually quite interesting. Here we can see how he uses Distance Field (or Force) to create Ring. This is probably some kind of Shader you can use to create poster of Japanese horror Movie "The Ring".
#include "stdosl.h"
// SOURCE: http://bencrowder.net/blog/2013/05/open-shading-language-in-blender/
shader ring(
float intensity = 0.1,
float border = 0.4,
float radius = 1.0,
output color Out = color(0.8, 0.6, 0.4)
)
{
point pt = P; // The point that gets passed in
float x = pt[0]; // OSL uses array indices for x,y
float y = pt[1];
// Get the distance from (0, 0) to the point we're processing and then
// subtract from the radius to get the distance from the circle to the point
float dist = radius - sqrt(x * x + y * y);
// If dist < 0, point is outside the circle
// If dist > border, point is inside the circle
// If dist <= border && dist > 0, point is on the circle
float t = 0;
if (dist > border) {
t = 1;
} else if (dist > 0) {
t = dist / border;
}
// Add the intensity control and make sure the value >= 0
float final = abs(intensity / t);
// Create the output color (and drop the red channel by half)
color c = color(final / 2, final, final);
Out = c;
}
The way the texture shader is written above is very interesting allowing user to plug other node into the INPUT attribute of the OSL: Ring and get different result immediately. This is kind of thing we should think when we write our own OSL node.
TRANSFORM TEXTURE WITH OBJECT
To avoid "texture swimming", we need to assign Texture Space to our OSL Texture. We can either doing it directly inside OSL or using Blender Cycles Texture Coordinate node and Mapping node that goes into the Vector position.If you add a single code below, it will properly assign Texture Space to the "object" space.
point P = transform("object", Vector);
Again, you probably notice that in all above OSL code, I have been using it P directly, and this is kind of the "wrong way". I always use Vector = P variables.
I should have done this:
x = Vector[0];
y = Vector[1];
z = Vector[2];
So, if we correctly use the actual Variables, we can do something like below, if we do not want to assign Texture Object Space in OSL code:
SELF-QUESTION: Texture Space? ST in RSL, UV in OSL? UVW? Does it matter? If I am not wrong, we can use global variables U and V.
RSL PORT: CHECKERED PATTERN
Based on:http://renderman.pixar.com/view/simple-shader-writing
This is my first attempt to port RSL Shader to OSL. I am not fully understand it, I seem to replace S and T with the Position X and Position Y of Pixel and it seems to work. I also leave some other RSL global variables that does not seemed to be required, such as Opacity. I am not sure yet how Opacity is being treated inside OSL.
# include "stdosl.h"
shader checkered (
int frequencyS = 1,
int frequencyT = 1,
int bendScaleS = 1,
int bendScaleT = 1,
int modAmount = 2,
float fac = 0.5,
color Color1 = color(1,0,0),
color Color2 = color(0,1,0),
point Pos = P,
output color ColorOut = color(0.8),
output closure color MyBSDF = diffuse(N)
)
{
float PI = 3.1415926535897932/180.0;
//float PI = math.pi;
float posX = Pos[0];
float posY = Pos[1];
float s = posX;
float t = posY;
float ss = s + sin(t * 2 * PI) * bendScaleS;
float tt = t + sin(s * 2 * PI) * bendScaleT;
float stripeS = floor(ss * frequencyS);
float stripeT = floor(tt * frequencyT);
float xxx = mod(stripeS + stripeT, modAmount);
normal Nn = normalize(N);
ColorOut = mix(Color1, Color2, xxx);
MyBSDF = ColorOut * diffuse(Nn);
}
RSL PORT: 3D TEXTURE?
# include "stdosl.h"
// http://www.fundza.com/rman_shaders/pattern3d/index.html
surface sine3d(
float freq = 4,
float amp = 0.5,
float offset = 0,
float blur = 0.05,
output color Col_Out = color(0.8)
)
{
// Convert the shading point to the object coordinate system.
point p = transform("object", P);
// Get the coordinates of the shading point.
float x = p[0];
float z = p[2];
// The arc tangent of the shading point will be in radians
// (-3.142 to 3.142) ie -180 to 180 degress.
float theta = atan2(x,z);
// Generate a sine wave - remapped to 0 to 1.
float wave = (sin(theta * freq) + 1) * 0.5;
// Adjust the height and position of the sine wave.
float waveHt = wave * amp + offset;
// Get a filtered value of the wave height.
waveHt = smoothstep(waveHt - blur, waveHt + blur, p[1]);
Col_Out = mix(color(1,1,1), color(0,0,0), waveHt);
}
It's always interesting to manually translate Shader code from one language to another, it helps to understand both language and see the differences. Comment out parts with // notation. Maybe syntax or variable from one language is different and you can // comment out that lines and see if the translated code is compiled in the new language.
If we study hard learning OSL, maybe we can get to know RSL too and it is great.
http://www.smartcg.com/tech/cg/courses/RMan/notes/index.html
Quicky Porting: RSL Turbul Function
At this moment, I don't quite understand the RSL Ka, Kd, Ks, faceforward(), normalize() function, etc and how it translates properly to OSL. However, I am interested with the function turbul() in this code below:
http://www.smartcg.com/tech/cg/courses/RMan/notes/Class02/Class02.html
// SOURCE: http://www.smartcg.com/tech/cg/courses/RMan/notes/Class02/Class02.html
// Roughly ported from RSL to OSL by Jimmy Gunawan, 2013, Blender Sushi.
float turbul(
point PP,
int n
)
{
float t;
if(n==4)
t = 0.5333*(noise(PP) + 0.5*(noise(2*PP)) + 0.25*(noise(4*PP)) + 0.125*(noise(8*PP)));
else if (n==3)
t = 0.5714*(noise(PP) + 0.5*(noise(2*PP)) + 0.25*(noise(4*PP)));
else if (n==2)
t = 0.6667*(noise(PP) + 0.5*(noise(2*PP)));
else if (n==1)
t = noise(PP);
return t;
}
shader smarble(
//float Ka=1,
//float Kd=0.6,
//float Ks=0.4,
//float roughness=0.2,
//float Ksin=1.0,
float freq=0.1,
int n=4,
color myColor = color(1,1,0),
output color Col_Out = color(.25)
)
{
point PP;
float cmi;
normal Nf;
vector V;
color diffuseColor;
//Nf = faceforward(normalize(N), I);
//V = -normalize(I);
PP = transform("shader", P) * freq;
// float t = 0.5*(1+sin(Ksin*3.1415*turbul(PP,n)));
float FORCE = turbul(PP,n);
// print(u);
/*
color base = color spline(FORCE,
color (0.8, 0.2, 0.05),
color (0.8, 0.2, 0.05),
color (0.8,0.5,0.3),
color (0.6,0.594,0.58),
color (0.3,0.3,0.4),
color (0.05, 0.05, 0.1),
color (0.8,0.79,0.77),
color (0.8,0.8,0.79)
);
*/
//color based = color(1,0,0);
Col_Out = FORCE;
}
The actual OSL Shader which I ported from RSL roughly (I stripped out some of the functions) is returning an interesting B&W turbulance kind of noise. This kind of thing is plug-able to the ColorRamp node to give color.
Or something like below...
Camouflage Suzanne. |
Back to 3D Texture Sine 3D....
shader sine3d(
color colorA = color(1,0,0),
color colorB = color(0,1,0),
float freq = 4,
float amp = 0.5,
float offset = 0,
point Vector = P,
float blur = 0.05,
output color Col_Out = color(0.3)
)
{
point p = transform("object", Vector);
float x = p[0];
float z = p[2];
// The arc tangent of the shading point will be in radians
// (-3.142 to 3.142) ie -180 to 180 degress.
float theta = atan2(x,z);
// Generate a sine wave - remapped to 0 to 1.
float wave = (sin(theta * freq) + 1) * 0.5;
// Adjust the height and position of the sine wave.
float waveHt = wave * amp + offset;
// Get a filtered value of the wave height.
waveHt = smoothstep(waveHt - blur, waveHt + blur, p[1]);
// Control the mixing of two colors by the waveHt.
//Oi = mix(color(1,1,1), color(0,0,0), waveHt);
//Ci = Oi * Cs * wave;
Col_Out = mix(colorA, colorB, waveHt);
}
You can always change the code and play around with the code, save every changes and make notes. Here for example, I am curious about the waveHt variable, I just replace it with the wave and I am getting something that looks like Pie OSL:
shader sine3d(
color colorA = color(1,0,0),
color colorB = color(0,1,0),
float freq = 4,
float amp = 0.5,
float offset = 0,
point Vector = P,
float blur = 0.05,
output color Col_Out = color(0.3)
)
{
point p = transform("object", Vector);
float x = p[0];
float z = p[2];
// The arc tangent of the shading point will be in radians
// (-3.142 to 3.142) ie -180 to 180 degress.
float theta = atan2(x,z);
// Generate a sine wave - remapped to 0 to 1.
float wave = (sin(theta * freq) + 1) * 0.5;
// Adjust the height and position of the sine wave.
float waveHt = wave * amp + offset;
// Get a filtered value of the wave height.
waveHt = smoothstep(waveHt - blur, waveHt + blur, p[1]);
// Control the mixing of two colors by the waveHt.
//Oi = mix(color(1,1,1), color(0,0,0), waveHt);
//Ci = Oi * Cs * wave;
Col_Out = mix(colorA, colorB, wave);
}
I am not a Math expert, so I take apart code by changing values of attributes.
Here is another RSL code "Simple Sine RSL" converted into OSL. In RSL, there is something called S and T which is related to Texture. I think in OSL it is U and V.
/*SOURCE
http://fundza.com/rman_shaders/pattern3d/index.html
*/
#include "stdosl.h"
shader simple_sine(
float freq = 5,
output color Col_Out = color(0.8),
point Vector = P
)
{
float x = Vector[0];
float sinval = sin(u * 2 * M_PI * freq); // instead of ST in RSL, we use UV in OSL
// Remap from -1 to +1 to 0 to 1
sinval = (sinval + 1) * 0.5;
//sinval = sinval;
//Oi = Os;
//Ci = Oi * Cs * sinval;
Col_Out = sinval;
}
RSL PORT: Brick Texture
// Based on RSL Brick Texture// from Book "Texture & Modeling Procedural A Procedural Approach"
// pages 39-40
# include "stdosl.h"
shader brick(
float BMWIDTH = 5,
float BMHEIGHT = 5,
float MORTARTHICKNESS = 0.1,
point Vector = P,
//float Ka = 1,
//float Kd = 1,
color Cbrick = color (0.5,0.15,0.14),
color Cmortar = color(0.5, 0.5, 0.5),
output color Col_Out = color(0.7)
)
{
color Ct;
point Nf;
float ss;
float tt;
float th;
float sbrick;
float tbrick;
float w;
float h;
//Nf = normalize(faceforward(N, I));
float x = Vector[0];
float y = Vector[1];
float MWF = MORTARTHICKNESS * 0.5/BMWIDTH;
float MHF = MORTARTHICKNESS * 0.5/BMHEIGHT;
ss = x / BMWIDTH;
tt = y / BMHEIGHT;
if (mod(tt * 0.5, 1) > 0.5)
{
ss += 0.5;
}
sbrick = floor(ss);
tbrick = floor(tt);
ss -= sbrick;
tt -= tbrick;
w = step(MWF,ss) - step(1-MWF,ss);
h = step(MHF, tt) - step(1-MHF, tt);
Ct = mix(Cmortar, Cbrick, w*h);
Col_Out = Ct;
}
OSL: Stripes + Noise Node + ColorRamp
Sometimes, we can just do something like below, instead of specifying Color in the code, we can just pass on the texture functions:
# include "stdosl.h"
shader multipleStripes (
float amplitude = 2.0,
float offset = 0.0,
float frequency = 1.0,
point Vector = P,
output color Col_Out = color(0.8)
)
{
float x = P[0];
float y = P[1];
float z = P[2];
float formula = sin(frequency*x + offset) * amplitude + cos(frequency*x + offset) * amplitude;
Col_Out = 1/abs(formula);
}
OSL DOODLE
This is one of my random exploration of OSL. I am still not sure about things, such as Math Function that PLOT (in 2D or 3D) and Math Function that actually works and create 3D Solid Texture that is volumetric. Just like how we can already do with Blender Internal Renderer to certain extent.
Something like this is nice:
It would be nice if someone could explain it to me until it is crystal clear and I will write additional note in here so that you can understand it.
Anyways, I just plug some Plotting function into the Point Position Float values:
#include "stdosl.h"
// SOURCE: http://trac.sagemath.org/ticket/1888
float ffx(float t)
{
return (2 + cos(2*t))*cos(3*t);
}
float ffy(float t)
{
return (2 + cos(2*t))*sin(3*t);
}
float ffz(float t)
{
return sin(4*t);
}
shader study_001 (
point Vector = P,
color ColorA = color(1,0,0),
color ColorB = color(0,1,0),
output color Color_Out = color(0.8)
)
{
float x = Vector[0];
float y = Vector[1];
float z = Vector[2];
float fx = ffx(x);
float fy = ffy(y);
float fz = ffz(z);
// parametric_plot3d( (fx, fy, fz), (t, 0, 6*pi), plot_points = 500, frame=False)
Color_Out = mix(fx, fy, fz);
}
NOISE FUNCTIONS IN OSL
Cell, Noise, Voronoi....We can either use Noise Functions directly inside OSL or utilize the already existing Blender Noise nodes such as Voronoi, etc.
noise()
WHAT IS THAT "INCLUDE" STUFF?
You probably notice one line: # include "stdosl.h" this line allow you to use some useful functions. You can read this file using normal text editor.TILING: XY Wizard
Tiling texture is an interesting area.
Read a great discussion about tiling here, download XY Wizard and study it.
http://blenderartists.org/forum/showthread.php?277284-Tiles-Texture-OSLFUNCTIONS
color()clamp()
sin()
mod()
floor()
abs()
CLOSURE
diffuse(N)FURTHER SELF STUDY
There is no best way to learn this OSL stuff on your own by reading examples. The answers are already there, you just need to see it.Start from here and see how Dingto recreating some of Blender's built in cycles node using OSL:
http://www.openshading.com/osl/example-shaders/
For example, node_brick_texture.osl from Open Shading website, is a recreation of Brick Texture node that is buit in with Cycles. You can have a look and compare that Brick Texture OSL code with simpler Brick OSL code that I ported from RSL.
Study some RSL shaders from website like FUNDZA. RSL and OSL are kind of similar.
If you are ambitious, you can check out Houdini's VEX code, play around with VEX node network. It is similar to Blender's Node Network, but inside it, you have component level nodes: sin(), abs(), and more. Translating and porting Houdini VEX maybe slightly complicated.
I have not looked into GLSL to OSL porting, but some tutorials out there is doing that.
You may notice all above what we have been writing is really just to end up as color() OUTPUT. We use mix(valueA, valueB, Fac) a lot. And what we have been modifying is the Fac value using Math.
That is the basic of OSL Texture writing.
RELATED LINKS
OSLhttp://blenderthings.blogspot.com/
http://www.blenderartists.org/forum/showthread.php?270332-OSL-Goodness
http://blog.selfshadow.com/
http://blenderartists.org/forum/showthread.php?274730-OSL-Porting-from-RSL
http://elbrujodelatribu.blogspot.fr/
http://learningblender3dsoftware.blogspot.com/2012/11/hello-osl.html
http://www.elysiun.com/forum/showthread.php?301168-A-color-stencil-node
RSL
http://hosukchang.com/
http://xrt.wikidot.com/
http://www.fundza.com/index.html
Post a Comment