14
$\begingroup$

Is there a way to get an object's width in Cycles as a value that I can use, for example, as the color input of a diffuse shader? Basically, is there a way I can get the volume absorption shader's output as a value?

Here is an example of what I mean:

enter image description here

The thicker the glass, the more subsurface scattery it looks.

I was able to achieve what I wanted by creating two renderlayers; the first renderlayer would render some object with just a volume absorption material and output the image to a folder, and the second renderlayer containing the same object but with a material using the first renderlayer's output as an image texture. But this is pretty annoying because I have to use two renderlayers and an image sequence.

I have tried using the fake absorption method (The one that utilizes the Beer-Lambert law) to do so, but it seems I cannot invert the output value or even use it at all on a surface shader that does not have light pass through it. Maybe there is a way to alter the math behind the nodes so that it outputs an inversed version of itself?

$\endgroup$
  • 1
    $\begingroup$ You can sort of do this with the ray length output, but AFAIK it only allows you to control the shading of backfaces. If you only want to mix diffuse colors based on thickness or something, then that may work. $\endgroup$ – gandalf3 Aug 22 '15 at 6:32
  • $\begingroup$ Related: blender.stackexchange.com/q/4969/599 and blender.stackexchange.com/q/3573/599 $\endgroup$ – gandalf3 Aug 22 '15 at 6:39
  • $\begingroup$ @gandalf3 I found both the related links very helpful, but I feel like in both of them the issue was not resolved. Also, in your first comment what is the 'that' that may work? $\endgroup$ – Wyvernul Aug 22 '15 at 15:08
  • $\begingroup$ Indeed. If all you want to do is mix between two solid colors, then this answer describes it in more detail. $\endgroup$ – gandalf3 Aug 22 '15 at 17:40
  • 1
    $\begingroup$ This may also interest you or future seekers youtube.com/watch?v=qkqsx951gcg it's about glass absorption in blender cycles using scientific study to recreate the real world glass shader. $\endgroup$ – hawkenfox Jan 2 '16 at 12:46
8
$\begingroup$

Open Shading Language

You can enable OSL in the render tab of the properties panel.

Create a text either externally or in Blender, write your OSL code in that text and save if necessary. In the node editor, add a script node and select the mentioned text.

Based on the Blender manual using trace in this case is OK.

Simple depth

These shaders need only one trace per sample and get precise values. Can be used anywhere without any bias.

Normal dependent depth

shader depth(output float depth = 0) {

    if(trace(P, - N))
        getmessage("trace", "hitdist", depth);
}

You can use this code to get the depth along the normal of the surface.

Normal

View dependent depth

shader depth(output float depth = 0) {

    if(trace(P, - I))
        getmessage("trace", "hitdist", depth);
}

You can use this code to get the depth from the point of view.

Plugging in the proper function of this depth to a transparent shader you can create a uniform volume absorption for example.

View

Stochastic depth

These shaders trace one random direction per sample which average out over the rendering process. The output of these have to be used carefully: They can be used to mix colors or shaders and probably well enough as the roughness of a glass for example. But they can not be used for a color ramp with lots of color stops for example.

Mathematically: They are unbiased if the resulted color of the sampled pixel is a liner function of the depth value.

I could add multiple sampling, which would dramatically reduce the bias for those non-linear cases, but using those would slow down rendering. I will write those versions if you ask me to.

The random

float random(int seed) {

    return cellnoise(P * 732283.511 + seed * 138257.835);
}

This is a position dependent direct seeded pseudo-random number generator. Somehow I did not get too much luck with the built-in random function. Works because the position is already pseudo-random with small variance.

Uniform depth

float random(int seed) {

    return cellnoise(P * 732283.511 + seed * 138257.835);
}

vector hemiRandom(vector direction) {

    vector result;

    int seed = 0;

    do {

        result[0] = random(++seed) - 0.5;
        result[1] = random(++seed) - 0.5;
        result[2] = random(++seed) - 0.5;

    } while(dot(result, result) > 0.25);

    if(dot(result, direction) > 0)
        return result;

    return - result;
}

shader depth(output float depth = 0) {

    if(trace(P, hemiRandom( - N)))
        getmessage("trace", "hitdist", depth);
}

This takes every direction equally into account.

Semi-mathematically: Every direction inside the mesh is sampled with the same chance.

You could considere this as one general view independent depth.

Uniform

Lambert depth

float random(int seed) {

    return cellnoise(P * 732283.511 + seed * 138257.835);
}

vector lambertRandom(vector direction) {

    vector result = 0;

    int seed = 0;

    do {

        result[0] = random(++seed) - 0.5;
        result[1] = random(++seed) - 0.5;

    } while(dot(result, result) > 0.25);

    result[2] = sqrt(0.25 - result[0] * result[0] - result[1] * result[1]);

    vector a = 0;

    if(direction[0] == 0) {

        a[1] = direction[2];
        a[2] = - direction[1];

    } else {

        a[0] = direction[1];
        a[1] = - direction[0];
    }

    vector b = cross(a, direction);

    return result[0] * a + result[1] * b + result[2] * direction;
}

shader depth(output float depth = 0) {

    if(trace(P, lambertRandom( - N)))
        getmessage("trace", "hitdist", depth);
}

Takes depths towards the normal more into account, the same way a transulcent shader samples the light of its environment.

Mathematically: The density of the random is linear to the dot product of the sample and the normal.

You could considere this as another general view independent depth.

Lambert

Rough depth

float random(int seed) {

    return cellnoise(P * 732283.511 + seed * 138257.835);
}

vector roughRandom(vector face, vector direction, float roughness) {

    vector result;

    int seed = 0;

    while(1) {

        result[0] = random(++seed) - 0.5;
        result[1] = random(++seed) - 0.5;
        result[2] = random(++seed) - 0.5;

        if(dot(result, result) < 0.25) {

            result = normalize(direction) + result * roughness;

            if(dot(result, face) < 0)
                return result;
        }
    }
}

shader depth(float roughness = 0, output float depth = 0) {

    if(trace(P, roughRandom(N, - I, roughness)))
        getmessage("trace", "hitdist", depth);
}

This is a poor roughness model but could be good enough for your use.

This is general view dependent depth with roughness control.

Rough

Nodes

These are the nodes used for the renders:

Nodes

$\endgroup$
  • 1
    $\begingroup$ I kind of got caught up in this, spent some time to develop it also for my possible future use. :) $\endgroup$ – Róbert László Páli Aug 1 '16 at 11:47
0
$\begingroup$

To get the thickness per pixel, would use many many raycast,

You can do that with bvhTree.castRay, then raycast backward along the same angle, from behind, the distance between the two points is the thickness.

You could save the data as a map but it would be view dependant

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.