cloth intersection shader


#1

hey you shader programmers!

i’m currently working with cloth stuff and we (like most of the cloth-doing people) have the problem that at some frames objects which should stay under the cloth (body or a second layer of cloth) come through the upper cloth layer.

i was wondering if it is possible to write a shader (we’re using maya -> mental ray) that helps with that problem. first the user has to tell it which object is on the top, second and last layer (like coat > shirt > body). then it checks when the camera ray collides with the first object. then it let’s the ray pass through for a given length (like .5 units threshold) and if it finds any other of the given objects in this range it checks the given stack (top,second,last) which object to take for shading (top -> first, last -> last).

so if the ray intersects the shirt object first, it travels on and if it intersects the coat objects in the given range it takes the coat for shading. so the intersection errors of the cloth would be solved …

i hope this was some kind of understandable. sorry for my english!

i’d really be interested if someone knows if this kind of shader is possible with maya/mr/rm-shader-programming.


#2

Not too terribly complicated. I’ll give it a shot.

EDIT:
Okay, here you go. Only supports 3 layers right now. And it isn’t currently connectable to maya shader nodes - but it’s easy enough to get around that.
Also, the labels refer to miLabels that you’ll have to add to your objects. You can use this MEL command to add the attribute:

addAttr -ln miLabel -at long pSphere1; (just as an example)

You’ll find the miLabel attribute under the base object node, not the shape node. Give the objects on each layer a different number, and match that number accordingly in the shader. I’m sure it’d be possible to automate the application of object labels, but I didn’t have time to try anything like that.
Works just as you described it: If the object that the eye ray hits isn’t the top layer, mr keeps tracing (actually traces inwards along the interpolated normals). It should work such that if your cloth geometry was getting mixed up so much that you saw the layers in the order of bottom->middle->top, or even bottom->middle->bottom->middle->top, you’ll still get the top layer (provided that the layer is within the threshold).
Start with low (.1 or less) threshold values. Otherwise the threshold may go beyond the range of your object.

Anyway, should be working, but let me know if I missed something.


#3

hey vormav!
you’re THE man! i thought that i may get some tips or ideas or just something like “no sorry, that shouldn’t work” but never ever i expected someone would sit down and code this thing for me! thank you.
so i’ve created a MI-file, linked everything and created a scene as you described.
in maya 7.0 the shader crashed without showing anything, in maya 6.5 it worked but didn’t bring up the desired results.
i’ve put two spheres inside each others. one just a bit smaller than the other. then i definded the inner as layer 0 (top) and the outer as 1 (bottom). normally it should now have shown me the inner sphere over the outer after setting up the mr-node. right?! it showed only the outer one.
i’ve put the scene and the mi-file in the attachment. maybe you could have a look on it.
it would also be important that the ray traces straight and doesn’t change its direction.
thank you again!


#4

Yeah, it’s really only half-working right now: If a middle layer object sticks out in front of the top layer, or if the bottom layer object sticks out in front of the middle or top layer (within the threshold), then that point is just shaded with the top (or middle) layer’s shader. I call it a half-fix because, while it won’t hide the offending geometry, it will at least allow it to blend in; whereas it might be extremely obvious when an inner layer is sticking out if it’s shaded differently, this will just make it look more like the piece that’s sticking out is just a part of that outer layer.
Actually hiding the geometry is going to take a little bit more work. I won’t have time to do more with it for a couple days, but I’ll post the source code if you want to mess around with it.


#include <shader.h>
#include <geoshader.h>

struct cloth_layers {
	miColor top;
	int top_object_label;
	miColor middle;
	int middle_object_label;
	miColor bottom;
	int bottom_object_label;
	miScalar threshold;
};

DLLEXPORT int cloth_layers_version(void) {return(1);}

DLLEXPORT miBoolean cloth_layers (
	miColor *result,
	miState *state,
	struct cloth_layers *paras)
{
	//declare variables
	int top_object, middle_object, bottom_object;
	miScalar threshold;
	double distanceCovered = 0;
	miVector normalGeom = state->normal;	//store state->normal in another variable. This is only necessary to avoid modifying state variables when mi_vector_neg() is called later on
	miBoolean satisfied = 0;
	miBoolean middleHit = 0;
	miBoolean bottomHit = 0;
	int label;

	/*the first test is whether or not the eye ray has hit the top layer
	So, evaluate the tag of the top layer. It is not necessary at this point to
	evaluate the tags of either the bottom or middle layer (slight optimization)*/
	top_object = *mi_eval_integer(&paras->top_object_label);	//get the label of the object the eye ray has hit
	if(mi_query(miQ_INST_LABEL, state, state->instance, &label)) {
		if(label == top_object) {	//if already at the top layer, we're done
			*result = *mi_eval_color(&paras->top);
		} else {
			//the eye ray hasn't hit the top layer , so check to see if it's the middle or bottom
			middle_object = *mi_eval_integer(&paras->middle_object_label);
			bottom_object = *mi_eval_integer(&paras->bottom_object_label);
			threshold = *mi_eval_scalar(&paras->threshold);
			mi_vector_neg(&normalGeom);	//reverse direction of normalGeom to give a vector pointing directly inwards, for further raytracing
			if(middle_object == label) {
				middleHit = 1;
			}
			if(bottom_object == label) {
				bottomHit = 1;
			}
			if(threshold > 0) {		//optimization; the shader isn't coded to handle negative thresholds, and a threshold of zero requires no further tracing. Otherwise, tracing is necessary
				state->child->point = state->point;
				while(!satisfied) { //Trace until we've either gone past the threshold, or trace_probe doesn't hit anything
					if(mi_trace_probe(state, &normalGeom, &state->child->point)) {
						distanceCovered += state->child->dist;	//distanceCovered is the distance from the original shading point (from the eye ray) - need to add the new tracing distance each time
						if(distanceCovered > threshold) {	//gone over the threshold, finished with the loop
							satisfied = 1;
						} else {
							if(mi_query(miQ_INST_LABEL, state, state->child->instance, &label)) {	//query for the label of the new point hit by secondary tracing
								if(label == top_object) {	//if we find the top layer within the threshold, we're done
									*result = *mi_eval_color(&paras->top);
									return(miTRUE);
								}
								if(label == bottom_object && !middleHit) {
									bottomHit = 1;
								}
								if(label == middle_object) {
									middleHit = 1;
									bottomHit = 0;
								}														
							} else {
								return(miFALSE);
							}
						}
					} else {	//trace_probe didn't hit anything. we're done with the loop
						satisfied = 1;
					}
				}
			}
			if(middleHit) {		//if middleHit has been checked, return the middle layer
				*result = *mi_eval_color(&paras->middle);
			} else if(bottomHit) {	//if middleHit isn't checked, but bottomhit is, return the bottom layer
				*result = *mi_eval_color(&paras->bottom);
			}
		}
	} else {	//if mi_query fails, return false. This shouldn't happen.
		return(miFALSE);
	}
	return(miTRUE);
}

Also, I really think that you should be using the interpolated normals for this instead of the eye ray direction. The example you gave with the two spheres is perfect for showing why. I’m attaching an image that illustrates this. But if you were to do the secondary tracing directly from the eye ray, in examples like your top-layer sphere within a bottom-layer sphere setup, the bottom layer would be clearly visible as you begin to reach the outside of the sphere.
The only problem that really arises from using the normals is that these aren’t always going to point straight inwards. But for the vast majority of cases, I think it’ll be close enough.


#5

sorry, but i don’t have any experience coding mr-shaders - so i’ll wait till you have time. but don’t hurry … it isn’t too urgent.
thanks again!


#6

Vormav,

Your conditionals might cause aliasing problems. How about using cubic interpolation between those states to get a non-zero derivative as the renderer sweeps through the surfaces?

-M

#7

Hey. I’ve been thinking it over for awhile, but I can’t quite figure out why those conditionals you mentioned would cause aliasing issues (since you’re not actually evaluating at the secondary trace point - just using it to decide which shader to return at the primary ray point. Unless it’s related to the floating point accuracy of the distance value from the secondary ray-tracing, perhaps?). Potential breaks in the surface shading when the threshold setting isn’t sufficient, but I don’t know about aliasing issues. Would you mind elaborating on that a little bit?
Also, when you suggest interpolating between the states, how exactly do you mean to interpolate between them? You don’t mean to sample multiple rays from the primary intersection point and interpolate between those, do you?
The best thing I can think of is that, in some cases where an inner layer is going beyond the outer layer - and doing so in a wierd, twisted fashion wherein the inverted normals don’t necessarily point to the inside of the mesh (very possible in a cloth simulation), then yeah, that’ll create nasty artifacting, which could be fixed - to some degree - by multi-sampling secondary rays and interpolating between those samples. But…just want to see if I’m understanding the suggestion correctly - or if I’m even close for that matter.

Also, your pm box is full. :wink:


#8

Ahh… disregard then…:wink: I didn’t study your code enough to know exactly what it was doing. I just looked at all those conditionals and then my instincts took over. :bounce:

-M


#9

This thread has been automatically closed as it remained inactive for 12 months. If you wish to continue the discussion, please create a new thread in the appropriate forum.