Saturday, 11 May 2013

Working with the incidence vector in OSL

The fact that in OSL we have the incidence vector I available has proven its utility already in the many cool toon and carpaint shaders people have implemented. In this article we take this concept a bit further and create a shader that does not change it lighting model as a function of the angle between incidence vector and normal but changes its complete behavior depending on the angle between incidence vector an object orientation, allowing us the model a 3D-object (Venetian blinds) with just a 2D-tecture.

Lenticular printing

The idea of providing a different image based on the viewing angle is not new and is fact readily available in traditional media under the name Lenticular printing.

But in OSL we are able to act on any incidence vector, not just the one from the camera. This means for example that shadow rays act as they should and that we can make our material transparent in places to let it cast correct shadows.

In the sample image and in the video we used this trick to render venetian blinds as seen from different position with just s single plane.

The concept is simple: the horizontal slats of the blinds appear taller when seen from an angle so we calculate the angle between the incidence vector and the x-axis (because unfortunately there is no attribute object:rotation to determine the up axis of an object) and divide that by maximum angle that would let us see something through the blinds. That fraction is used to paint part of a horizontal strip representing a slat. Finally we replace the shading normal by one that is perpendicular to the slat allowing for s bit of a curve across the slat:

surface venetianblinds(
  point Pos = P,
  float Spacing = 0.1,
  float Width = 0.1,
  float Curve = 0.2,
  
  output float Fac = 0,
  output float Top = 0,
  output normal Normal = N
){
  Top = dot(I,vector(0,0,1))>0;
  
  float maxangle = atan(Spacing/Width);
  float angle = abs(M_PI_2-acos(dot(normalize(-I),vector(0,0,1))));

  float height = Spacing;
  // TODO we should look at the sign here to prevent 'flip'
  if (angle < maxangle){ height *= angle/maxangle; }

  float z = mod(Pos[2],Spacing);
  Fac = (z < height);
  
  if(z< height){
    Normal = normalize(vector(0,0,1)+N*Curve*(height/2-z)/height);
  }
  
}

Example node setup

The node setup used to render both the still and the video looks like this: This material is applied to a simple plane. The lighting consists of a single mesh light on the left.

Saturday, 4 May 2013

Rayleigh scattering in Cycles

Prompted by a question in a comment by Joakim Poromaa Helger I came up with this approach to emulate Rayleigh scattering (the effect that turns the sky blue and the sun red at the same time) in Cycles: by combining a transparent and a glass shader.


In the picture above there is a bright white sun on the right. In a material that exhibits Rayleigh scattering (like air) blue light is scattered more than red light. That means that light traveling from a lightsource through such material will be tinted red because blue light is scattered to all sides. In a direction away from the direct path of the light we see just this scattered blue light and not (or much less) the direct light.

Now if we combine a red(ish) transparent shader with a blue(ish) glass shader, the transparent shader will cause some of the light to be passed through the object without regard for index of refraction and give it a redish hue, while the glass shader will pass some (refracted) blue light through and reflect some. (In the sample picture I have turned off caustics because that takes forever to give a good quality render but also because in this setup caustics exhibit a blue color while it should be redish as well). The node setup looks like this:

With some extra trickery we can correct the color for any caustics as well:

(Note that this image took 10,000 samples and the caustics are still very noisy. Clearly caustics is not Cycles strong point. The light/shadow inside the orb are due to the fact I placed it slightly below the ground but am to lazy to fix it).

We achive the caustic coloring by arranging that the camera sees the blue glass shader while any object receiving diffuse light (like the floor) sees a red glass shader, a distinction we can make with help of the light path node:
Now obiously the effect here is exagerated for demonstration purposes and arguably it is not a real simulation of Rayleigh scattering but for artistic purposes this might be a good start.



Wednesday, 24 April 2013

An OSL starfield shader for Blender Cycles

Inspired by a BlenderHD video I implemented a simple starfield shader in OSL. The distribution of stars can be controlled by a density map and the color of the stars follows (very roughly) the probability of colors for visible stars. As I will show in an example, adding dust clouds and twinkling can be done with procedural textures.

Starfield example

The image below is a still from a short animation.

(It is pretty dark, for cinematic effects you probably would choose a far brighter setup)
The stars are generated with the starfield shader and their distribution is controlled by a noise texture. The faint green glow of stellar dust and the faint orange color on the horizon are generated with a separate noise and gradient texture respectively. (example node setup at the end)

The animation features additional twinkling that is not visible in the still image. This is effected by blending time animated 4d simplex noise.
(In the animation the contrast/lightness was autocorrected for a better view)

The code

The code below contains two functions: one that calculates a probability dependent voronoi distribution and the shader itself. The code is pretty straight forward but note that the Falloff value should not be set too high to prevent artifacts in the star shapes when seen in close-up. (These are due to the fact that we divide space in cells and the fact that we only considered the nearest neighboring cells for our voronoi distribution. Taking into account further neighbors would solve this but impact the performance of the shader in a rather drastic manner).

The outputs of the shader consist of a color and a factor. The color is the emmisive power in W/m2. This could be used as an input to an emmision closure but in fact it might be simpler just to normalize this and use it as a color directly (as is done in all examples). The factor is just a value which is 1.0 for the center of the stars aand decreases exponentially when further away.

point voronoi3dp(point p, float density, output float d2)
{
 int xx, yy, zz, xi, yi, zi;

 xi = (int)floor(p[0]);
 yi = (int)floor(p[1]);
 zi = (int)floor(p[2]);
 
 float dbest = 1e10;
 point pbest = 1e10;
 vector dz = vector(7,111,19);
 for (xx = xi - 1; xx <= xi + 1; xx++) {
  for (yy = yi - 1; yy <= yi + 1; yy++) {
   for (zz = zi - 1; zz <= zi + 1; zz++) {
    vector ip = vector(xx, yy, zz);
    if(cellnoise(ip)< density){
     point  vp = ip + cellnoise(ip+dz);
     vector dp = p-vp;
     float  d  = dot(dp,dp);
     if (d < dbest) {
      dbest = d;
      pbest = vp;
     }
    }
   }
  }
 }
 d2=dbest;
 return pbest;
}

shader stars(
 point Pos = P,
 float Stardensity = 0.001,
 float Scale = 1000.0,
 float Falloff = 10,
 
 output color col = 0.0,
 output float fac = 0.0
){
 point p = Pos * Scale;
 
 float d;
 voronoi3dp(p,Stardensity,d);
 fac = exp(-d*Falloff);
 if(d<100){
  col = blackbody(3500.0+6000.0*cellnoise(p+vector(17,17,17)));
 }
}

Example node setup

The node setup used to generate both the still image and the animation is shown below. It's fairly large but not really complicated.

(click to enlarge)
Each texture contribution is ordered in a horizontal row of nodes. From top to bottom: the starfield (dark blue, note the normalization of the color), the twinkling (light blue), the dust (green) and the horizon glow (yellow). The glare was added in the compositor along with some contrast enhancement (for the video sample I added extra contrast enhancement in an external program). An example .blend file bundled with the stars.osl script and the simplexnoise.osl script is available as a .zip file on GitHub. (click View Raw button to download)

Sunday, 14 April 2013

100k visits, hurrah!



This blog has reached the 100k visits mark just a couple of hours ago. Not bad for something that started just a few months ago :-) Apparently you all find it worthwhile reading material so I will definitely continue writing. (and to be honest, I feel flattered by all the nice comments I get, the Blender community is a really friendly bunch of people :-) So a big thank you to you and onward to the next milestone!

Friday, 12 April 2013

A new tree add-on part VI: speed improvements for complex trees

Trees that contain many branchpoints take a progressively longer time to generate as they become more complex. This is due to the time it takes to determine which branch segment is closest to any endpoint. The code up to version 0.0.7 did a linear scan through all the branch segments for each endpoint and while this was still fast enough for moderately sized trees (the time it takes doesn't explode exponentially because each iteration removes endpoints within kill distance of a branch) it took many seconds for a tree with thousand endpoints, short internode lengths and a short kill distance. In other words it got slow when the trees became interesting.

Version 0.0.8 (available on Github, click view raw to download) adds a significant speed up for larger trees (3-6 times depending on parameter settings). Quite complex tree skeletons can now be generated in a few seconds instead of half a minute or more (depending on the power of your pc of course. And remember, this is a pure python implementation so having multiple cores makes no difference, it's the raw speed of a single core that counts). Please note that because endpoints within kill distance are removed in a different order, trees generated in version 0.0.8 are not identical to trees generated in previous versions but their general appearance stays the same.

For the technically inclined: this speed up is realized by storing all branch segments in a kd-tree, so looking for a nearest neighbor now scales as the log of the number of branch segments instead of taking linear time. This is a very noticeable difference once your tree grows to hundreds or even thousands of branch segments. The kd-tree is a pure python implementation in its own, separate module kdtree.py that might be interesting for other applications as well.

Saturday, 30 March 2013

A new tree add-on part V: speeding up skinning of the tree skeleton

one of the issues that bothered me about my new tree add-on was that applying the skin modifier to flesh out the branch skeleton was incredibly slow, especially for larger trees. I therefore decided i'd try writing my own skinning routine and the results are quite promising: a factor 10 or more for moderately sized trees.


The new version (0.0.7) is as usual available on GitHub. How to use and install this add-on is documented in previous articles: I II III IV.
The new skinning method is the default if you uncheck the no modifiers checkbox but if you like you can still select Blenders skin modifier from the drop down as shown on the right. (This can also be done later because we leave the skeleton itself intact; as it only consists of edges it won't show up in renders but you could use it to add any modifier later on).

Timings

The table below lists some timings for my computer, all values were left to their defaults except for the number of endpoints:
Timings in seconds for generating a tree skeleton
Endpoints No modifier Own skinning Blenders skinmod
100 0.02 0.04 0.46
200 0.04 0.07 0.74
400 0.08 0.16 1.51
400 0.56 0.87 14.43

The last line shows the results for a fair sized, bushy tree (400 endpoints, but with the internode length and kill distances reduced to 0.5 and 2.5 respectively and with 3000 extra endpoints per 1000 iterations). The difference between less than a second and more than 14 seconds is the difference between comfortable modelling and frustration, and there is still some room for improvement because the code is still riddled with print statements.
There are three factors that help to speed up the skinning operation compared to blenders built-in skin modifier:
  • unlike the modifier we do not have to make a copy of the mesh,
  • we also generate a lot less geometry it seems and
  • we don't have to deal with special cases: the skin modifier can handle any geometry while our skinning just has to deal with straight edges or simple forks.

Results

When we apply a subsurface modifier to our skinned branches the result is already quite acceptable although I see room for improvement, specifically in the shape of the forks. As you can see there is some bulging were the branches join and this is especially noticeable when there is either a very sharp anngle between the branches or when both branches bend out of plane. Fortunately the highlighted 5-gon lends itself to all kinds of angle dependant adaptations so I expect I can tweak this further to get smoother results.
The focus for the next minor release will be on bug fixing and code cleanup to prepare for a full 1.0.0 release. This will be accompanied by better (hopefully: -) docs as well.

Monday, 11 March 2013

A new tree add-on part IV: adding objects and fixing bugs

version 0.0.6 provides new functionality to add additional objects to the tree besides leaves. This might be helpful in creating trees with blooms or fruit.

adding arbitrary objects

If you click on the add object button you can not only select an object to duplicate but you can also set a number of additional parameters that control the distribution of this additional object analogous to the parameters that control the distribution of the leaves. [Sorry, no sample image yet. The code is, as always, available on GitHub ]
Check for earlier articles in this series the archive section in the sidebar on the right.
Besides this extra functionality version 0.0.6 fixes two bugs: selecting a saved preset might give an error so now we exclude the various group properties from saved presets (after all a named group might not exist at all in another scene) and in certain situations many identical internodes might be produced. This is not only difficult to skin but results in bunches of leaves as well. This might be a flaw in the original algorithm that we solve by checking whether a new internode doesn't occupy the same position as any existing one.
That last one is tricky and happens when a newly calculated branchpoint is not within killing distance of the endpoints originally directing it and further away from those endpoints than the original branchpoint causing the same branchpoint to be generated again each iteration. I've illustrated this situation in the diagram below:

(original branchpoint in red, new branchpoint in purple, endpoints in green. Here the original distances (purple lines) are larger than the kill distance and the new distances (blue lines) even more so.) I wonder if there is a better way to prevent this than just increasing the kill distance (which would result in a very sparse tree) or checking a new branchpoint against all previous ones like we do now (which is quite expensive and rather inelegant).