Page 1 of 2 12 LastLast
Results 1 to 10 of 16

Thread: color array inputs and evaluation

  1. #1
    Join Date
    Dec 2004
    Location
    Marina Del Rey, California
    Posts
    4,143

    Default color array inputs and evaluation

    Ever want to have a color array as an input, but be able to evaluate a shader graph as one of the color slots in the array?

    For example, say you were combining separate components of illumination, and wanted to sum some arbitrary number of them together for output into your primary framebuffer. (Say each component was passed through a user framebuffer before entering the combining shader.)

    Anyway, here is a simple example of how to evaluate each color separately in a color array input. I have an add_colors shader which takes in just a single input, an array of colors. The declaration might look like this:

    Code:
    declare shader
        color "add_colors" (
            array color "colors"
        )
        version 1
    end declare
    Now, the loop which iterates on the colors has to be careful in its setup, so that each slot in the array will do a separate mi_eval_color. Here is the code for the shader:

    Code:
    struct add_colors { 
        int      i_colors; 
        int      n_colors; 
        miColor colors[1];
    };
    
    
    miBoolean add_colors( 
        miColor *result, miState *state, struct add_colors *params)
    {
      int n_colors = *mi_eval_integer(&params->n_colors);
      int i_colors = *mi_eval_integer(&params->i_colors);
      miColor *color;
      int i;
      
      
      for &#40;i = 0; i < n_colors; i++&#41; &#123;
        color = mi_eval_color&#40;params->colors + i_colors + i&#41;;
    
        result->r +=  color->r;
        result->g +=  color->g;
        result->b +=  color->b;
      &#125;
        return miTRUE;
    &#125;
    Note that I'm ignoring alpha in this simple example. Also, I'm taking advantage of the fact that inside mental ray my allocation is done automatically when using mi_eval_color. If I wanted to create a copy of a color array, I would have to allocate some memory.
    Barton Gawboy

  2. #2
    Join Date
    Dec 2004
    Location
    Marina Del Rey, California
    Posts
    4,143

    Default

    A cool thing about this is that you can now create Phenomena that can have an explicit number of args for inputs, but reuse just the one actual shader. For example:

    Code:
    declare phenomenon
        color "add_2_colors" &#40;color "color1" default 0 0 0 1,
    			 color "color2" default 0 0 0 1
                            &#41;
         shader "sum"
            "add_colors" &#40; 
    	"colors" 
                &#91; = interface "color1",
                  = interface "color2"
    	    &#93;
        &#41;
        root = "sum"
        version 1
        apply texture, material
    end declare
    
    declare phenomenon
        color "add_3_colors" &#40;color "color1" default 0 0 0 1,
    			 color "color2" default 0 0 0 1,
    			 color "color3" default 0 0 0 1
                            &#41;
         shader "sum"
            "add_colors" &#40; 
    	"colors" 
                &#91; = interface "color1",
                  = interface "color2",
                  = interface "color3"
    	    &#93;
        &#41;
        root = "sum"
        version 1
        apply texture, material
    end declare
    Barton Gawboy

  3. #3
    Join Date
    Dec 2004
    Location
    Marina Del Rey, California
    Posts
    4,143

    Default

    Made this sticky, because I think it is so important with respect to making clean component shader libraries.
    Barton Gawboy

  4. #4
    Join Date
    Aug 2008
    Location
    Ravenna, Ohio, United States
    Posts
    125

    Default

    Hello, since my questions are closely related to your example, I hope it is still ok to ask it here, if not I'm sorry. Pretty much I don't really understand where and how each item in the array are "added" together, and I wanted to know how you would have all the items calculated with different expressions. Like for example, having the result from subtraction, or a multiplication of every item, or having the result be an average of all the items.Another example would be to have a result of the first item raised to the power of the next item, and so on.

    While trying to test subtraction, I have tried changing the result section from-
    Code:
        result->r +=  color->r;
        result->g +=  color->g;
        result->b +=  color->b;
    to-
    Code:
        result->r -=  color->r;
        result->g -=  color->g;
        result->b -=  color->b;
    Although this just seems to make a totally black shader. And I wouldn't even know where to start with using a pow() statement, or doing an average. Although maybe an average would simply be-
    Code:
        result->r +=  color->r / n_colors;
        result->g +=  color->g / n_colors;
        result->b +=  color->b / n_colors;
    So pretty much what I am trying to achieve is different types of layering arrays. And when I decide on all of the different expressions I want to include, maybe I could use another array integers called of "modes" for each layer,where each modes item has its own switch statement. Although how I'll achieve either of these, is beyond me right now...

  5. #5
    Join Date
    Dec 2004
    Location
    Marina Del Rey, California
    Posts
    4,143

    Default

    Although a layer version of this is fully do-able, I would highly suggest to make separate components for different operations.

    I have come to this conclusion after teaching about shader networks, because it is much faster to recognize on visual inspection how the network combines shader nodes. With a mode input,it is harder to recognize what the shader is actually doing, and thus, harder to work with a team.

    Now, for more clarity you don't have to use += or -=, for each component, you could do
    result = result + added_value, or
    result = result - subtracted_value, then if result < 0, result = 0, or similar to check for bounds

    But what does subtract really mean for you, are all the array elements subtracted, or is there a notion of a first base element that is not?

    For average, I'd divide the sum by n_colors after adding all the elements, for clarity.
    Barton Gawboy

  6. #6
    Join Date
    Dec 2004
    Location
    Marina Del Rey, California
    Posts
    4,143

    Default

    And regarding bounds checking, remember that with modern color and lighting pipelines, we use component values greater than one.
    Barton Gawboy

  7. #7
    Join Date
    Dec 2004
    Location
    Marina Del Rey, California
    Posts
    4,143

    Default

    Another thing to note, is that this shader could be used in a shader list.

    If result already has something in it from a previous shader in a shader list, then the elements would be added to that.

    Shader lists are not typically supported by the 3D applications for material shaders, but if you put them in a Phenomena, it will work in the 3D application.
    Barton Gawboy

  8. #8
    Join Date
    Aug 2008
    Location
    Ravenna, Ohio, United States
    Posts
    125

    Default

    Hello, bart, thanks for your replies. I do agree that it is hard to see what the shader is doing from visual inspection, that is why it is nice to be able to check the out of range value at a certain pixel, like mental mill can do on each node. For example-
    http://forum.mentalimages.com/attach...1&d=1241725961
    I will have to test your solution instead of using +=. To clarify, I did mean to have the first array element, or "color1" to be the start of the calculation, so if there were 3 colors it would be color1 - color2 - color3, and for raising to the power, pow(color1, pow(color2, color3)),.Also, how would the average look like when the sum is divided afterward? Would it be-
    result = (result + added_value) / n_colors;
    or could you write-
    result = (result + added_value);
    result = result / n_colors;
    Thanks again for all your help.
    Attached Images Attached Images
    Last edited by Ian_; May 7th, 2009 at 21:54.

  9. #9
    Join Date
    Aug 2008
    Location
    Ravenna, Ohio, United States
    Posts
    125

    Default

    Ok, your suggestion seams to work, but I have to do it for each component-
    result->r = result->r + color->r;
    result->g = result->g + color->g;
    result->b = result->b + color->b;
    result->a = result->a + color->a;
    If I just use result = result + color; the compiler gives an error saying that you can't add 2 pointers, and if I use - or * instead of + it says-
    error C2440: '=' : cannot convert from 'int' to 'miColor *'
    Here is the main code-
    Code:
      int n_colors = *mi_eval_integer(&paras->n_colors);
      int i_colors = *mi_eval_integer(&paras->i_colors);
      miColor *color;
      int i;
      for (i = 0; i < n_colors; i++) {
        color = mi_eval_color(paras->colors + i_colors + i);
        result = result - color;
       }
    	return(miTRUE);
    }
    }
    Also with subtracting color from result, it does seem start with 0, that's why visually, you can tell that using + does anything, whereas multiplying results in 0 and subtraction also starts with 0. So I do need a way of starting from the first color item.

  10. #10
    Join Date
    Dec 2004
    Location
    Marina Del Rey, California
    Posts
    4,143

    Default

    Sorry about leaving out the components, I was just giving a psuedo-code example, not spelling it out exactly. Typically, I'd make a code block if I meant it to be compiled. Also, I try not to put in a code block, I haven't compiled. Also, I'm leaving out the DLLEXPORT needed for Windows compiling. These following compile, for example:

    Code:
    miBoolean average_colors( 
        miColor *result, miState *state, struct average_colors *params)
    {
      int n_colors = *mi_eval_integer(&params->n_colors);
      int i_colors = *mi_eval_integer(&params->i_colors);
      miColor *color;
      int i;
      
      for (i = 0; i < n_colors; i++) {
        color = mi_eval_color(params->colors + i_colors + i);
    
        result->r +=  color->r;
        result->g +=  color->g;
        result->b +=  color->b;
      }
      result->r /= n_colors;
      result->g /= n_colors;
      result->b /= n_colors;
    
        return miTRUE;
    }
    Note that I divided after the summing loop.

    Code:
    miBoolean multiply_colors( 
        miColor *result, miState *state, struct multiply_colors *params)
    {
      int n_colors = *mi_eval_integer(&params->n_colors);
      int i_colors = *mi_eval_integer(&params->i_colors);
      miColor *color;
      int i;
      
      for (i = 0; i < n_colors; i++) {
        color = mi_eval_color(params->colors + i_colors + i);
    
        result->r *=  color->r;
        result->g *=  color->g;
        result->b *=  color->b;
      }
        return miTRUE;
    }
    Last edited by bart; May 7th, 2009 at 23:59.
    Barton Gawboy

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •