Mario Marengo Posted February 18, 2006 Share Posted February 18, 2006 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. Quote Link to comment Share on other sites More sharing options...
sibarrick Posted February 18, 2006 Share Posted February 18, 2006 Just to make sure I have this right is the attached a fair visual summary of what you are after. Quote Link to comment Share on other sites More sharing options...
sibarrick Posted February 18, 2006 Share Posted February 18, 2006 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. Quote Link to comment Share on other sites More sharing options...
Mario Marengo Posted February 18, 2006 Author Share Posted February 18, 2006 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! Here, for reference, is the parametric approach ("forPolys" set to 0) applied to a parametric surface (NURBs): Here's a test with the parametric approach applied to polys (nasty triangles): And here it is with the new polygonal method I'm working on... but without the Ng correction (parameter "forPolys" set to 1) Cheers! Quote Link to comment Share on other sites More sharing options...
sibarrick Posted February 18, 2006 Share Posted February 18, 2006 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. Quote Link to comment Share on other sites More sharing options...
Mario Marengo Posted February 20, 2006 Author Share Posted February 20, 2006 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: And this is with the correction: Quote Link to comment Share on other sites More sharing options...
Mario Marengo Posted February 20, 2006 Author Share Posted February 20, 2006 Well... thanks to Mark Elendt (from SESI), we have a solution! YAY! Nope, it's not shader-based because, as Mark explained: The problem (as I see it) is that the derivatives of any variable willbe 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! Quote Link to comment Share on other sites More sharing options...
sibarrick Posted February 21, 2006 Share Posted February 21, 2006 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. Actually it's quite comforting in a way that even you can be beaten when maths just won't play the game. Thanks for all the work and sharing too. Good stuff. Quote Link to comment Share on other sites More sharing options...
Mario Marengo Posted February 21, 2006 Author Share Posted February 21, 2006 Actually it's quite comforting in a way that even you can be beaten when maths just won't play the game. Oh, that's very easily done -- I like to describe my self-taught math knowledge as Swiss Cheese... "full of holes" Quote Link to comment Share on other sites More sharing options...
bgold Posted March 14, 2006 Share Posted March 14, 2006 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 > 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 > 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; } Quote Link to comment Share on other sites More sharing options...
Mario Marengo Posted March 14, 2006 Author Share Posted March 14, 2006 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 > 0.0) { vector tu = tanU/urough, tv = tanV/vrough; //<-- CHANGED LL = normalize(L); cos_i = dot(LL , nn); if (cos_i > 0.0) { H = normalize(V + LL); float NdotH = dot(H,nn); uval = dot(tu, H); //<-- CHANGED vval = dot(tv, H); //<-- 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. Quote Link to comment Share on other sites More sharing options...
bgold Posted March 14, 2006 Share Posted March 14, 2006 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 <math.h> 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 > 0.0) { illuminance(P, nn, M_PI_2) { vector LL = normalize(L); float cos_i = dot(LL, nn); if(cos_i > 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 Quote Link to comment Share on other sites More sharing options...
Recommended Posts
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.