Jump to content

Skin Shader trouble


Mcronin

Recommended Posts

OK, I'm trying to port Matt Pharr's skin shader for Entropy to VEX. I've gotten it to compile and it works, sorta... The shader is very dark (I have to jack up my color setting way over 1.0 to get something that looks even remotely like his shader) and I've got a dark halo. This is my first serious attempt at writing VEX, and I'm sure I'm doing something wrong. Would anyone mind taking a look at the code and telling me what's up. Also, the illuminance function. I can't find documentation on it. Can anyone point me to it? I tried specifying PI/2 and vcc complained about not knowing what PI is. It compiled when I tried nothing as below, and I also tried (3.14etc/2) and it made no difference. Does Illuminance default to PI/2 like the Illuminance VOP does?

Matt's explanation can be found in the 2001 Renderman notes here:

http://www.renderman.org/RMR/Books/sig01.c...course48.pdf.gz

Matt's code

/* Evaluate the Henyey-Greenstein phase function for two vectors with
   an asymmetry value g.  v1 and v2 should be normalized and g should 
   be in the range (-1, 1).  Negative values of g correspond to more
   back-scattering and positive values correspond to more forward scattering.
*/
float phase(vector v1, v2; float g) {

	float costheta = -v1 . v2;
	return (1. - g*g) / pow(1. + g*g - 2.*g*costheta, 1.5);
}

/* Compute a the single-scattering approximation to scattering from
   a one-dimensional volumetric surface.  Given incident and outgoing
   directions wi and wo, surface normal n, asymmetry value g (see above),
   scattering albedo (between 0 and 1 for physically-valid volumes),
   and the thickness of the volume, use the closed-form single-scattering
   equation to approximate overall scattering.
*/
float singleScatter(vector wi, wo; normal n; float g, albedo, thickness) {
    float win = abs(wi . n);
    float won = abs(wo . n);

    return albedo * phase(wo, wi, g) / (win + won) *
  (1. - exp(-(1/win + 1/won) * thickness));
}


vector efresnel(vector II; normal NN; float eta; output float Kr, Kt;) {
    vector R, T;
    fresnel(II, NN, eta, Kr, Kt, R, T);
    Kr = smoothstep(0., .5, Kr);
    Kt = 1. - Kr;
    return normalize(T);
}


/* Implements overall skin subsurface shading model.  Takes viewing and
   surface normal information, the base color of the skin, a
   color for an oily surface sheen, the ratio of the indices of 
   refraction of the incoming ray (typically ~1 for air) to the index
   of refraction for the transmitted ray (say something like 1.4 for
   skin), and the overall thickness of the skin layer.  Then loops
   over light sources with illuminance() and computes the reflected
   skin color.
*/
color subsurfaceSkin(vector Vf; normal Nn; color skinColor, sheenColor;
                     float eta, thickness) {
	extern point P;
	float Kr, Kt, Kr2, Kt2;
	color C = 0;

	vector T = efresnel(-Vf, Nn, eta, Kr, Kt);
     
	illuminance(P, Nn, PI/2) {
     vector Ln = normalize(L);

     vector H = normalize(Ln + Vf);
     if (H . Nn > 0)
  C += Kr * sheenColor * Cl * (Ln . Nn) * pow(H . Nn, 4.);
     C += Kr * sheenColor * Cl * (Ln . Nn) * .2;

     vector T2 = efresnel(-Ln, Nn, eta, Kr2, Kt2);
     C += skinColor * Cl * (Ln . Nn) * Kt * Kt2 * 
  (singleScatter(T, T2, Nn, .8, .8, thickness) +
   singleScatter(T, T2, Nn, .3, .5, thickness) +
   singleScatter(T, T2, Nn, 0., .4, thickness));
	}
	return C;
}

/* Basic surface shader that uses the skin reflection model implemented
   above.  Uses Cs for the basic color of the skin (.8, .5, .5) works
   reasonably well for Caucasian skin.
*/
surface skin(color Ka = .5; color sheenColor = 1.;
             float eta = 1./1.4, thickness = .5) {
	normal Nn = faceforward(normalize(N), I);
	vector Vf = -normalize(I);

	Oi = Os;
	Ci = Os * subsurfaceSkin(Vf, Nn, Cs, sheenColor, eta, thickness);
}

My Code

//Lables for the SHOP
#pragma opname  v_LayeredSkin
#pragma oplabel  "VEX Layered Skin"
#pragma opicon  SHOP_surface

//Labels for parameters
#pragma label	diffD  "Dermis Color"
#pragma label	diffS  "Sheen Color"
#pragma label	ior  "IndexofRefraction"
#pragma label	thickness	"Thickness"

//Paramter types
#pragma hint	diffD  color
#pragma hint	diffS  color
#pragma hint	ior  float
#pragma hint	thickness	float


/* Evaluate the Henyey-Greenstein phase function for two vectors with
 * an asymmetry value g.  v1 and v2 should be normalized and g should 
 * be in the range (-1, 1).  Negative values of g correspond to more
 * back-scattering and positive values correspond to more forward scattering.
 */
float phase(vector v1, v2; float g;)
	{
	float costheta = -(dot(v1, v2));
	return (1.0 - g*g) / pow(1.0 + g*g - 2.0*g*costheta, 1.5);
	}

/* Compute a the single-scattering approximation to scattering from
 * a one-dimensional volumetric surface.  Given incident and outgoing
 * directions wi and wo, surface normal n, asymmetry value g (see above),
 * scattering albedo (between 0 and 1 for physically-valid volumes),
 * and the thickness of the volume, use the closed-form single-scattering
 * equation to approximate overall scattering.
 */
float singleScatter(vector wi, wo, n; float g, albedo, thickness;)
	{
	float win = abs(dot(wi, n));
   	 float won = abs(dot(wo, n));

   	 return albedo * phase(wo, wi, g) / (win + won) * (1.0 - exp(-(1/win + 1/won) * thickness));
	}

vector efresnel(vector II, NN; float eta, Kr, Kt;)
	{
	vector R, T;
	fresnel(II, NN, eta, Kr, Kt, R, T);
	Kr = smooth(0.0, 0.5, Kr);
   	 Kt = 1.0 - Kr;
   	 return normalize(T);

	}

/* Implements overall skin subsurface shading model.  Takes viewing and
   surface normal information, the base color of the skin, a
   color for an oily surface sheen, the ratio of the indices of 
   refraction of the incoming ray (typically ~1 for air) to the index
   of refraction for the transmitted ray (say something like 1.4 for
   skin), and the overall thickness of the skin layer.  Then loops
   over light sources with illuminance() and computes the reflected
   skin color.
*/
vector subsurfaceSkin(vector Vf, Nn, skinColor, sheenColor; float eta, thickness;)
	{
	float Kr, Kt, Kr2, Kt2;
	vector C = 0;

	vector T = efresnel(-Vf, Nn, eta, Kr, Kt);
     
	illuminance(P, Nn)
  {
      vector Ln = normalize(L);

      vector H = normalize(Ln + Vf);
      if (dot(H, Nn) > 0)
 	 C += Kr * sheenColor * Cl * (dot(Ln, Nn)) * pow(dot(H, Nn), 4.0);
      C += Kr * sheenColor * Cl * (dot(Ln, Nn)) * 0.2;
      vector T2 = efresnel(-Ln, Nn, eta, Kr2, Kt2);
      C += skinColor * Cl * (dot(Ln, Nn)) * Kt * Kt2 * 
 	 (singleScatter(T, T2, Nn, 0.8, 0.8, thickness) +
    singleScatter(T, T2, Nn, 0.3, 0.5, thickness) +
    singleScatter(T, T2, Nn, 0.0, 0.4, thickness));
  }
	return C;
	}

/* Basic surface shader that uses the skin reflection model implemented
   above.  Uses Cs for the basic color of the skin (.8, .5, .5) works
   reasonably well for Caucasian skin.
*/
surface skin(vector diffD=.5; vector diffS=1; float ior=1; float thickness=.5;)
	{
	vector Nn = frontface(normalize(N), I);
	vector Vf = -(normalize(I));
	float eta = (ior/1.4);

	Cf = subsurfaceSkin(Vf, Nn, diffD, diffS, eta, thickness);
	}

Also, note that I'm a barely competent C/C++ programmer and this is my first stab at writing a shader in VEX. Please tell me if I've done anything stupid from a coding standpoint, aside from whatever I did wrong that's causing the problems I stated above.

EDIT: don't mention the formatting :unsure: When I copied and pasted the code my formatting got blown away :angry:

Link to comment
Share on other sites

I haven't looked at your code but here's some tips for using vex:

- Look in the main help contents "VEX Reference" web page. You can also use a regular page with help function if you're on Linux using the URL at the top if you want.

- Look in $HH/vex/include. I believe you need to include math.h in order to have PI or M_PI depending on your vcc options. prman.h is also instructive for converting prman to vex shaders.

- For VOP to VEX comparisons, it's very easy to right-click on the output node and choose "View Code...".

Link to comment
Share on other sites

Thanks for the info, Edward.

I'm getting to the problem now. It has to do with Matt's efresnel function, specficly how he handles Kt. He presents a fix for fresnel fuctions that mishandle Kt and it's different than what's recommended in the VEX docs. Anyway, I've figured a kludge now that generates something that looks like his shader... unfortuanely it just not a really good skin solution :( Time to try something else.

I was looking at the Renderman notes from last Siggraph, and both Pixar and ILM presented solutions that require referencing the irradiance cache. Can this be done in VEX? I found the irradiance function in VEX, but no way to reference an external cache.

Link to comment
Share on other sites

hi,

i have done the same combined with a colortexture.

works fine. ( i have changed some params sligthly ).

// rewritten and combined with texturing

#pragma hint texlightness color

#pragma hint uv hidden

#pragma hint texmap file

#include <prman.h>

#include <shading.h>

#include <vexnotes.h>

#include <math.h>

float phase(vector v1, v2; float g)

{ float costheta = dot(-v1,v2); return (1. - g*g) / pow(1. + g*g - 2.*g*costheta, 1.5); } float singleScatter(vector wi, wo; normal n; float g, albedo, thickness)

{ float win = abs(dot(wi,n)); float won = abs(dot(wo,n));

return albedo * phase(wo, wi, g) / (win + won) * (1. - exp(-(1/win + 1/won) * thickness)); }

vector efresnel(vector II; normal NN; float eta;output float Kr, Kt;) {

vector R, T;

fresnel(II, NN, eta, Kr, Kt, R, T);

Kr = smoothstep(0., .4, Kr);

Kt = 1. - Kr;

return normalize(T);

}

vector subsurfaceSkin(vector Vf, Nn, texlightness, sheenColor; float eta, thickness)

{ vector P;

float Kr, Kt, Kr2, Kt2;

vector C = {0,0,0};

vector T = efresnel(-Vf, Nn, eta, Kr, Kt);

vector T2;

illuminance(P, Nn, PI /2 ) {

vector Ln = normalize(L);

vector H = normalize(Ln + Vf);

if (dot(H,Nn) > 0)

C += Kr * sheenColor * Cl * dot(Ln,Nn) * pow(dot(H,Nn), 2.); //4.

C += Kr * sheenColor * Cl * dot(Ln,Nn) * .3;

T2 = efresnel(-Ln, Nn, eta, Kr2, Kt2);

C += texlightness * Cl * dot(Ln,Nn) * Kt * Kt2 *

(singleScatter(T, T2, Nn, .8, .8, .5) +

singleScatter(T, T2, Nn, .3, .5, .5) +

singleScatter(T, T2, Nn, 0., .4, .5));

}

return C;

}

// Basic surface shader that uses the skin reflection model implemented // above. surface skin_tex(

float Kd = 1.1 ;

vector texlightness = 1; float eta = .28;

string texmap ="";

vector uv = 0;

)

{

vector rest = 0;

vector sheenColor;

float thickness = .5;

float ss, tt;

vector Nn,Vf;

if (isbound("uv"))

{

ss = uv.x;

tt = uv.y;

}

else

{

ss = s;

tt = t;

}

Nn = faceforward(normalize(N), I); Vf = -normalize(I);

Oi = Os;

sheenColor =(vector(texture(texmap, ss, tt)))*Kd;

Ci *= subsurfaceSkin(Vf, Nn, texlightness,sheenColor, eta, thickness);

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...