Help with mi_call_material

Discussions concerning programming of SOFTIMAGE©
Post Reply
User avatar
wbahnassi
Posts: 3
Joined: 14 Oct 2019, 20:20
Contact:

Help with mi_call_material

Post by wbahnassi » 14 Oct 2019, 23:58

Hi,

I'm trying to write a "decal shader" for mentalRay. The idea is simple. This shader will look if the current location is covered by a decal object, then it will output a color given by the material set on the decal object instead of the actual object on which the decal shader is applied. This allows you to add details without making unique textures. However, I'm currently struggling with successfully calling mi_call_material to evaluate the replacement material. Here is what I do:
  1. Get an miTag for the decal object instance.
  2. Use mi_query() with miQ_INST_MATERIAL to get the decal object material.
  3. Build a new miState as a primary ray and set the material member to the miTag from the line above.
  4. Call mi_call_material() with the new miState. <-- crash
I juggled the values I fill in the new miState a bit, and the best I was able to achieve was mi_call_material to fail with miFALSE instead of crashing.

Code: Select all

	// decalObj is a valid miTag referencing the decal object instance
	miTag objMat = miNULLTAG;
	mi_query(miQ_INST_MATERIAL, nullptr, decalObj, &objMat);

	// Prepare the decal material state
	miState decalState = *state; // Start with a copy of the state passed to this shader
	decalState.parent = nullptr;
	decalState.reflection_level = 0;
	decalState.refraction_level = 0;
	decalState.face = 'f';
	decalState.material = objMat;
	decalState.volume = miNULLTAG;
	decalState.environment = miNULLTAG;
	decalState.refraction_volume = miNULLTAG;
	mi_query(miQ_INST_LABEL, nullptr, decalObj, &decalState.label);
	decalState.instance = decalObj;
	decalState.pri = nullptr; // Primary ray
	// The fields commented below to be filled with proper math
	//decalState.bary[0] = decalState.bary[1] = decalState.bary[2] = 0.0f;
	//decalState.tex_list;
	//decalState.bump_x_list;
	//decalState.bump_y_list;
	decalState.tex = uv;
	decalState.shader = nullptr;
	decalState.child = nullptr;

	bRetval = mi_call_material(result, &decalState);
So, am I trying to achieve something not supposed to be done? I feel my problem is that the values I fill in the new miState are confusing mentalRay.. I wonder if the decalState.shader member must be set to something other than null, but then again, I'm not sure how to fill it or if this is truly the problem.

Any help is much appreciated!

Best

User avatar
rray
Moderator
Posts: 1775
Joined: 26 Sep 2009, 15:51
Location: Bonn, Germany
Contact:

Re: Help with mi_call_material

Post by rray » 15 Oct 2019, 01:09

Welcome to the forum wbahnassi. I vaguely remember from writing the tracer shader (see resource section) that mental ray didn't like states I created myself, it just let me modify states that it gave to me. It's been over 10 years though so this info might be totally off.

What might be possible then is if you have the decals hover a bit over the surface, then use maybe mi_trace_probe to get info on the object below, and modify the states before/after the mi_trace_probe call? Don't really know if this would do any good also no further idea atm. Will post more if/when memory comes back 📡
softimage resources section updated Jan 5th 2024

User avatar
wbahnassi
Posts: 3
Joined: 14 Oct 2019, 20:20
Contact:

Re: Help with mi_call_material

Post by wbahnassi » 16 Oct 2019, 16:35

Thanks a lot rray. Indeed, the state returned by mi_trace_probe is a usable one for mi_call_material. I just had to modify the ray type in the state passed to mi_call_material() first, since the state->child filled by mi_trace_probe() has a ray type of miRAY_PROBE.
What might be possible then is if you have the decals hover a bit over the surface, then use maybe mi_trace_probe to get info on the object below
So the way I'm making this shader is kinda the reverse of that. The decal object is basically a box. You resize/orient this box as needed and all objects that fall within this box will get a decal projected on them along the Z axis of the decal box. But in order for an object to accept receiving decals, it has to use a "Decal Filter" shader node within its shader tree. This shader node is usually ineffective and just passes through the input to the output, except when this shader detects that the current sample falls within some decal box in the scene, and if so, it will evaluate the decal box material and return its color instead of acting as a pass-through.

Now with mi_trace_probe() I probe from the object's surface and outside along the hit point normal. If the point is contained within a decal box, I should eventually hit this box from the inside (state->inv_normal == 1), but I don't like this solution because I'm using a ray intersection to check if a point is within a box, and I might hit several other objects before reaching the decal box (if at all)...
I can think of two solutions here:
  1. Somehow control mi_trace_probe() to only return hits on decal boxes and ignore all other objects in the scene (I have no idea how to do this).
  2. Manually query the sample pos against a database of decal boxes using fast point-in-box tests.
I currently do (2) with the list of decal boxes passed in as an array parameter to the Decal Filter shader. This way you can control the accept list per-object (or per-material actually), but I like to offer a fully automatic solution as well without an explicit list of boxes, so I wonder what would be a good way to gather all decal boxes from the scene in a list that the shader can iterate on..
The decal boxes are modeled as regular boxes in XSI and they are effectively invisible.

Ideas?

Best!

User avatar
rray
Moderator
Posts: 1775
Joined: 26 Sep 2009, 15:51
Location: Bonn, Germany
Contact:

Re: Help with mi_call_material

Post by rray » 17 Oct 2019, 12:13

No good idea atm. For the WireD shader I used a UserDataBlob with the object's polygon topology information that was updated with a compiled op. This was passed to the MR shader as binary user data. That might work for objects too, but it wouldn't be fully automatic either.

I guess Mental ray should have the information somewhere in its scene data already, but I never looked into how mental ray accesses its scene or would do filtering based on some properties. The custom box lookup sounds like it would be the fastest once a solution was found.
softimage resources section updated Jan 5th 2024

mantom
Posts: 10
Joined: 21 Nov 2014, 07:55

Re: Help with mi_call_material

Post by mantom » 24 Nov 2019, 01:46

Last time I did anything along these lines was XSI v4.2. So forgive any inaccuracies from rust.

The only full automated way I can think of from within mental ray's API would be through the use of geometry shaders. Geometry shaders are called before rendering begins and have ability to traverse the scene graph. This means it should only have to be called once, and should be able to do all the inside/outside tests and record the results somewhere in the scene as userdata. The trick will be in passing the userdata location to the decal shaders. I don't know if it's even possible, but the geometry shader may be able to apply the userdata to other objects in the scene. If so, then it can apply it to the objects with decal shaders assigned. This would allow the decal shaders to check it's own userdata in it's init() function to know if it must perform any decal shading, or simply pass the color through and exit. Not a simple path, but may be possible.

One potential problem is your geometry shader might be called before all the geometry in the scene is loaded/defined. In which case some objects may be inaccessible. Another potential problem is data created by the geometry shader may be deleted when the geometry shader terminates and before your decal shader is called (I don't think that will happen, but it's possible).

A more crude solution:

Create an siOnBeginRenderPass() event script to apply a UserDataBlob on all objects in the scene with decal shaders assigned, then record relevant decal box location and Z-axis vector in the UserDataBlob.
When scene is pushed to mental ray to render, the decal shader checks it's userdata during init() when called. If UserdataBlob exists, then decal shading takes place using parameter values.
When render completes, siOnEndRenderPass() event script executes and deletes all the UserDataBlobs created pre-render.

this would feed only the relevant decal boxes to the shaders and avoid need for mi_trace_probe().

Matt

User avatar
wbahnassi
Posts: 3
Joined: 14 Oct 2019, 20:20
Contact:

Re: Help with mi_call_material

Post by wbahnassi » 07 Dec 2019, 16:59

Thanks mantom for the suggestions about the geometry shader. I was spending my time recently getting the actual rendering to look correct as opposed to accelerating the decal box lookups. So for now I'm still using mi_trace_probe, but I will try to move to something better now that the whole system is working.

Here is a screenshot of the thing in action:

Image

Notable things:
  • The decal recepient is only expected to use a DecalFilter shader in its shader tree. Other than that, nothing is needed, not even any UVs. The decal will provide its own UVs wherever it appears. If you don't want an object to receive decals, simply don't put a DecalFilter shader within its material.
  • The decal box material has to use a special "DecalHide" shader. This does two things in reality: it hides the box itself from rendering. Plus, it packs the data to be consumed by DecalFilter. Without this, an object won't act as a decal projector.
  • To override the normals via the decal, you can't use XSI's standard bump map node because it requires actual UV projections to be present on the mesh (assuming you rely on UVs to sample your bump info). In this system I don't generate a standard UV projection because I don't operate in a geometry shader. Instead, the decal UVs are fed in the state's "tex", which XSI's bump node seems to ignore. The "DecalBump" node is an exact replica of XSI's standard bump but it supports operating on "tex" in addition to standard UV projections.
  • The decal parameters are built around the architectural shader because that's what we're using in our project right now, but it can be modified of course to handle any other parameters you like to "decal" in addition to those already offered.
I still have to improve the workflow as well as improve efficiency, but at least it's already usable as it is now. Thanks again to rray for your help, and for homam for prepping the screenshot :)

Post Reply

Who is online

Users browsing this forum: No registered users and 54 guests