Jump to content

Combed Normal Map


up4

Recommended Posts

Hi,

 

I want to save the result of a comb SOP into a tangent space normal map. I'm using a surface shader that translates the normals in tangent space using tangentnormals and save the result to surface color. I set Mantra to render the UV object that I want. In a COP, I substract the resulting render from another render of the same object before the comb SOP (I only want the combed difference). I save that in EXR 32-bit, linear (no LUT), etc.

 

If I want to load the resulting EXR back onto the geometry to replace the comb SOP with an attribfrommap SOP. I guess I also need to process the map values to translate them back in object space vectors, right? If so, is there an inverse function to tangentnormals that already exists?

 

Please see HIP attached. Sphere #1 is for UV unwrapping and to use as a pointer for the "default normals" and Sphere #2 is where the combing and reloading of the map takes place.

 

Thanks!

 

Vincent

combed_normals.hipnc

Link to comment
Share on other sites

*sigh*

 

I know this kind of thing has been discussed in many places many times (especially under the ZBrush tangent space normal map moniker). But somehow, and I don't think I'm this stupid, it's kind of hard to get it to work.

 

So just to get Google to index this thread better, here is the VEX inline code I'm using as a shader on for a UV Mantra render of the object I'm trying to extract the flow/combing data from:

vector  st  = vop_floattovec($s, $t, 0);
vector  nn  = normalize($N);
vector  nng  = normalize($Ng);
vector  utan  = vector();
vector  vtan  = vector();
vector  nutan;
vector  nvtan;

vop_computeTangents("uv", nn, st, utan, vtan, utan, vtan);
nutan = normalize(utan);
nvtan = normalize(vtan);

$output = vop_floattovec(vop_dot(nutan, nng), vop_dot(nvtan, nng), vop_dot(nn, nng));
$output += 1;
$output *= 0.5;

I pass it the N, Ng, s, t inputs from the surface globals and I only define the "Cf" $output handle. I include "voplib.h" otherwise I get errors.

 

What I want now, is to take the resulting normal map in tangent space load it in my geometry network with an attribfrommap SOP and wrangle the values from tangent space back to an object space point attribute (furdirection).

 

I appreciate your help. Thanks.

Link to comment
Share on other sites

and so in this lonely place where I find myself I can think out loud and just jump right into vop_computeTangents to reverse engineer its inverse and I get this new inline VEX which is easy enough to understand for me to just write what will bring all this back to object space:

vector  nn = normalize(N);
vector  nng = normalize(Ng);
vector  utan = gradient(s);
vector  vtan = gradient(t);
vector  nutan = normalize(utan - nn * dot(nn, utan));
vector  nvtan = normalize(vtan - nn * dot(nn, vtan));


$output = vop_floattovec(vop_dot(nutan, nng), vop_dot(nvtan, nng), vop_dot(nn, nng));
$output += 1;
$output *= 0.5;

I'll try tomorrow.

Link to comment
Share on other sites

You can get the world space normal from tangent space by multiplying the different tagent vectors (normal, tangent, bitangent) by the indexes.

So if you have a vector v in tangent space. The same vector in world space is v.x * tangent + v.y * bitangent + v.z * normal. (Might have to check if it's this order)

From world to object space, you can use ntransform.

  • Like 1
Link to comment
Share on other sites

Sorry for my being anal here, but there is something I don't understand here (actually, I don't understand much, but I'm trying hard lol).

 

Given this (in a surface shader context):

vector utan = gradient(s);
vector vtan = gradient(t);
float uval = dot(utan, N);
float vval = dot(vtan, N);

Why is neither uval nor vval equal to zero if the gradient function returns a tangent to the surface?

Link to comment
Share on other sites

I suppose the differential of s and t as returned by gradient() is not guaranteed to be orthogonal to N. That is easy to imagine in cases where N has been overridden by the user to be something else than the true surface normal, but there might be some other cases, or just inaccuracies, where the orthogonality doesn't hold.

 

If you take a look at how the Compute Normals VOP does it, it has a corrective term

 

out_utan = gradient(uv.x);
out_vtan = gradient(uv.y);

out_utan = normalize(out_utan - nn*dot(nn, out_utan));
out_vtan = normalize(out_vtan - nn*dot(nn, out_vtan));

 

the nn*() term is the projection of the original gradient vector to the normal, and when you subtract the projection from the original (aka the rejection) you get the orthogonal part of the tangent.

Link to comment
Share on other sites

Hi! Thanks for your answer. The geometry is a polygonal sphere with UV unwrapped and nothing else. I know because eventually I want to get the difference between the normal and a combed attribute (furdirection) in tangent UV space (I think it's called a "flow" map?). So I need to make sure the UV bases are really tangent to the normal before projecting the combed attribute to them. And I'm in no luck: even with the "correction", I'm still not getting truly tangent bases:

vector utan = gradient(s);
vector vtan = gradient(t);
vector outan = normalize(utan - N*dot(N, utan));
vector ovtan = normalize(vtan - N*dot(N, vtan));
float pu = dot(outan, N);
float pv = dot(ovtan, N);

Neither pu nor pv is ever equal to zero. Is there something really big I'm missing and I need to go back to late high school linear algebra? Or maybe go home and just rethink my whole life? ;-)

Edited by up4
Link to comment
Share on other sites

I think something more significant is at play here than just floating point arithmetics. Please refer to attached hip. There are two ROP in the file. "mantra1" renders the sphere with default normals (perpendicular to the surface). This should be R128G128B128 (0 = 128 as per vopsurface1 inline code to keep values in visible spectrum). But is is not even close. "mantra2" renders the combed normals on the same geo.

 

There must be something big I'm not doing right here… Help! ;-)

post-12164-0-66151400-1418064457_thumb.p

post-12164-0-65053600-1418064485_thumb.p

combed_normals.hipnc

Edited by up4
Link to comment
Share on other sites

Yeah, the big thing here is that mantra operates in camera space. So the N in the shader is very different from the N attribute on the geometry. A common pitfall, I suppose everyone gets hit by that at some point:)

 

Here's a a partially fixed hip. The tangents from uvs seem a little weird still. The intrinsic object/world options in the Compute Tangents VOP seem to give prettier-looking results, but if you're aiming to use these maps outside of Houdini then you probably do need the uv gradients.

 

combed_normals_ee.hipnc

Link to comment
Share on other sites

Hi eetu!

 

So I will try to serialize this to VEX code and see what is going on. The output of mantra1 seems fine, but the one from mantra2 seems a little off indeed (it is not possible to paint such a "square" pattern, I think, see attached image).

 

I need to understand what is going on under the hood because (well for one, I'm curious, but also because…) I need to use those maps outside of Houdini. I need the 2 channels to mean left/right and up/down with respect to a tangent UV plane. I want whoever is going to paint this to be able to understand the map when stacked on top of the color textures and maybe paint/retouch them in photoshop. And I need to master this because I will need to reload everything back into Houdini geo attributes.

 

Will be back soon with the flattened VEX…

post-12164-0-97533000-1418089830_thumb.p

Edited by up4
Link to comment
Share on other sites

Plus, I just realized that the camera to object transformation is exactly what Dany suggested. So, I'll try to produce simplified code and look into exactly what seems off with this solution, unless someone else can tell…

Link to comment
Share on other sites

Ah yes, I mean the jagged edge where something flips (circled in green), I think the tangents should be smooth and continuous within a uv-patch. Maybe the normal's y coordinate goes from positive to negative or something, and that causes a flip somewhere - or something similar.

post-2678-0-91182800-1418109110_thumb.pn

Edited by eetu
Link to comment
Share on other sites

I happen to be working on something related, so I spent quite some time banging my head into the wall with this one. All told, I must have spent several days this year trying to get all sorts of tangent stuff working.. In the end it seems that gradient() doesn't really work well here (because of uv-rendering maybe?), but Du() and Dv() work better - if you take care to be in the correct transformation space for everything. So here's a version that works, or at least seems to work. I also made a quickie shader that tries to use the resulting maps, for verification purposes.

post-2678-0-82155500-1418172071_thumb.pn

combed_normals_ee3.hipnc

  • Like 1
Link to comment
Share on other sites

  • 2 weeks later...

Hi!

 

OMG, I think that I never sent the reply to his message. After I thought I posted it, I was actually worried that it was a little too fanboyish in the way that I was thanking you for your solution. I also said that I would take a couple of days to move to my new place and so back I am realizing this message never got out of my browser window.

 

So, first: THANKS SO MUCH!!

 

What I'm going to do now is to serialize it back in a VEX inline text to decorticate the logic. But I have a question first, if you don't mind. You are binding the uv and NORIGINAL from the geometry to the shading context. Could the N (or Ng) and s,t, be used instead, with the proper coordinate transform? Also, now that I think of it, shouldn't dPds and dPdt be tangent to the surface in the direction of the UV coordinates too? If so, could they be used directly as well instead computetan?

 

The furdirection attribute map creation workflow is the last step I need to work out before I can have the fur system generate hair such as these (but also overall male body hair) without the use of guide curves, only attribute maps. Because the furdirection is the only essential fur attribute that is not a float. For the other attributes, a mere bitmap editor is sufficient.

 

Take care. Happy Holidays!

 

Vincent

Link to comment
Share on other sites

  • 3 weeks later...

What I'm going to do now is to serialize it back in a VEX inline text to decorticate the logic. But I have a question first, if you don't mind. You are binding the uv and NORIGINAL from the geometry to the shading context. Could the N (or Ng) and s,t, be used instead, with the proper coordinate transform? Also, now that I think of it, shouldn't dPds and dPdt be tangent to the surface in the direction of the UV coordinates too? If so, could they be used directly as well instead computetan?

 

Yeah, you could probably use s and t as well, you would probably need to do the extra cross product to ensure that your base is orthogonal. For some reason I presumed you wanted to take the maps outside of Houdini and use them with some other app/engine, so I thought having the u and v directions as the base would be the standard way of defining it. If you're keeping everything inside Houdini, you can of course use any base you like, as long as you do it the same way when both creating and applying your map.

 

I haven't followed 100% what you are aiming to accomplish in the end, but at first glance this seems to be a rather convoluted way of storing the vectors if you're indeed working inside Houdini. 

Link to comment
Share on other sites

You presumed right. I want something that is "clean" from a "content authoring" standpoint (i.e.: something that is tangent to the surface with regards to UV mappings) so that the "hair system" maps thus created can be superimposed over the skin color maps in the painting program. And store/archive the characters in 100% open standard files (EXR+OBJ). But I want it to be as simple as possible within Houdini as well (especially since we will be in-between H13 and H14 for a while).

 

I guess it is me that assumed that s,t were in the same direction as u,v. But if they are not, then I do not understand the difference between s,t and u,v correctly. I will RTFM a bit more, then.

 

Thanks,

 

V

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