Volumetric scatter density - ray length

I have been playing with the volumetric scatter and absorption nodes over the past few days.

I want to vary the density of the volumetric material via the thickness of the object in a non linear fashion i.e. instead of the volumetric effect gradually getting gradually more prominent as the thickness increases - I want it to stay clear on the thinner parts, then suddenly get denser. Instead of the density changing linearly by say 1 unit per unit depth, I want to increase it exponentially.

I though about using the same trick as the “fake volumetric absorption” method uses to create absorptive glass (i.e. using the Lightpath --> Ray Length node) - however it doesn’t seem to work.

Does anyone know of a way to tweak the volume density falloff based on the object thickness?

OSL???

(everybody forget that we have OSL!!)

void rng_seed(output int rng, int seed)
{
  int chash = seed;
  if (chash == 0) chash = 1;
  rng = chash * 891694213;
}

float rng_uniform(output int rng)
{
  float res = rng / float(2147483647) * 0.5 + 0.5;
  rng *= 891694213;
  return res;
}

void to_unit_disk(float x, float y, output float x_out, output float y_out)
{
  float r, phi;
  float a = 2.0 * x - 1.0;
  float b = 2.0 * y - 1.0;
    
  if(a > -b) {
    if(a > b) {
      r = a;
      phi = M_PI_4 *(b/a);
    }
    else {
      r = b;
      phi = M_PI_4 *(2.0 - a/b);
    }
  }
  else {
    if(a < b) {
      r = -a;
      phi = M_PI_4 *(4.0 + b/a);
    }
    else {
      r = -b;
      if(b != 0.0) phi = M_PI_4 *(6.0 - a/b);
      else phi = 0.0;
    }
  }
  x_out = r * cos(phi);
  y_out = r * sin(phi);
}

void make_orthonormals(vector N, output vector a, output vector b)
{
  if(N[0] != N[1] || N[0] != N[2]) a = cross(vector(1, 1, 1), N);
  else a = cross(vector(-1, 1, 1), N);
  
  a = normalize(a);
  b = cross(N, a);
}

vector sample_cos_hemisphere(vector N, float randu, float randv)
{
  vector T, B;
    
  make_orthonormals(N, T, B);
  to_unit_disk(randu, randv, randu, randv);
  float costheta = sqrt(max(1.0 - randu * randu - randv * randv, 0.0));

  return randu * T + randv * B + costheta * N;
}

shader MinThickness(
  float Distance = 0.2,    
  int Samples = 1,
  output float Fac = 0)
{
  int i, rng;
  float f, randu, randv, ray_t = 0;
  float mindst=Distance;
  vector ray_P, ray_R;

  f = fmod(cellnoise(P*123456.0), 1.0);
  rng_seed(rng, int(f * 2147483647));
  
  for(i = 0; i < Samples; i++) {
    randu = rng_uniform(rng);
    randv = rng_uniform(rng);
    
    ray_P = P;
    ray_R = sample_cos_hemisphere(-N, randu, randv);
    
    if(trace(ray_P, ray_R, "maxdist", Distance)) {
        getmessage("trace", "hitdist", ray_t);
        ray_t/=Distance*dot(-N, ray_R);
        mindst=min(mindst, ray_t);
    }
  }
  Fac=pow(M_E, -mindst)/Distance;
}

Now all one have to do is to use this node to mix colors, change densities, or whatever.

You can count on Secrop to come up with a simple solution…LOL :evilgrin:

I didn’t forget we have OSL - I just:

  1. Prefer to work in nodes
  2. Prefer to render using GPU
  3. Lack the skills to code in OSL and also currently lack the time to learn it :o