Wednesday, October 19, 2011

Scaling Normal Maps in Slim

v1 is the normal map input.
v2 is our multiplier

1. get individual channels first
float red = comp(v1,0);

2. since normal maps have R and G values ranged between 0.5 and 1 lets subtract 0.5 from it so that it ranges from 0 to 0.5
float red1 = red - 0.5

3. multiply red1 with v2 to scale our red and green values and then add 0.5 to have our range back at 0.5 to 1

result = (red1*v2)+0.5;

float red = comp(v1,0)-0.5;
float green = comp(v1,1)-0.5;
float red1 = (red*v2)+0.5;
float green1= (green*v2)+0.5;
result = color(red1,green1,1);

Tuesday, April 26, 2011

Architectural Visualization: Concrete - Part I - Analysis

Intro
Concrete comes in many forms, and I thought it might be time I finally make a detailed study into the different types of concrete.

Monday, April 25, 2011

Architectural Visualization: Planks

Intro
Since I'll be working on a architectural visualization project, I will document the techniques I found interesting. The first notable one so far are the planks near a pool area.

For the planks, I found an 1kx1k tileable image. With some scripting and hypershade trickery, it is more than enough, even at closeups.

Goal
1. add transform noise to the planks
2. offset UV of individual planks
3. add different values to the planks

Sunday, April 17, 2011

Writing mental ray shaders: UV Chooser

Intro
Here is a shader that loads a texture, and chooses which UV set to use. There's an equivalent node in Maya, but it requires setting the input values in hypershade, which isn't intuitive for the avg user. In 3ds max the same thing is set with UV channels. Multi texture layering is an important technique when dealing repetitive textures, by using 5 1k textures, i can create a better looking texture than a single 5k texture. Not sure why maya is making it so hard for avg users.

MI Source
declare shader
color "uv_chooser" (
color texture "tex",
integer "uv_sets"
)
apply material
end declare


C Source
#include "shader.h"

struct uv_chooser{
miTag tex;
miInteger uv_sets;
};

miBoolean uv_chooser(miColor *result, miState *state, struct uv_chooser *params) {
miTag tex = *mi_eval_tag(&params->tex);
miInteger uv_sets = *mi_eval_integer(&params->uv_sets);
int i = uv_sets;
mi_lookup_color_texture(result, state, tex, &state->tex_list[i]);
return miTRUE;
}



Friday, April 15, 2011

Writing mental ray shaders: Mosaic Tiles

Introduction
Here is a little something thats detours from the book. Combining quantization and texture uv, I can create a mosaic effect.

Methodology
1. Have an input texture
2. quantize the uv_coordinates
3. use the quantized_uv_coordinates inside mi_lookup_texture_color

 From top to bottom: Original, tile = 20, tile = 10, Porn?!

Writing mental ray shaders: Texture Mapping

Introduction
Once we have UV coordinates, we can start mapping textures according to these coordinates. This section will introduce the use of miTAG, and the function mi_lookup_color_texture(). I'll create a node thats similar in function to the 2d_placement node in Maya. Key functions are, UV offsets, and  UV scaling.

Notes
Name
Arguments
mi_lookup_color_texture
*col, *state, tag, *v
Return the value in a color texture at a given coordinate.

The tag is assumed to be a texture as taken from a color texture parameter of a shader. This function checks whether the tag refers to a shader (procedural texture) or an image (file texture or byte stream), depending on which type of color texture statement was used in the .mi file. If tag is a shader, coord is stored in state→tex, the referenced texture shader is called, and its return value is returned. If tag is an image, coord is brought into the range (0…1, 0…1) by removing the integer part, the image is looked up at the resulting 2D coordinate, and miTRUE is returned. If the texture has been marked for filtering, like with the filter keyword in the .mi file, then multi-level pyramid filtering is performed, a procedure derived from classical mip-map textures. In both cases, the color resulting from the lookup is stored in *color.

Thursday, April 14, 2011

Writing mental ray shaders: Quantization Part I

Introduction
At this point in the book, Writing mental ray Shaders, it starts with the UV shader, which uses the quantization function. I think the function deserves its own little write up, so we'll divert a little bit and play with the color quantization function.

Quantization
From Wikipedia
Quantization, in mathematics and digital signal processing, is the process of mapping input values that are members of some relatively large set of admissible input values to output values that are members of a smaller countable set of output values. The set of possible input values may be infinitely large, and may possibly be continuous and therefore uncountable (such as the set of all real numbers, or all real numbers within some limited range). The set of possible output values may be finite or countably infinite. A device or algorithmic function that performs quantization is called a quantizer.

The most common type of quantization is known as scalar quantization. Scalar quantization, typically denoted as y = Q(x), is the process of using a quantization function Q( ) to map a scalar (one-dimensional) input value x to a scalar output value y. Scalar quantization can be as simple and intuitive as rounding high-precision numbers to the nearest integer, or to the nearest multiple of some other unit of precision.

Rounding
From Wikipedia
Rounding a number x to a multiple of some specified increment m entails the following steps:
1. Divide x by m, let the result be y;
2. Round y to an integer value, call it q;
3. Multiply q by m to obtain the rounded value z.
$z = \mathrm{round}(x, m) = \mathrm{round}(x / m) \cdot m\,$
Regardless, it is recommended to read through the wikipedia entry for details on rounding numbers.

Type Conversion
From Wikipedia
As we can see, we can use explicit type conversion as a rounding function. Getting all these definitions out of the way, we can start working on our UV as color with quantization shader.

Writing mental ray shaders: Quantization Part II

Intro
Here I will combine my two previous posts into one shader, the uv_as_colors_banding shader. I'll implement the rounding function as demonstrated in the wikipedia article. I did not understand some of the source code from the book

Methodology
1. Implement uv as colors
2. Define u_count, which is the amount of banding in the u direction
3. Define v_count, which is the amount of banding in the v direction
4. Implement the quantize function.

Wednesday, April 13, 2011

Writing mental ray shaders: UV as Colors

Introduction
Here, we'll start working with UV's. We'll first look at the state variable, tex_list. I will first use tex_list in a shader that translates tex_list into the red and green component of result. In the next section I will combine the quantization function into the uv as colors shader. As I'm no expert in programming, my codes will be a little simpler, but hopefully easier to understand.

Definition
state->tex_list is a pointer to an array containing the texture coordinates of the intersection point in all texture spaces.

Tuesday, April 12, 2011

Introduction
By definition, transparency is the physical property of allowing light to pass through a material. So, before I start, we need to examine this function.

miBoolean mi_trace_transparent(
miColor  *result,
miState  *state)

From Mental Ray online manual
This function casts a ray from state→dir to direction. It returns miFALSE if the trace depth has been exhausted or if the hit object has disabled refraction receiving. If no intersection is found, the optional environment shader is called. It also works when ray tracing is turned off, and considers visible as well as trace objects.

The API library function mi_trace_transparent sends a ray in the same direction and stores the resulting color in result. Note that this is a potentially recursive shader call—if the ray strikes another instance with a material that contains this transparency shader, then mi_trace_transparent will be called in it, and so on.
 Final Image
For instance, lets examine the final test image I made. We're sending out an eye ray it hits the blue plane at point P. It calls the trace function, the function sends out a ray in the same direction as the eye ray, and hits the green plane, it calls the trace function again, and hits the red plane . It will call the function until trace depth is exhausted. It should be noted, setting trace depth in Maya is not enough, you need to set refraction depth as well.

Monday, April 11, 2011

Writing mental ray shaders: Set Range Utility

Goal
Make and/or improve on the set range utility found in maya.

Methodology
1. Given an oldmin and oldmax, find the range by oldmax - oldmin.
2. Given an newmin and newmin, find the range by newmax - newmin.
3. For a given point P, find the range of P by P - oldmin.
4. Find the current_factor by (Step3/Step1)
5. Find the new_factor by (Step4 * Step2) + newmin

Writing mental ray shaders: Z Depth Part II

Introduction
In this part I'll create the set range and blend functions that can be found in maya. These functions will be a set of auxilliary functions that I can call upon, much like how the set range and blend nodes work in Maya. Again, I am working from, Writing mental ray® Shaders: A Perceptual Introduction (mental ray® Handbooks). Buy the book, its worth it :)

I'll further refine the zdepth shader, incorporating the set range and blend functions.

Goal
1. Develop a Set Range function and use as a library function
2. Develop a Blender function and use as a library function
3. Apply the above functions into the zdepth shader

Friday, April 8, 2011

Writing mental ray shaders: Z Depth Part I

Introduction
Unlike the shader demonstrated in Chapter 7 of Writing Mental Shaders, this is a camera z depth shader. The shader in the book is a world z depth shader.

Goal
A shader that displays a grey scale from near to far from the rendering camera.

Methodology
1. For a point P in space, from the rendering camera, find the -Z position of point P.
2.  Define a near distance and a far distance, use the rendering camera near/far clipping planes to determine, or alternately use the measure distance tool.
3. Use a set range function to remap the distance of (near to far) to 0 and 1, and assign this to a variable factor.
4. Use factor as greyscale values of a surface shader, or use factor as an blend value of a blend function(as above) to blend two colors.

Thursday, April 7, 2011

Writing mental ray shaders: Normals as Colors Part II

Goal
A shader that visualizes the world space, object space, and camera space normals.

Methodology
Same as in part I, but use a switch statement with different mi_vector_**** functions.

Writing mental ray shaders: Normals as Colors Part I

Goal
A shader that visualizes the surface normal.

Methodology
1. Find the surface normal at point p
2. Assign xyz values to rgb at point p

Wednesday, April 6, 2011

To clarify, I am working from Andy Kopras' book

Writing mental ray® Shaders: A Perceptual Introduction (mental ray® Handbooks)

His website is,

I'm blogging the excercises as a means to remember, and to have something quick and easy to refer to. It is also for the day when I have to teach these materials. I am using linux and maya2011 64 to test the shaders.

Friday, April 1, 2011

Writing mental ray shaders: Facing Forward

Goal
A shader that provides the visual representation of facing forward, where the surface normal is 90 degrees to the camera is black, and 0 degrees to the camera is white.

Methodology
1. create a color parameter called tint
2. create a scalar variable called scale
3. assign scale the dot value product of the surface normal and camera normal
4. assign result the value of tint multiplied by scale

Mi File
declare shader
color "facing_ratio" (
color "tint"  default 1 1 1
)
apply material
end declare


C File
#include "shader.h"

struct facing_ratio {
miColor tint;
};

DLLEXPORT
miBoolean facing_ratio (
miColor *result, miState *state, struct facing_ratio *params ) {
miColor *tint = mi_eval_color(&params->tint);
miScalar scale = -state->dot_nd;
result->r = tint->r * scale;
result->g = tint->g * scale;
result->b = tint->b * scale;
result->a = 1.0;
return miTRUE;
}

Questions

On line 10 of the C source code,

Q. Why can't I directly assign the color of mi_eval_color() to tint?
A. mi_eval_color() or mi_eval in general, does not return color. It returns a pointer.

Q. What is it doing?
A.  This is dereferencing, it is storing the location of mi_eval_color() at the location of tint. And hence the final value

Q. When did tint become a pointer? Or can I convert any type to a pointer type with a "*"?
A. Any variable has an address associated with it. The * refers to the address.

Notes
1. dot_nd is a state variable that is a dot product of the surface normal and camera vector. It is found in, in the MentalRay manual - Using and Writing Shaders - State Variables - Intersection
2. mi_eval returns a pointer to the parameter value, no matter where it comes from. If the shader accessed its parameters directly, without using mi_eval, it would get garbage (such as 0 or NaN) if the parameter is assigned. More detail in the MentalRay manual - Using and Writing Shaders -  Parameter Assignment and mi_eval.
3. A refresher for pointers in C. "Practical Programming in C"

Thursday, March 31, 2011

Writing mental ray shaders: Color Balance

Goal

Methodology
1. create a color parameter called base
2. create a color parameter called gain
3. create a color parameter called offset
4. create a shader called "color_gain"
5. create a shader called "color_offset"
6. create a function where result = base * gain
7. create a function where result = result + offset

Mi File
declare shader
color "color_balance" (
color "base"  default 1 1 1,
color "gain" default 1 1 1,
color "offset" default 0 0 0,
)
end declare


C File

#include "shader.h"

DLLEXPORT

struct color_balance {
miColor base;
miColor gain;
miColor offset;
};

DLLEXPORT

miBoolean color_balance (miColor *result, miState *state, struct color_balance *params) {
miColor *base = mi_eval_color(&params->base);
miColor *gain = mi_eval_color(&params->gain);
miColor *offset = mi_eval_color(&params->offset);
result->r = base->r * gain->r;
result->g = base->g * gain->g;
result->b = base->b * gain->b;
result->r += offset->r;
result->g += offset->g;
result->b += offset->b;
return miTRUE;
}


Writing mental ray shaders: Color Offset

Goal
A simple shader that takes a base color and adds another color.

Methodology
1. create a color parameter called base
2. create a color parameter called offset
3. create a shader called "color_offset"
4. create a function where result = base + offset

Mi File
declare shader
color "color_offset" (
color "base"  default 1 1 1,
color "offset" default 0 0 0
)
apply material
end declare


C File

#include "shader.h"

DLLEXPORT

struct color_offset {
miColor base;
miColor offset;
};

DLLEXPORT

miBoolean color_offset (miColor *result, miState *state, struct color_offset *params) {
miColor *base = mi_eval_color(&params->base);
miColor *offset = mi_eval_color(&params->offset);
result->r = base->r + offset->r;
result->g = base->g + offset->g;
result->b = base->b + offset->b;
return miTRUE;
}


Writing mental ray shaders: Color Gain

Goal
A simple shader that takes a base color and multiplies by another color.

Methodology
1. create a color parameter called base
2. create a color parameter called gain
3. create a shader called "color_gain"
4. create a function where result = base * gain

Mi File
declare shader
color "color_gain" (
color "base" default 1 1 1,
color "gain" default 1 1 1
)
version 1
apply material
end declare


C File

#include "shader.h"

DLLEXPORT

struct color_gain {
miColor base;
miColor gain;
};

DLLEXPORT

miBoolean color_gain (miColor *result, miState *state, struct color_gain *params) {
miColor *base = mi_eval_color(&params->base);
miColor *gain = mi_eval_color(&params->gain);
result->r = base->r * gain->r;
result->g = base->g * gain->g;
result->b = base->b * gain->b;
return miTRUE;
}


Friday, March 25, 2011

Intro
__________________________________________________________

The end user of a phenomenon shader are usually lighters in a pipeline environment so as a shader artist/developer, it would be helpful to think in terms of how a lighter will use this shader. The phenomenon does not replace the shader graph. However, it does simplify the user interface of the shader graph into something that is easier for the lighter. This is also currently the pipeline direction Renderman Studio 3 is going. Our previous RMS pipelines exposes the full shader tree to the lighter, which is 1. unwieldy to the lighter 2. unwieldy to the hardware. But unlike renderman, there are no performance gains in mentalray.

I will be making two mental ray phenomenon shaders, first will be the water droplet shader, and the second will be the rim shader, which will be exercise. I can use these phenomenons later on in my fruit shader series. Other resources for making phenomenon shaders can be found here,

Key Points
__________________________________________________________

• Splines, color ramps, maya layered shader/texter and any other nodes with arrays will NOT work in phenomenon
• Use MentalRay equivalent nodes when possible
• Phenomenizer does not work with maya nodes

Determining User Settings
__________________________________________________________

First, a review my shader graph.

 Overview
 Detailed Droplet Tree
Here are the settings I have deemed important enough for customizability.

small droplet
• cell size
• density
• spottiness
large droplet
• cell size
• density
• time
• frequency
• v1 position
• v2 position
bump
• bump depth
mia material
• diffuse weight
• reflection color

__________________________________________________________

Looking at my shader graph, notice there are several remapHSV nodes in there, these are going to be difficult to implement in a phenomenon. As getting array inputs(the spline graph) has not been proven to work(yet?). However, I will just work around it for now, substituting remapHSV with a contrast node.

 Replace Remap with Contrast
Here, I just tweak the contrast node to mimic what the remaphsv node is doing. The other remaphsv nodes define the crossectional shape of the droplet by customizing the gradient falloff of the noise map.

 Reworking and Testing the droplet
By removing these remaphsv nodes, the bump would look slightly off, but only noticeable in extreme closeups. I retweaked threshhold level of the leather noise node to match the values of my original. Hence, I will add the threshhold controls to my droplet UI. A quick way to test is just use surface shaders or for mental ray nodes, a mib_lambert. I also removed the layered texture node and replaced it with a mib_color_mix. As seen above, a quick comparison of each node and the final texture output.

 Cleaned up

Writing the Phenomenon
__________________________________________________________

I will begin implementing the above network into a phenomenon. The first thing to do,

declare phenomenon
color "droplet_phen" (
)
version 1
apply material
end declare


This is the basic phenomenon declaration. Next, declare the root node, which should be the end result node.

shader "dropletSurface" "maya_surfaceshader" (
"outColor" = "mia_material1.outValue",
"outTransparency" 0. 0. 0.,
"outMatteOpacity" 1. 1. 1.,
"outGlowColor" 0. 0. 0.
)

"cutAwayOpacity" 0.,
"alphaMode" 0
)



The root node is named shadingEngine, which is a maya node with the name "maya_shadingEngine". Why am I using maya_shadingEngine? It's explained here, which states,

"All maya built in mentalray shaders does not use a simple “color” as an output but a “struct”, a user defined output structure. Because root expects a color, it does not know how to evaluate this thing and this causes this error:...

So here is the basic structure of a phenomenon shader, each node is defined as

followed by its variable properties. To find out the exact name and variables of a node, check inside the mentalray/include directory of the maya installation and find the following files,
• mayabase.mi - maya nodes
• base.mi  - mentalray nodes
• architecture.mi - mia
• subsurface.mi - misss
Unlike the aforementioned mi files, the type of each property does not need to be declared, we are only assigning values to the property.

Before I start importing all the nodes into my phenomenon file, I declare the the settings that allows the user to access the shader.

color "droplet_phen" (
scalar "diffuse_weight", #:default 0.2
color "reflection_color", #:default 0.9 0.9 0.9
scalar "sm_droplet_cell_size", #:default 0.125 shortname "smdr_size"
scalar "sm_droplet_density", #:default 0.3 shortname "smdr_density"
scalar "sm_droplet_spottyness", #:default 0 shortname "smdr_spot"
scalar "lg_droplet_cell_size", #:default 0.6 shortname "lgdr_size"
scalar "lg_droplet_density", #:default 0.1 shortname "lgdr_density"
vector "mask_bias",  #:default 0.6 0.6 0.6
scalar "bump_depth",  #:default 0.5
transform "sm_droplet_placement",
transform "lg_droplet_placement",
)

The three transform variables does not have a default value as it will differ from scene to scene. I left it open so I can manually connect a place3dtexture node into the transform slot. I will then link the interface to the three 3d textures I will be using later on. One can specify default values of a transform variable, its a 4x4 matrix, separated by commas(IIRC).

From the tip to the root,  import the nodes into the phenomenon file.

shader "small_droplets" "maya_leather" (
"cellColor" 1. 1. 1.,
"creaseColor" 0. 0. 0.,
"cellSize" = interface "sm_droplet_cell_size",
"density" = interface "sm_droplet_density",
"spottyness" = interface "sm_droplet_spottyness",
"randomness" 1,
"threshold" 0.6,
"creases" 0,
"filter" 0,
"filterSize" 0. 0. 0.,
"filterOffset" 0,
#  "blend",
"wrap" 1,
"invert" 0,
"alphaIsLuminance" 0,
"colorGain" 1. 1. 1.,
"colorOffset" 0. 0. 0.,
"alphaGain" 1,
"alphaOffset" 0,
"defaultColor" 0.5 0.5 0.5,
"placementMatrix" = interface "sm_droplet_placement",
"local" 0
)

There are basically four things I'm doing from here on out,
1. create a new node from the corresponding node in the shader tree
2. copy settings from the original shader tree into the phenomenon
3. assign variables to the interface
4. assign variables to the output of another node
Take a look at the following,

shader "mask_contrast" "maya_contrast" (
"contrast" 100. 100. 100.,
)

1. I created a contrast node, and named it "mask_contrast"
2. I set the contrast to a 100 in  every channel(I just need one actually)
3. I assigned the bias to an interface item
4. I assigned the input of contrast to be the noise map
Every node is a repeat of the above process. Once every node in the original shader tree is represented in the phenomenon file, load and test the shaders. It is very likely to encounter syntax errors, as the input and output names can be quite confusing. Testing the mi file node by node is a good way to debug the phenomenon. Testing and building the phenomenon is similar to how a shader is built and tested in hypershade. Below is the full phenomenon file.

 Droplet UI

Final Droplet Phenomenon
__________________________________________________________
declare phenomenon
color "droplet_phen" (
scalar "diffuse_weight", #:default 0.2
color "reflection_color", #:default 0.9 0.9 0.9
scalar "sm_droplet_cell_size", #:default 0.125 shortname "smdr_size"
scalar "sm_droplet_density", #:default 0.3 shortname "smdr_density"
scalar "sm_droplet_spottyness", #:default 0 shortname "smdr_spot"
scalar "lg_droplet_cell_size", #:default 0.6 shortname "lgdr_size"
scalar "lg_droplet_density", #:default 0.1 shortname "lgdr_density"
vector "mask_bias",  #:default 0.6 0.6 0.6
scalar "bump_depth",  #:default 0.5
transform "sm_droplet_placement",
transform "lg_droplet_placement",
)

"cellColor" 1. 1. 1.,
"creaseColor" 0. 0. 0.,
"cellSize" = interface "sm_droplet_cell_size",
"density" = interface "sm_droplet_density",
"spottyness" = interface "sm_droplet_spottyness",
"randomness" 1,
"threshold" 0.6,
"creases" 0,
"filter" 0,
"filterSize" 0. 0. 0.,
"filterOffset" 0,
#  "blend",
"wrap" 1,
"invert" 0,
"alphaIsLuminance" 0,
"colorGain" 1. 1. 1.,
"colorOffset" 0. 0. 0.,
"alphaGain" 1,
"alphaOffset" 0,
"defaultColor" 0.5 0.5 0.5,
"placementMatrix" = interface "sm_droplet_placement",
"local" 0
)

"cellColor" 1. 1. 1.,
"creaseColor" 0. 0. 0.,
"cellSize" = interface "lg_droplet_cell_size",
"density" = interface "lg_droplet_density",
"spottyness" 0.1,
"randomness" 1,
"threshold"0.80,
"creases" 0,
"filter" 0,
"filterSize" 0. 0. 0.,
"filterOffset" 0,
#  "blend",
"wrap" 1,
"invert" 0,
"alphaIsLuminance" 0,
"colorGain" 1. 1. 1.,
"colorOffset" 0. 0. 0.,
"alphaGain" 1,
"alphaOffset" 0,
"defaultColor" 0.5 0.5 0.5,
"placementMatrix" = interface "lg_droplet_placement",
"local" 0
)

"amplitude" 1,
"ratio" 0.809,
"threshold" 0,
"scale" 1. 1. 1.,
"origin" 0. 0. 0.,
"depthMax" 1,
"frequencyRatio" 7.252,
"inflection" 0,
"noiseType" 3,
#  "density",
#  "spottyness",
#  "sizeRand",
#  "randomness",
#  "falloff",
#  "numWaves",
#  "implode",
#  "implodeCenter",
"filter" 0,
"filterSize" 0. 0. 0.,
"filterOffset" 0,
#  "blend",
"wrap" 1,
"invert" 0,
"alphaIsLuminance" 0,
"colorGain" 1. 1. 1.,
"colorOffset" 0. 0. 0.,
"alphaGain" 1,
"alphaOffset" 0,
"defaultColor" 0.5 0.5 0.5,
"local" 0
)

"contrast" 100. 100. 100.,
)

"num" 2,
"mode_0" 2,
"mode_1" 0,
"mode_2" 0,
"mode_3" 0,
"mode_4" 0,
"mode_5" 0,
"mode_6" 0,
"mode_7" 0,
"weight_1" 1,
"weight_2" 1,
"weight_3" 1,
"weight_4" 1,
"weight_5" 1,
"weight_6" 1,
"weight_7" 1,
"color_0" = "small_droplets.outColor",
"color_1" = "large_droplets.outColor",
"color_2" 0. 0. 0.,
"color_3" 0. 0. 0.,
"color_4" 0. 0. 0.,
"color_5" 0. 0. 0.,
"color_6" 0. 0. 0.,
"color_7" 0. 0. 0.,
"color_base" 0. 0. 0.
)

"normalCamera" 0. 0. 1.,
"bumpValue" = "mib_color_mix1.outColorR",
"bumpDepth" = interface "bump_depth",
"bumpFilter" 1.,
"bumpFilterOffset" 0.,
"tangentUCamera" 1. 0. 0.,
"tangentVCamera" 0. 1. 0.
)

"normal" = "bump3d1.outNormal",
"space" 1,
)

"diffuse_weight" = interface "diffuse_weight",
"diffuse" = "mib_color_mix1.outColor",
"diffuse_roughness" 0.,
"reflectivity" = "mib_color_mix1.outColorR",
"refl_color" = interface "reflection_color",
"refl_gloss" 1.,
"refl_gloss_samples" 8,
"refl_interpolate" off,
"refl_hl_only" off,
"refl_is_metal" off,
"transparency" 0.,
"refr_color" = "mib_color_mix1.outColor",
"refr_gloss" 1.,
"refr_ior" 1.52,
"refr_gloss_samples" 8,
"refr_interpolate" off,
"refr_translucency" off,
"refr_trans_color" 0.7 0.6 0.5 1.,
"refr_trans_weight" 0.5,
"anisotropy" 1.,
"anisotropy_rotation" 0.,
"anisotropy_channel" -1,
"brdf_fresnel" off,
"brdf_0_degree_refl" 0.2,
"brdf_90_degree_refl" 1.,
"brdf_curve" 5.,
"brdf_conserve_energy" on,
"intr_grid_density" 2,
"intr_refl_samples" 2,
"intr_refl_ddist_on" off,
"intr_refl_ddist" 0.,
"intr_refr_samples" 2,
"single_env_sample" off,
"refl_falloff_on" off,
"refl_falloff_dist" 0.,
"refl_falloff_color_on" off,
"refl_falloff_color" 0. 0. 0. 1.,
"refl_depth" 5,
"refl_cutoff" 0.01,
"refr_falloff_on" off,
"refr_falloff_dist" 0.,
"refr_falloff_color_on" off,
"refr_falloff_color" 0. 0. 0. 1.,
"refr_depth" 5,
"refr_cutoff" 0.01,
"indirect_multiplier" 1.,
"fg_quality" 1.,
"fg_quality_w" 1.,
"ao_on" off,
"ao_samples" 16,
"ao_distance" 10.,
"ao_dark" 0.2 0.2 0.2 1.,
"ao_ambient" 0. 0. 0. 1.,
"ao_do_details" on,
"thin_walled" off,
"no_visible_area_hl" on,
"skip_inside_refl" on,
"do_refractive_caustics" off,
"backface_cull" off,
"propagate_alpha" off,
"hl_vs_refl_balance" 1.,
"cutout_opacity" 1.,
"no_diffuse_bump" off,
"bump" = "misss_set_normal1.normal",
"mode" 4,
"lights" []
)

"outColor" = "mia_material1.outValue",
"outTransparency" 0. 0. 0.,
"outMatteOpacity" 1. 1. 1.,
"outGlowColor" 0. 0. 0.
)

"cutAwayOpacity" 0.,
"alphaMode" 0
)

version 1
apply texture, material

end declare


Tuesday, March 22, 2011

Intro
__________________________________________________________

I'll be making a procedural orange shader, that looks good at closeups. So this is an exercise in procedural noise layering. The main take away here,
• identifying noise layers/general analysis
• chaining bump maps
I always start out with references, so here are some,

 source

This pretty much has all the properties an orange in detail. I want an extremely red orange, so I will push it even further than the above image.

I can quickly outline some bump noise patterns I can see here,
• high frequency, ~0.4mm to 0.8mm, these are the cellular bumps and color at extreme close up
• medium frequency, there are some dimples ~0.5mm to 1mm, but their profile is such that its more visible
• low frequency, large scale knobbiness of the orange skin, some browning skin patterns
Some notes on the color, sss, and reflection
• high frequency leathery skin thats even more pronounced in SSS
• high glossy specularity with color at the end of the falloff
• to determine the average SSS, look at the light side to dark side fall off. Even
Bump
__________________________________________________________

From my noise pattern outline, i quickly created 3 3d textures,

 Bump Sub Tree
 Low, Med, High Freq Map Settings
I used leather 3d texture maps for the high and med freqeuncy maps and a solid fractal 3d map for the low frequency map.

 High Frequency Pattern
 Med. Freq. Pattern 1

 Med. Freq. value correct 1
 Med. Freq. Pattern 2

 Med. Freq. value correct 2

 Low Frequency Pattern

 Final bump output
I tweaked each individual bump pattern separately, determining that the high frequency patterns are small bump, while the medium frequency pattern are sharp holes, and the low frequency patterns are bumps as well. The geometry provided have built in low frequency noise as well. I chained each bumps' outnormal to the next bumps' normal camera to achieve the final bump output.

Diffuse & SSS
__________________________________________________________

 diffuse, spec, bump

I''ll work on the diffuse and sss at the same time, but first, I duplicated  the orange and scaled it up a little.
The reason being, I need to take a look at the SSS falloff, and for that I need to see how far the SSS penetrates into the shadow side of the orange. Don't worry about how the bump looks so strange, after adding SSS the high contrast washes out.

For the above render, the epidermal radius is set at 2.0, which washes out any shadows, so i'll set it down to 0.2.

This gives the shadow a softer edge and the light penetration is consistent with my reference.

The subdermal scatter looks like what I want to achieve, albeit not as intense as I am showing it here. If you shine a flash light, hold it close to the orange, you will discover that at high intensities, the light travels quite far inside the meat, and the skin has these little holes that light penetrates in and out of. Or the holes might not be holes but variations in the thickness of the orange peel.

Now, for the diffuse color, using a ramp with its built in HSV noise is good enough if the UV seams are hidden well enough. However, the models provided, and a lot of the times, UVs are an afterthought. As a shader artists, 3d procedural textures, custom coded or procedural texture trees(hypershade, slim, mental mill) are preferred.

 Final Render

Saturday, March 19, 2011

Shader TD and the look development process explained

I found this gem in the CGtalk forums, couldn't have explained it better.

"Well, when you have multiple lighters working on the same sequence, you ideally don't want them to have to tweak the shaders to get the look that they are after. Not only does this take more time (and sometimes break the pipeline if all the shader parameters aren't promoted up to a settable/animatable level), but it also makes it difficult to keep the look consistent among several artists.

Generally a reference light setup will be made for the show, or for each sequence in a film. The texture artists and look dev artists will make sure the objects look good under those lighting conditions. Then if needed, several variations of the object and/or its shader parameters will be published out for use by the lighting artist. Likewise, a lead lighting artist/TD will generally set up the overall lighting scheme for the set or sequence, and publish that out as well. Then it is up to the individual lighting artists to take the set lighting preset and the surfacing preset, and tweak the lights as needed on a per-shot basis.

Lighters shouldn't be tweaking textures, just like they shouldn't be tweaking geometry. If any texture or shader or geometry problems show up, they should be kicked back as retakes to the appropriate departments. Likewise, lighters shouldn't be tweaking shaders either. Lighters should only be concentrating on the lighting (and often compositing) of the scene.

Now, I'm not saying that in practice many lighters don't wind up tweaking everything under the sun in order to get their shots to work. They do. They just shouldn't have to. It is messy, often hard to reproduce across multiple shots, and generally a waste of their time."
-MDuffy

Wednesday, March 16, 2011

Intro
__________________________________________________________

A little about what i'll be trying to achieve with this tutorial,
2. create a grape phenomenon shader
3. create presets for the grape shader and adapted to use with cherries.

Ok, so we want to make some delicious fruity shaders, seeing theres an available and free fruit platter over at 3drender.com's lighting challenge we'll just start with that. Next up, we'll need to gather some references,

 Image 1 HSV range of grapes
 Image 2
 Image 3
 Image 4
 Images 5
 Images 6

 Image 7

There are several varieties of grapes, with hue variations between purple and green. I have decided to proceed with the red/maroon grapes.

From the reference we can start determining the number of layers and shading component this shader would require.

The Basics
__________________________________________________________
• diffuse
• reflection
• specular
• sss
• rim
1. in closer examination, the grape skin has some white discoloration to it.
2. the reflection is from the water, and there is a glossy reflection/spec from the skin
3. two layers, the skin and the meat
4. heavy rim effect, combine with reflection fresnel effect

1.Diffuse
__________________________________________________________
One thing to remember is that since we will be adding SSS the diffuse component should be darker than what appears in the photo. Try peeling a grape skin, and flatten it out on the table, that should be the diffuse color component(and even darker than that for effect). There are some white discoloration on the grape skin, from what i read they are molds?. I'll add the effect, but tone it down, so it still looks real, but more appealing.

 Diffuse Component Sub Tree

For the white mold i used two 3d textures, a 3d noise and multiply it by a crater to break up the pattern. And for the base, I use a ramp as a solid color, and add small bit of HSV color noise. Breaks up the monotony of solid colors. Add the white mold to the base, but set the alpha of the mold layer to something low.

Key Settings:
• Base color
• White mold alpha
2. Reflection
__________________________________________________________
Look closely at the reference, particularly images 2 and 6. In image 2, the reflection intensity are higher due to the water. In image 6, we can see the base glossy reflection/specularity. We should also consider whether the white mold has any effect on base glossy reflection/specularity.  Before you start, get a hdr map in there, there are three ways to do this,
• Image Based Lighting under render globals
• mib_lookup_spherical connected to the environment slot in the Shading Goup
• if theres a slot in the node
I use the second method as its more flexible.

 Reflection and Water Reference

I'm aiming for the glossy base reflection of the grape skin first. So I'll put in a mib_glossy_reflection.

 Glossy Reflection
Here we have glossy reflection at its default settings(environment color at white), the environment color is really just the intensity(really terrible that Mental Ray lacks consistency across the board). Notice how the area light disappeared, and seems to be a blackhole, not good. Most of the time mib_glossy_reflection will work for me, however, i need to tweak the surface shading normals(ie bump) on my reflection, so i'll use the mia material instead.

 Base MIA Glossy Reflection
For MIA glossy reflection, i set the BDRF curve to 1.0, and 90 degree refl intensity to 0. As I don't want the rim/fresnel effect here. It still shows, however, but neglible. Tweak the glossiness and intensity and turn off everything else.

Next, for the water, we need to analyse the noise pattern, scale, and frequency of the droplets. So some observations,

• droplets are ~0.5mm to ~2mm in diameter
• droplets occur more on the top, and less on the bottom
• a single large droplet on the bottom, on some grapes.

 Droplet Tree

 Lrg and Sml Droplet Settings

I will use the leather 3d texture node for both small and large droplets, and a volume noise for masking out areas for small droplets. Note the remap HSV, both large and small droplet uses the same curve, the curve defines the profile of droplet.

 Droplet bumps
 Scale  Y

I'm testing with a lambert shader. Since we're using a 3d texture, connect the map to a 3d bump > misss_normal > mia bump slot. The large drops are too round, so I will scale the Y axis of the placed3d node to simulate the gravity effect on the droplet. I scaled the small droplets the same way, but less.

 Reflection + Refraction
 Base + Droplet
 Value Correct

I will turn off the diffuse component and turn on reflection and refraction now. I mapped the noise to an HSV remap to get the contrast and value for reflection and refraction. I corrected the final output with a HSV remap as the hottest highlights are blowing out, and the dark side reflections aren't showing up.

3. SSS
__________________________________________________________
Looking at the references, we can determine some properties,
• grape skin, should be similar to the diffuse layer, but brighter.
• the meat is yellowish, with vein patterns in them
• the back scattering is yellowish as well, to give that very translucent look
Theres plenty of SSS tutorials out there, so i'll just throw out the settings. I'm using the fast_SSS shader here. The diffuse color goes into the diffuse color slot here as well. Kind of skipping on detail methodology here, but the basic shading principles still apply here, Add noise to everything, and add noise to the noise.

4. Rim
__________________________________________________________

 Rim Tree and Settings
There are light side rims and the dark side rims. Using a sampler info node > ramp(v coord) > lambert(color), i can create the light side rim. I would first invert the ramp, then tweak the ramp for the rim thickness i desire.

the dark side rim would be like this,

A. sampler info > ramp > surface shader(color)
B. sampler info > ramp > lambert(color)

A-B = dark side rim, so use a layered texture and subtract it.

Notes: for B, i would set diffuse to  a value between 1 and 3 to get a thin dark side rim. but anything greater than 1 i would add a Remap HSV and set the high saturation level to inverse of diffuse level. so if diffuse is 3, saturation would be 0.333.
B. sampler info > ramp > lambert(color) > Remap HSV > layered texture.

5. Combining everything
__________________________________________________________

 Final Tree
 Extreme Closeup
 Close Up
 Final View

The droplet doesn't hold up that well under extreme closeups, but at that distance, the droplets becomes the focus, and a particle on surface + blobby solution will be better suited for our hero droplet.

Just before going for the final render, I turned on Final Gather, and I turned on indirect lighting for the lightmap as well. I went back and forth and tweaked the settings, the basic setup is there, however, the difficult part is balancing every aspect of the shader tree and coming to a conclusion on how YOU want it to look like. Sometimes, its easier to just go for a photorealistic look, but sometimes  it looks better placing emphasis on several aspects. In this instance, I upped the SSS, specifically the back SSS, and left the reflection on relatively high.