Jump to content

Uv Gradients


Recommended Posts

Hi all,

I just can't seem to figure this one out... :(

Part of my shader requires that I build a local coordinate system (seems to be the theme of the month, lol) at P that is oriented such that its X-axis lines up with the projected *texture* coordinate system's U-axis, and the local Y-axis with the texture's V-axis. Finally, the local Z axis can be left matching the shading normal. Texture uv coordinates are bound to the object as the vector parameter "uv".

For any parametric surface, I can calculate the uv gradient like this:

vector dpdu = Du(P);
vector dpdv = Dv(P);
vector local_x = normalize(Du(uv.x)*dpdu + Dv(uv.x)*dpdv);
vector local_y = normalize(Du(uv.y)*dpdu + Dv(uv.y)*dpdv);
vector local_z = normalize(frontface(N,I));

But my object is polygonal (remodeling is not an option) and the above breaks down for any poly which is not perfectly rectangular -- try it on a triangle grid as an extreme example to see what I mean.

The problem is not that the orientation of dPds and dPdt is flip-flopping all over the place, but that they are not orthogonal to each other... and I haven't been able to compensate for that fact (after trying a *lot* of things).

So I'm stuck.

I'm going to try and sample a uv-ramp at three points and see if I can get the gradient like I would for a density function, but I'm not holding my breath...

Any/all ideas greately appreciated.

Link to comment
Share on other sites

I've had a play around and made some pretty pictures of derivatives on triangles but I can't see a way to fix it. It seems to be a fundamental limitation of dealing with triangles. I have had this problem in the past too and my only solution probably won't help you. I was lucky enough to be converting nurbs surfaces to triangles so I was able to store extra information in the conversion process as point attributes I then used that in the shader to replace the derivatives. I don't know if anything like that is possible in your case?

The only other thing i can think of is along similar lines and is also a hack, it would involve "projecting" an axis along with the uv coordinates when the model is textured. I expect you have concidered that already though.

So sorry I can't help.

post-509-1140260347_thumb.jpg

post-509-1140260357_thumb.jpg

Link to comment
Share on other sites

Hey Simon,

Thank you so much for looking into this, I really, really appreciate it! :)

It's a tough one, and it's been driving me crazy for a couple of days...

Embedding vector info in the map is no good because it doesn't get transformed along with the projection. Inheriting from a parametric version is a possibility, but not always available. Doing it directly in SOPs (as Ben Schrijvers suggested in the list) is also a possibility but still requires a way to calc the gradients on a triangle formed from neighbouring points, and that calculation, as I've found out, may not be so trivial.

One thing seems to be clear to me though: this is useful (with a capital U) and is begging for a true polygonal solution.

I stayed up waaay past my bed time last night wracking my little brain, and I *think* I may have come up with a possible solution (...notice the absolute certainty in that statement?).

Here's what I've got so far. It doesn't yet deal with the Ng->N correction and it's likely got its share of small brain farts, but it's the first little ray of sunshine...

 // Calculate gradient of scalar x
// Expects the normal 'n' to be unit-length
vector gradient(vector p,n; float x; int forPolys,dsmooth,dextrap) {
   vector grad = 0;

   vector dpdu = Du(p,"smooth",dsmooth,"extrapolate",dextrap);
   vector dpdv = Dv(p,"smooth",dsmooth,"extrapolate",dextrap);
   vector dxdu = Du(x,"smooth",dsmooth,"extrapolate",dextrap);
   vector dxdv = Dv(x,"smooth",dsmooth,"extrapolate",dextrap);

   // Parametric version
   if(!forPolys) {

      grad = normalize(dxdu*normalize(dpdu) + dxdv*normalize(dpdv));

   // Polygonal version
   } else {        

      // form triangle from three local points,
      // with origin at p, and on plane 'n'.
      vector p0 = 0, p1 = dpdu, p2 = dpdv;

      // build local 2D basis {lx,ly} with lx on p1-p0 edge
      vector lz = n;
      vector lx = normalize(p1);
      vector ly = normalize(cross(lx,lz));

      // attribute values at each triangle point
      float x0 = x, x1 = x0+dxdu, x2 = x0+dxdv;

      // transform points to local frame
      vector lp0 = 0;
      vector lp1 = set( dot(p1,lx), dot(p1,ly), 0);
      vector lp2 = set( dot(p2,lx), dot(p2,ly), 0);

      // position deltas
      float dx0 = lp1.y-lp2.y,
            dx1 = lp2.y-lp0.y,
            dx2 = lp0.y-lp1.y;
      float dy0 = lp2.x-lp1.x,
            dy1 = lp0.x-lp2.x,
            dy2 = lp1.x-lp0.x;

      // calc the gradient (gx) in the local frame
      float d = (lp1.x-lp0.x)*(lp2.y-lp0.y) -
                (lp2.x-lp0.x)*(lp1.y-lp0.y);

      vector gx = set( (dx0*x0 + dx1*x1 + dx2*x2) / d,
                       (dy0*x0 + dy1*x1 + dy2*x2) / d,
                        0 );

      // put back in original space and normalize
      grad = normalize( set(
               gx.x*lx.x + gx.y*ly.x,
               gx.x*lx.y + gx.y*ly.y,
               gx.x*lx.z + gx.y*ly.z ) );

   }

   return grad;

}

// Calculate gradients of 2D vector {x,y} (e.g: texture uv)
// Expects the normal 'n' to be unit-length
void gradient2(vector p,n; float x,y; int forPolys,dsmooth,dextrap;
               export vector gradx; export vector grady ) 
{
   gradx = gradient(p,n,x,forPolys,dsmooth,dextrap);
   grady = gradient(p,n,y,forPolys,dsmooth,dextrap);
}

This is for the shading contexts at the moment, but given the method it uses, it could easily be turned into a sop (vex or hdk). Anyway... I'll start refining/debugging now and see where I end up.

I'll post up the code as I go... keep your fingers crossed! :P

Here, for reference, is the parametric approach ("forPolys" set to 0) applied to a parametric surface (NURBs):

post-148-1140294831_thumb.jpg

Here's a test with the parametric approach applied to polys (nasty triangles):

post-148-1140292903_thumb.jpg

And here it is with the new polygonal method I'm working on... but without the Ng correction (parameter "forPolys" set to 1)

post-148-1140293046_thumb.jpg

Cheers!

Link to comment
Share on other sites

I actually nearly suggested trying to align things with another frame of reference but I couldn't think how to do it. I still can't quite picture it from the code, but it clearly seems to work although not 100% smooth yet, I'm sure you'll get there. This actually helps me understand what is going on with derivatives in mantra quite a bit so thankyou again for your exemplary work. :)

Link to comment
Share on other sites

Tiny bit of progress...

Added the Ng->N correction, which does smoth it out somewhat, but there are still discontinuities in some configurations. I'm starting to think that the remaining issues might not be solvable from within the shader as we have no control over the dPds/dPdt orientations... I'll try doing it in SOPs to see if it's more successful there (we can pick our edges in that case, so...maybe <_<)

Here's the code with the Ng correction:

// Calculate gradient of scalar x
// Expects both 'n' and 'ng' to be unit-length
vector gradient(vector p,n,ng; float x; int forPolys,dsmooth,dextrap) {
   vector grad = 0;

   vector dpdu = Du(p,"smooth",dsmooth,"extrapolate",dextrap);
   vector dpdv = Dv(p,"smooth",dsmooth,"extrapolate",dextrap);
   float  dxdu = Du(x,"smooth",dsmooth,"extrapolate",dextrap);
   float  dxdv = Dv(x,"smooth",dsmooth,"extrapolate",dextrap);

   vector d2xdu = Du(dxdu,"smooth",dsmooth,"extrapolate",dextrap);
   // Parametric version
   if(!forPolys) {

      grad = normalize(dxdu*normalize(dpdu) + dxdv*normalize(dpdv));

   // Polygonal version
   } else {        

      // form triangle from three local points,
      // with origin at 0, and on plane 'n'.
      vector p0 = 0, p1 = dpdu, p2 = dpdv;

      // attribute values at each triangle point
      float x0 = x, x1 = x0+dxdu, x2 = x0+dxdv;

      // build local 2D basis {lx,ly} with lx on p1-p0 edge
      vector lx = normalize(p1);
      vector ly = normalize(p2);
      vector lz = cross(lx,ly);
      ly = cross(lz,lx);

      if(lz!=n) {
         matrix3 m = dihedral(lz,n);
         p1*=m; p2*=m;
         lx*=m; ly*=m; lz*=m;
      }

      // transform points to local frame
      vector lp0 = 0;
      vector lp1 = set( dot(p1,lx), dot(p1,ly), 0);
      vector lp2 = set( dot(p2,lx), dot(p2,ly), 0);

      // position deltas
      float dx0 = lp1.y-lp2.y,
            dx1 = lp2.y-lp0.y,
            dx2 = lp0.y-lp1.y;
      float dy0 = lp2.x-lp1.x,
            dy1 = lp0.x-lp2.x,
            dy2 = lp1.x-lp0.x;

      // calc the gradient
      float d = (lp1.x-lp0.x)*(lp2.y-lp0.y) -
                (lp2.x-lp0.x)*(lp1.y-lp0.y);

      vector gx = set( (dx0*x0 + dx1*x1 + dx2*x2) / d,
                       (dy0*x0 + dy1*x1 + dy2*x2) / d,
                        0 );
      grad = normalize( set(
               gx.x*lx.x + gx.y*ly.x,
               gx.x*lx.y + gx.y*ly.y,
               gx.x*lx.z + gx.y*ly.z ) );

   }

   return grad;

}

This is without the correction:

post-148-1140450730_thumb.jpg

And this is with the correction:

post-148-1140450748_thumb.jpg

Link to comment
Share on other sites

Well... thanks to Mark Elendt (from SESI), we have a solution! YAY! :D

Nope, it's not shader-based because, as Mark explained:

The problem (as I see it) is that the derivatives of any variable will

be linear across the polygon.  This means that taking the second

derivative should result in 0 values.  Which is not what you want...

which makes sense and puts a final nail in the shader-based coffin -- isn't it great how everything makes sense once someone else thinks it through for you? :)

Here's the code and a hip file to test it:

grad.tar.gz

And a great big thanks to Mark for all the help!

Link to comment
Share on other sites

which makes sense and puts a final nail in the shader-based coffin -- isn't it great how everything makes sense once someone else thinks it through for you? :)

Here's the code and a hip file to test it:

grad.tar.gz

And a great big thanks to Mark for all the help!

24910[/snapback]

Hehe I know that feeling. :rolleyes:

Actually it's quite comforting in a way that even you can be beaten when maths just won't play the game. :P

Thanks for all the work and sharing too. Good stuff.

Link to comment
Share on other sites

  • 3 weeks later...

Hello,

Has anyone used this technique in conjunction with rendering anisotropic specular? I've found that in the current configuration the sgrad vex sop won't operate properly due to the fact that the uv texture sop is set to "natural location" (tanu == tanv always). I changed it to "point texture", but no longer got the nice gradient as before, but at least got differing tanU and tanV. The attached code is what I am using for anisotropic-- standard anisotropic calcs, just using a bound tanU and tanV (this function is meant to be inside of an illuminance loop).

Any help in getting anisotropic to work on a polygonal surface is greatly appreciated!

thanks,

-brian

vector
calc_specularAnisotropic(vector nn; vector V; vector tanU, tanV; float urough, vrough)
{
    vector    LL;              // Normalized light vector
    vector    H;               // Half angle vector
    vector    lclr;            // Light color

    float     rz, cos_r, cos_i; // Reflected and incident angles

    float     nml_term;
    float     uval, vval;

    nml_term = 4.0 * M_PI * urough*vrough;

    rz = 0;

    cos_r = dot(nn, V);
    lclr = 0;
    if (cos_r &gt; 0.0)
    {

        if(!isbound("tanU")) {
            tanU = cross(normalize(dPdt),nn);
        }
       
        if(!isbound("tanV")) {
            tanV = cross(normalize(dPds),nn);
        }
        
        tanU /= urough;
        tanV /= vrough;

        printf("tanU: %f tanV: %f\n", tanU, tanV);

            LL = normalize(L);
            cos_i = dot(LL , nn);
            if (cos_i &gt; 0.0)
            {
                 H = normalize(V + LL);
                 float NdotH =  dot(H,nn);
                 uval = dot(tanU, H);
                 vval = dot(tanV, H);
                 rz = cos_i*exp(-2.*(uval*uval + vval*vval) / 
                                    (1.0 + NdotH)); 
                 rz /= nml_term * sqrt(cos_i*cos_r); 
                  
            }
        
    }
   
    return rz;
}

Link to comment
Share on other sites

Hello,

  Has anyone used this technique in conjunction with rendering anisotropic specular? I've found that in the current configuration the sgrad vex sop won't operate properly due to the fact that the uv texture sop is set to "natural location" (tanu == tanv always).

Hi Brian,

The algorithm doesn't work for vertex attributes, so you have to make sure that texture UVs are assigned to points, not vertices.

That aside though, it looks like your function is modifying tanU and tanV (which I guess you must have defined as export parameters)... I don't think you want to be doing this (it might be throwing off your results). I haven't tested your code, but just to make sure that this parameter-stomping isn't causing any problems, I would let the calling function/shader test whether the parameters are bound:

surface testAniso (
      float urough = 0.1;
      float vrough = 0.5;

      vector tanU = 0;
      vector tanV = 0;
   )
{
   vector n  = normalize(frontface(N,I));
   vector v  = normalize(-I);
   vector tu = isbound("tanU") ? tanU : cross(normalize(dPdt),n);
   vector tv = isbound("tanV") ? tanV : cross(normalize(dPds),n);

   Cf = 0;
   illuminance(P,n) {
      Cf += calc_specularAnisotropic(n,v,tu,tv,urough,vrough);
   }
}

and make their scaled versions in the calc_specularAnisotropic() function local:

vector
calc_specularAnisotropic(vector nn; vector V; vector tanU, tanV; float urough, vrough)
{
   vector    LL;              // Normalized light vector
   vector    H;               // Half angle vector
   vector    lclr;            // Light color

   float     rz, cos_r, cos_i; // Reflected and incident angles

   float     nml_term;
   float     uval, vval;

   nml_term = 4.0 * M_PI * urough*vrough;

   rz = 0;

   cos_r = dot(nn, V);
   lclr = 0;
   if (cos_r &gt; 0.0)
   {
           vector tu = tanU/urough, tv = tanV/vrough; //&lt;-- CHANGED

           LL = normalize(L);
           cos_i = dot(LL , nn);
           if (cos_i &gt; 0.0)
           {
                H = normalize(V + LL);
                float NdotH =  dot(H,nn);
                uval = dot(tu, H); //&lt;-- CHANGED
                vval = dot(tv, H); //&lt;-- CHANGED
                rz = cos_i*exp(-2.*(uval*uval + vval*vval) /
                                   (1.0 + NdotH));
                rz /= nml_term * sqrt(cos_i*cos_r);

           }

   }

   return rz;
}

At least to help remove one possible source of grief...

Hope that helps.

Link to comment
Share on other sites

Thanks Mario for the good idea. Here is a simplified shader with the changes you suggested. Still doesn't produce the results I expect. For example, when uvs are applied to points, tanU and tanV don't seem continuous. Any ideas are greatly appreciated.

thanks,

-brian

#include &lt;math.h&gt;

surface anisotropic(
      vector tanU = 0;
      vector tanV = 0;

      float urough = .1;
      float vrough = .1;
      ) {

  Cf = 0;

  float rz = 0;
  
  float nml_term = 4.0 * M_PI * urough*vrough;
    
  vector V = -normalize(I);
  vector nn = normalize(N);

  float cos_r = dot(nn, V);

  vector tu = tanU/urough, tv = tanV/vrough;

  if(cos_r &gt; 0.0) {
    
    illuminance(P, nn, M_PI_2) {
      
      vector LL = normalize(L);
      
      float cos_i = dot(LL, nn);
      
      if(cos_i &gt; 0.0) {
	vector H = normalize(V + LL);
	float NdotH = dot(H, nn);

	float uval = dot(tu, H);
	float vval = dot(tv, H);

	rz = cos_i * exp(-2.0 * (uval*uval + vval*vval) / (1.0 + NdotH));

	rz /= nml_term * sqrt(cos_i * cos_r);

	Cf += rz * Cl;
      }
    }
  }  
}

gradAnisotropic.hip

anisotropic.otl

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...