PDA

View Full Version : Mental Ray shader query for GI lights


talickca
06-02-2005, 09:23 AM
Im very new to writing mental ray shaders. In theory Im working on a shader that will query each light in the scene to determine if that light is a global illumination light, or at least its been flagged as one via custom attribute, and write those results out to a simple text document, nothing fancy.

I believe using mi_query(miQ_NUM_GLOBAL_LIGHTS) or possibly even mi_query(miQ_GLOBAL_LIGHTS) might be the right way to go but Im not quite sure these are the correct functions to be calling. Perhaps I have the wrong functions?

Most shaders appear to use loop that iterates through each light something like:

for (i=0; i < n; i++)
{
sum.r = sum.g = sum.b = 0;
samples = 0;
......} then moving into a while loop.

Im not sure, but is this the proper place to query each light while the shader is looping through them, and determine if the light is a GI light?


Any help or advice is welcome. Thankyou for your time and patience.

jk

talickca
06-04-2005, 04:34 PM
For anyone who is remotely interested. I am very new at coding as well as custom shader writing for mental ray.

Goal:

To design a surface shader that will query each light in the scene to see if it has been flagged as a "GI light" or any other custom user set attribute. This will be implemented by adding a dynamic attribute on a light during creation (ie...gi_lightAttr) that contains a value of 1 or 0. If the attr is set to 1 the surface shader will pass the message to the light/light shader that is has in fact encountered a flagged light, and that light shader should in turn load a specific pre-rendered light map. This would be the general theory.

At first I thought I could just query each light to determine if it was in fact emitting a photon or energy. Using the miQ_energy function or class, would probably work. Instead I've been asked to just query each light for an attribute that the artist can check on or off and dump those results out to a text file for the mean time. Eventually passing the information to the light shader. For now just getting results out to a text file would be a reason to celebrate.

By starting with a simple surface shader code for example a base blinn from the mental ray dev kit. In the code that follows it contains a loop that will iterate through each light in the scene. I believe that somewhere in this loop, is where I should be querying each light if its GI flag has indeed been set to 1 or 0.

Due to my novice level of experience with C and C++ if any experienced shader writers feels like bestowing some wisdom Im all ears. I believe that building off a base shaders is the most time efficient and best method for learning.
#ifdef HPUX

#pragma OPT_LEVEL 1 /* workaround for HP/UX optimizer bug, +O2 and +O3 */

#endif

#include <stdio.h>

#include <stdlib.h> /* for abs */

#include <float.h> /* for FLT_MAX */

#include <math.h>

#include <string.h>

#include <assert.h>

#include "shader.h"



struct mib_illum_blinn {

miColor ambience; /* ambient color multiplier */

miColor ambient; /* ambient color */

miColor diffuse; /* diffuse color */

miColor specular; /* specular color */

miScalar roughness; /* roughness: avg. microfacet slope */

miScalar ior; /* relative ior (ior_out/ior_in) */

int mode; /* light mode: 0..2 */

int i_light; /* index of first light */

int n_light; /* number of lights */

miTag light[1]; /* list of lights */

};



DLLEXPORT int mib_illum_blinn_version(void) {return(2);}

DLLEXPORT miBoolean mib_illum_blinn(

miColor *result,

miState *state,

struct mib_illum_blinn *paras)

{

miColor *ambi, *diff, *spec;

miScalar roughness; /* average microfacet slope */

miScalar ior; /* index of refraction */

miTag *light; /* tag of light instance */

int n_l; /* number of light sources */

int i_l; /* offset of light sources */

int mode; /* light mode: 0=all, 1=incl, 2=excl */

int n; /* light counter */

int samples; /* # of samples taken */

miColor color; /* color from light source */

miColor sum; /* summed sample colors */

miVector dir; /* direction towards light */

miScalar dot_nl; /* dot prod of normal and dir */

miScalar s; /* specular refl. for these angles */

/* check for illegal calls */

if (state->type == miRAY_SHADOW || state->type == miRAY_DISPLACE ) {

return(miFALSE);

}

ambi = mi_eval_color(&paras->ambient);

diff = mi_eval_color(&paras->diffuse);

spec = mi_eval_color(&paras->specular);

roughness = *mi_eval_scalar(&paras->roughness);

ior = *mi_eval_scalar(&paras->ior);

*result = *mi_eval_color(&paras->ambience); /* ambient term */

result->r *= ambi->r;

result->g *= ambi->g;

result->b *= ambi->b;

mode = *mi_eval_integer(&paras->mode);

n_l = *mi_eval_integer(&paras->n_light);

i_l = *mi_eval_integer(&paras->i_light);

light = mi_eval_tag(paras->light) + i_l;

if (mode == 1) /* modify light list (inclusive mode) */

mi_inclusive_lightlist(&n_l, &light, state);

else if (mode == 2) /* modify light list (exclusive mode) */

mi_exclusive_lightlist(&n_l, &light, state);

/* Loop over all light sources */

for (n=0; n < n_l; n++, light++) {

sum.r = sum.g = sum.b = 0;

samples = 0;

while (mi_sample_light(&color, &dir, &dot_nl, state,

*light, &samples)) {

/* Diffuse reflection: Lambert's cosine law */

sum.r += dot_nl * diff->r * color.r;

sum.g += dot_nl * diff->g * color.g;

sum.b += dot_nl * diff->b * color.b;

/* Specular reflection: Blinn reflection model */

s = mi_blinn_specular(&state->dir, &dir,

&state->normal, roughness, ior);

if (s > 0.0) {

sum.r += s * spec->r * color.r;

sum.g += s * spec->g * color.g;

sum.b += s * spec->b * color.b;

}

}

if (samples) {

result->r += sum.r / samples;

result->g += sum.g / samples;

result->b += sum.b / samples;

}

}

/* add contribution from indirect illumination (caustics) */

mi_compute_irradiance(&color, state);

result->r += color.r * diff->r;

result->g += color.g * diff->g;

result->b += color.b * diff->b;

result->a = 1;

return(miTRUE);

}

gga
06-07-2005, 10:35 PM
If you have control of the source code for your light shader, the problem is relatively easy. You can then just use mi_shaderstate_* to first store the flag you want in the light shader (where you have access to all the light parameters) and read it back in in the surface shader within the while loop.

If you are using light shaders that come with the 3d package (like say xsi's light shader or maya's), it is a tad tricky. Some vendors, like alias or xsi, for example have special routines to access their own parameters (like determining whether a light is diffuse only, for example). Unfortunately, these were never made generic for all shader parameters and are incompatible across vendors.
Thus, to do what you want in a more compatible way, you basically need to delve into the internal structure of how mray stores shader parameters, which ain't pretty. Basically, starting from the light tag, you need to use a couple of mi_query's to obtain first the tag to the light shader and then the pointer to the declaration and parameters. Something like (in untested code):


// light[] is the one obtained from mi_query( miQ_GLOBAL_LIGHTS ...)
// or the light parameter passed to the shader.
miTag shaderTag;
mi_query( miQ_LIGHT_SHADER, state, light[i], &shaderTag );

char* values;
mi_query( miQ_FUNC_PARAM, state, shaderTag, &values );

int paramsSize;
mi_query( miQ_FUNC_PARAM_SIZE, state, shaderTag, &paramsSize );

// Now, the name of each parameter is stored in the
// declaration of the shader (ie. declare section of mi file)
miTag shaderDeclTag;
mi_query( miQ_FUNC_DECL, state, shaderTag, &shaderDeclTag );

char* params;
mi_query( miQ_DECL_PARAM, state, shaderDeclTag, &params );


// Iterate thru params, determining the type of parameter
// it is. This is encoded, if I recall correctly, with a first
// character that tells you what type of parameter it is and then the
// parameter name as a normal null terminated c string.
// Thus, something like: 'b'diffuseOnly\0'c'color\0
char* offset = values;
char* curr = params;
char* lastParam = params + paramsSize;
char type;

while ( curr < lastParam )
{
type = *curr; curr++;

if ( strcmp( curr, "whateverparameteryoulookfor" ) == 0 )
break; // we found the parameter we want...

mi_info("Parameter: %s", curr );

switch(type)
{
case 'b': // boolean
mi_info("Value: %d", (int)*((miBoolean*)offset));
offset += sizeof(miBoolean);
break;
case 's': // scalar
mi_info("Value: %f", *((miScalar*)offset));
offset += sizeof(miScalar);
break;
case 'c': // color
{
miColor* tmp = (miColor*) offset;
mi_info("Value: %f %f %f %f", tmp->r, tmp->g, tmp->b, tmp->a);
offset += sizeof(miColor);
break;
}
case 'v': // vector
{
miVector* tmp = (miVector*) offset;
mi_info("Value: %f %f %f", tmp->x, tmp->y, tmp->z);
offset += sizeof(miVector); break;
}
case 't': // tag
case 'p': // phenomena shading network
mi_info("Tag: %d", *((miTag*)offset));
offset += sizeof(miTag); break;
case 'm': // matrix
... etc...
default:
offset += 4; // for values we don't know, skip something
}

while( *curr != 0 ) curr++; // skip chars till null
curr++; // also skip null
}

if ( curr >= lastParam )
mi_fatal("light parameter not found");

// cast *offset into whatever you are expecting.
// You can use "type" to verify what you need to cast, with
// another switch statement if you want to be 100% sure.
// For example:
miColor lightColor = *( (miColor*)offset );


// Note, however this method does NOT work properly with shading networks.

talickca
06-09-2005, 05:39 AM
GGA-

Thankyou very much for your informative response. Sorry I did not reply sooner, my box has been with the tech department for updates so I havent had internet access.

We are using the mRay that is included with maya. I have managed to get the custom attribute coded into the .mi files for all the light shaders of mental ray. This being was the easy part. These light shaders then plug into the actual maya lights.

Since this is the case if Im understanding everything correctly I don't have control over the actual light shader code, so I will have to use the second method you reccommended. Using a miQuery to obtain the light tag and from there asking it for its light shader, then asking the shader if its attribute is checked. Also is it best to try and build off an existing shader and implement the code you have provided?

Thankyou very much for the sample of code you provided, for the most part I think I understand it. I was also curious as to what developement enviroment you use to code your shaders?

Thankyou for your time and patience,

jnk

gga
06-10-2005, 01:44 AM
We are using the mRay that is included with maya. I have managed to get the custom attribute coded into the .mi files for all the light shaders of mental ray. This being was the easy part. These light shaders then plug into the actual maya lights.

Hmm... how did you do that, if I may ask? AFAIK, You cannot add a parameter to a maya shader just like that. You can add custom text or maya attributes to the maya light, but these are problematic. Custom text will get passedto the .mi file, but not the interactive render. Maya attributes are not passed onto the mi file, IIRC.
My guess is that you really need to code your own light shader with the added parameters you need, in which case you can then just use mi_shaderstate_*.

For mi_shaderstate_ you'd write something like:


---mydata.h--
struct MyData {
miColor c;
float radii;
};
---mylgt.mi--
declare shader
color "mylgt" (
color "color",
scalar "radii"
)
version 1
apply light
end declare
---mylgt.cpp--
#include "mydata.h"
struct mylgt_t {
miColor color;
float radii;
};
#ifdef cplusplus
extern "C" {
#endif
// must match .mi file version
DLLEXPORT int mylgt_version(void) {return(1);}
DLLEXPORT miBoolean mylgt(
miColor* result,
miState* state,
mylgt_t* p
)
{
MyData value;
// fill it with some data from the parameter struct
value.color = *mi_eval_color(&p->color);
value.radii = *mi_eval_scalar(&p->radii);

mi_shaderstate_set( state, "mydata", &value,
sizeof(MyData), miSS_LIFETIME_EYERAY );
// This is a sample point light... see baselgt.c for more complex
// light types, like spot.
*result = *mi_eval_color(&paras->color);
return(mi_trace_shadow(result, state));
}
---mysrf.mi--
declare shader
color "mysrf" (
color "ambience",
color "ambient",
color "diffuse",
color "specular",
scalar "exponent",
integer "mode",
array light "light"
)
apply surface
version 1
end declare
---mysrf.cpp--
#include "mydata.h"
struct mysrf_t {
miColor ambience; /* ambient color multiplier */
miColor ambient; /* ambient color */
miColor diffuse; /* diffuse color */
miColor specular; /* specular color */
miScalar exponent; /* shinyness */
int mode; /* light mode: 0..2 */
int i_light; /* index of first light */
int n_light; /* number of lights */
miTag light[1]; /* list of lights */
};
#ifdef cplusplus
extern "C" {
#endif
// must match .mi file version
DLLEXPORT int mysrf_version(void) {return(1);}
DLLEXPORT miBoolean mysrf(
miColor* result,
miState* state,
mysrf_t* p
)
{
miColor *ambi, *diff, *spec;
miTag *light; /* tag of light instance */
int n_l; /* number of light sources */
int i_l; /* offset of light sources */
int m; /* light mode: 0=all, 1=incl, 2=excl */
int n; /* light counter */
int samples; /* # of samples taken */
miColor color; /* color from light source */
miColor sum; /* summed sample colors */
miVector dir; /* direction towards light */
miScalar dot_nl; /* dot prod of normal and dir */
miScalar expo; /* Phong exponent (cosine power) */
miScalar s; /* amount of specular reflection */
int size;
MyData* info;
ambi = mi_eval_color(&paras->ambient);
diff = mi_eval_color(&paras->diffuse);
spec = mi_eval_color(&paras->specular);
expo = *mi_eval_scalar(&paras->exponent);
m = *mi_eval_integer(&paras->mode);
*result = *mi_eval_color(&paras->ambience); /* ambient term */
result->r *= ambi->r;
result->g *= ambi->g;
result->b *= ambi->b;
n_l = *mi_eval_integer(&paras->n_light);
i_l = *mi_eval_integer(&paras->i_light);
light = mi_eval_tag(paras->light) + i_l;
if (m == 1) /* modify light list (inclusive mode) */
mi_inclusive_lightlist(&n_l, &light, state);
else if (m == 2) /* modify light list (exclusive mode) */
mi_exclusive_lightlist(&n_l, &light, state);

for (n=0; n < n_l; n++, light++) {
sum.r = sum.g = sum.b = 0;
samples = 0;

while (mi_sample_light(&color, &dir, &dot_nl, state,
*light, &samples)) {
// get data from light
info = mi_shaderstate_get( state, "mydata", &size );
if (info == NULL)
{
mi_warning("light had no data to pass");
}
else
{
mi_info("light has color data: %f, %f, %f | %f",
info->color.r,
info->color.g,
info->color.b,
info->color.a );
mi_info("light has radii data: %f", info->radii );
}
/* Lambert's cosine law */
sum.r += dot_nl * diff->r * color.r;
sum.g += dot_nl * diff->g * color.g;
sum.b += dot_nl * diff->b * color.b;
/* Phong's cosine power */
s = mi_phong_specular(expo, state, &dir);
if (s > 0.0) {
sum.r += s * spec->r * color.r;
sum.g += s * spec->g * color.g;
sum.b += s * spec->b * color.b;
}
}
if (samples) {
float one_samples = 1.0f / samples;
result->r += sum.r * one_samples;
result->g += sum.g * one_samples;
result->b += sum.b * one_samples;
}
}
/* add contribution from indirect illumination (caustics) */
mi_compute_irradiance(&color, state);
result->r += color.r * diff->r;
result->g += color.g * diff->g;
result->b += color.b * diff->b;
result->a = 1;
return(miTRUE);

}


#ifdef cplusplus
}
#endif




Also is it best to try and build off an existing shader and implement the code you have provided?

Probably. Whatever is easier for you.

Thankyou very much for the sample of code you provided, for the most part I think I understand it.


I was also curious as to what developement enviroment you use to code your shaders?

I work with emacs for all coding in any language. I stay away from IDEs in general for coding. At home I do use microsoft .net, but mainly because I am lazy bastard and have not bothered to install linux yet on any box. I would not recommend coding with .net if you have access to a linux box as gcc is a more strict compiler (catches more typos and stl problems). This tends to make it easier to go linux->windows than the other way around.

talickca
06-10-2005, 05:40 AM
gga-

I think Im the one who's incorrect here, perhaps I did something wrong.
You're experience far outways mine.

Thankyou again for all your help.

My first step was to find away to add the attr on the lights for our artists. So
I opened the .mi files up till I found all the mRay entries for the lights. Point, Spot, except physical light.. etc..I cant remember them all at the moment Im away from my work box. I added an attribute of boolean type there inside the .mi file, so that way when artist opens the attrib editor they see a nice little check box that says "gi_Light" on the light shader.

I was going to add something through mel to the lights, but then I figured that mRay would have no way of seeing that attribute or I might just make things harder. So I just added to the default .mi files. I actually thought this was enough to tag the lights this way for the artist to check the box. After this I exported out my .mb scene into .mi format with one of these new light shaders connected to an actual light, so I could verify that I was actually seeing my new custom flag.

I had the .mi file printed for review. Mid way down I located my new attribute/flag.

shader "mib_light_point1"
"mib_light_point" (
"color" 1. 1. 1.,
"shadow" on,
"factor" 0.,
"atten" on,
"start" 1.,
"stop" 100.,
"gi_Light" on <---------My new entry
)


Im guessing what I did was just pass custom text like you said here:

"Custom text will get passedto the .mi file, but not the interactive render"

So instead I should just rewrite my own light shader to connect to a light. I will take that approach instead. Thankyou again for your code sample, time, and your patience with a rookie.

Looking forward to your reply.


jnk

gga
06-10-2005, 06:07 AM
Ok, what you did is fine. However, that just makes the attribute show in the gui. You need to modify the mib_light_point C shader source to add the mi_shaderstate_ line like I said. And you should probably call it something else so as to not touch the original mental images code in case they change it one day.

Note that for the maya (the maya_spot, etc) shaders, you will be kind of hosed, as alias does not give you source code to them. You can start with baselgt's spot shader but re-creating everything that the maya spot shader does with your own code is kind of hard. Well, it is kind of impossible really, as the info about diffuse/specular only gets passed somewhat magically onto maya shaders.

talickca
06-22-2005, 08:20 AM
gga-

I hate to bother you again. I finally managed to get your code into a compiler, been swamped here.

Perhaps you could shed some light in my direction, I believe there is an issue with the following line:

==========================================
info = mi_shaderstate_get( state, "mydata", &size );

if (info == NULL)
=========================================
I know the error is probably trivial to a seasoned programmer but the compiler keeps complaining about

invalid conversion from `void*' to `MyData*'

From what I understand the mi_shaderstate_get returns a void/nothing? How is one expected to get results back from this?

Now when I use the following :

mi_shaderstate_get( state, "mydata" , &size ); without the "info =" part it will compile just fine, but later during rendering it fails badly.

If you have a spare moment any wisdom would be greatly appreciated.

-jnk

gga
06-22-2005, 02:45 PM
Oops, my fault for not testing the code. The compiler complains because indeed a void* can point to whatever data. The mentalray functions use void* so they can store and return any type of pointer. What you need to do is to make what is called a C cast or a C++ static cast to turn the void* into a pointer to your data type. That is:

info = (MyData*) mi_shaderstate_get( state, "mydata", &size );

or... in more C++ fashion:

info = static_cast< MyData*>( mi_shaderstate_get( state, "mydata", &size ) );

Both are equivalent but the C++ way will only work under a C++ compiler. For more info on casting, read your favorite C/C++ manual.

Then, of course, you always have to be careful to always a store a pointer of MyData when you use mi_shaderstate_set() or the results can be unpredictable (ie. crash).


Also, I also noticed that in the light and surface shader, I mistyped the lines like:
*result = *mi_eval_color(&paras->color);
when they should be:
*result = *mi_eval_color(&p->color);

talickca
06-23-2005, 12:36 AM
gga-

Thankyou again for your help, I will update the code soon as possible. You have been most helpful!

-jnk

talickca
06-23-2005, 02:44 AM
Finally got it compiled and it does actually complete a render.

Downside is Im getting some nasty errors in my error log.

API 0.0 warn 302004: /usr/aw/maya6.5/mentalray/include/mib_avalon.mi, line 15: while defining declaration "mib_avalon": declaring nonexisting function mib_avalon


and further down the log Im getting

PHEN 0.0 error 051011: shader "mib_avalon" not found

Its not giving any of the shader specific custom feedback in the render even if I use the mi_info as well.

Now I made sure that I had everything updated correctly with the new name of the shader. I also made sure it was declared my rayrc file and the .mi file was correct as well.

This is where I noticed that I specified it as a "apply material" not "apply surface" like you have. Could this be an issue thats causing my errors? I've tried both and still get errors either way. I verified that the versions were also correct. Not sure what Im still doing wrong.

-jnk

gga
06-23-2005, 08:15 AM
You are not loading the .dll or .so for the shaders. Make sure you have a link line in your rayrc file and that the file is in the location you specified. You can try doing a first test render with the most verbosity to see where mray searches for the shaders.

PS. it is apply material.

CGTalk Moderation
06-23-2005, 08:15 AM
This thread has been automatically closed as it remained inactive for 12 months. If you wish to continue the discussion, please create a new thread in the appropriate forum.