Tuesday, April 12, 2011

Writing mental ray shaders: Transparency

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. 

From Writing mental ray® Shaders,
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.



MI Source
declare shader
 color "transparent" (
  color "base" default 1 1 1,
  color "transparency" default 0 0 0
 )
apply material
end declare


C Source: Basic
#include "shader.h"

struct transparent {
 miColor base; 
 miColor transparency;
 };

miBoolean transparent(miColor *result, miState *state, struct transparent *params) {
 miColor opacity;
 miColor *base = mi_eval_color(&params->base);
 miColor *transparency = mi_eval_color(&params->transparency);
 mi_trace_transparent(result, state);
 opacity.r = 1 - transparency->r;
 opacity.g = 1 - transparency->g;
 opacity.b = 1 - transparency->b;
 opacity.a = 1 - transparency->a;
 result->r = result->r * transparency->r + base->r * opacity.r;
 result->g = result->g * transparency->g + base->g * opacity.g;
 result->b = result->b * transparency->b + base->b * opacity.b;
 return miTRUE;
 }


C Source: Optimized
#include "shader.h"

struct transparent {
 miColor base; 
 miColor transparency;
 };

miBoolean transparent(miColor *result, miState *state, struct transparent *params) {
 miColor *transparency = mi_eval_color(&params->transparency);
 if (transparency->r == 0 && transparency->g == 0 && transparency->b == 0) {
  *result = *mi_eval_color(&params->base);
 } else {
 if (!(transparency->r == 1 && transparency->g == 1 && transparency->b == 1 && transparency->a == 1)) {
  miColor *base = mi_eval_color(&params->base);
  miColor opacity;
  mi_trace_transparent(result, state);
  opacity.r = 1 - transparency->r;
  opacity.g = 1 - transparency->g;
  opacity.b = 1 - transparency->b;
  opacity.a = 1 - transparency->a;
  mi_opacity_set(state, &opacity);
  result->r = result->r * transparency->r + base->r * opacity.r;
  result->g = result->g * transparency->g + base->g * opacity.g;
  result->b = result->b * transparency->b + base->b * opacity.b;
  }
 }
 return miTRUE;
}

Notes
What the optimized code is doing is that for every point P, if the transparency value equals to 0, just evaluate the color. And if the transparency values are not equal to 1, run the trace function.

Exercises
1. Write an all channel equal boolean function. Two parameters, input color C and input scalar V. Compare C.r, C.g, C.b to V, if equal, return miTRUE.
2. Write an invert auxiliary function. One parameter, input color.
3. Write a  blend channel  function. Compared to blend color, this function takes *result, and use miColor channels as factor ie. transparency.
4. Rewrite the transparency shader with the auxiliary functions.

0 comments:

Post a Comment