Community
Arnold for Cinema 4D Forum
Rendering with Arnold in CINEMA 4D using the C4DtoA plug-in.
cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

Triplanar projection with three maps or object space normal shader?

12 REPLIES 12
Reply
Message 1 of 13
ulrichGTAV3
735 Views, 12 Replies

Triplanar projection with three maps or object space normal shader?

Hey there,

I need to shade a lot of different wheels with different track profiles. I'd like to differentiate between wheel tread and the sides. In Redshift I used a triplanar projection node, since I could map different textures to the three different dimensions. As I see it, in arnold triplanar you can have only one map. So I tried to map R, G and B of the utility shader with "Ns". But this is in world space, not in object space. So when I rotate the wheels in space those maps change.

So:

A: Is there a way to have 3 different maps in the triplanar projection node?

Or:

B: Can I get normal shading in object space?

Thank you very much!

Tags (1)
Labels (1)
12 REPLIES 12
Message 2 of 13
ulrichGTAV3
in reply to: ulrichGTAV3

PS: Also tried state_vector. Also in world space?

Message 3 of 13
madsd
in reply to: ulrichGTAV3

It's possible, I can update my OSL triplanar map to accept 6 inputs, 3 inputs.
But I would need a good reason to do it.

You can also do these things with Core Arnold maps, you dont need the triplanar map, instead build up 3 paths with 3 UV Transforms and 3 Normal directions.

Po ( Point Object ) needed if you dont want drifting.

Message 4 of 13
ulrichGTAV3
in reply to: ulrichGTAV3

Dear Mads,

sorry. I'm not sure I can follow... I don't know your OSL shader. And I don't know what you mean by "a good reason". Anyway... The solution with 3 normal directions is what I tried this afternoon. My problem was "So when I rotate the wheels in space those maps change.". thats what you call "drifting", if I understand you correctly. I cannot see how I can use "Po" to counteract that drifting.

Thank you very much...

Message 5 of 13
madsd
in reply to: ulrichGTAV3

I could write an update to my OSL Triplanar here, if you wanted to cut a small gem and put it in my purse.
https://github.com/gkmotu/OSL-Shaders/blob/master/Triplanar.osl

If you want to do this with pure arnold nodes, I build a quick logic you need to follow.
Here I show the top down projection of an input, a texture.
It follows, and does not drift since I probe Po instead of worlds space P.

I intentionally dehooked the -N stream so its only top down here, not both topdown and down top, to get both directions covered on the sphere.

qq.gif

Here is the noodle I threw together and what you will need to build your own triplanar logic with core nodes.

Notice we negate the Normal to -N for the backface logic to patch up the other side.
You will need to generate the same for the 2 other axis.

Note you can also just multiply the normal mask with the RGB(A) data to mask each of the 3, dual sided normals.

qwe.png


Update:

Use this instead...this works 100% like you want.

1.png

shader Normal
[[ string help = "Get the Normal in the specified coordinate space",
   string label = "Normal",
   string category = "Scene Attributes" ]]
(
    string Coordspace = "object" 
        [[ string widget="popup",
           string help = "world, object, camera, shader, screen, NDC, raster, or an explicitly named coordinate system",
           string options="world|object|camera|shader|screen|NDC|raster",
           int editable=1 ]],
    output vector Out = 0.0,
    output float tt = 0,
)
{
    Out = transform(Coordspace, N);
    
    tt = Out[2];
}


Message 6 of 13

"B: Can I get normal shading in object space?"

Use the space_transform shader.

Message 7 of 13
ulrichGTAV3
in reply to: ulrichGTAV3

@Peter: I tried. In any configuration drifting was still happening.

@ I'm still about to test your solutions. Just occupied with an other project. Just fyi.

Message 8 of 13
ulrichGTAV3
in reply to: ulrichGTAV3

Dear @Mads Drøschler,

I hacked your triplanar.osl (*duck and run*)

I do not really know C++ (only Java, JS, Python and a little bit of C#) and OSL is completely new to me.

So I think this would be very unstable since there is no failsafe for missing textures:


// Triplanar mapping
// Triplanar.osl, by Mads Drøschler
// Modified: 02-28-2021
// License: MIT Copyright Mads Drøschler

// ID and random generator
float ID(int InstanceMode, int RandFlip)
{
float id;
string ID = "theThing(tm)";
getattribute("nodeName", ID);
int Hash = hash(ID);
InstanceMode < 1 ? Hash = 0 : Hash = Hash;

return id = noise("cell", vector(abs(Hash), abs(RandFlip), 11));
}

// Texture
color Texture (string Texture, float x, float y)
{
return texture(Texture,x,1-y,"wrap", "periodic");
}

// Blend
float Blend (float blur, color BlendMap)
{
return mix(1,BlendMap[0],0.5)/(blur/10);
}

shader Triplanar
[[
string help = "<h3>Triplanar</h3>"
"Map surfaces automatically<br>"
]]
(
// Texture loader
string Tex1 = ""
[[
string widget = "filename",
string widget = "null",
int connectable = 0,
string help = "Load a texture to the Triplanar map."
]],
    string Tex2 = ""
[[
string widget = "filename",
string widget = "null",
int connectable = 0,
string help = "Load a texture to the Triplanar map."
]],
string Tex3 = ""
[[
string widget = "filename",
string widget = "null",
int connectable = 0,
string help = "Load a texture to the Triplanar map."
]],

// Blend map
color BlendMap = 1
[[
string widget = "null",
string help ="Pick a map to control the blend area. ( Use an OSL noise for example. ). This can help breaking up "
]],

//Debug
string UVC = "uvwunwrap\uv_checker.png"
[[
string widget = "filename",
string widget = "null",
int connectable = 0,
]],

int RandVal = 0
[[
string widget = "null",
int connectable = 0,
string help ="Enable to randomize the transforms on the object."
]],

int RandFlip = 12345
[[
string widget = "null",
int connectable = 0,
string help ="Random seed for the transform values."
]],

string Override = "Non"
[[
string widget = "null",
int connectable = 0,
string widget ="popup",
string options = "Non|UVMap|RGB",
string help ="Pick an override mode for debug purposes."
]],

float blur = 1.0
[[
string widget = "null",
int connectable = 0,
float min = 0,
float max = 2,
string help ="Width of the blend area between the XYZ projections."
]],
int InstanceMode = 0
[[
string widget = "null",
int connectable = 0,
string help ="Enable pr Object/Instance random transforms."
]],

// Scale
int scaleUniform =1
[[
string widget = "null",
int connectable = 0,
string help ="Scale the XYZ projections uniformly."
]],

float scaleU = 100
[[
int connectable = 0,
]],

float scaleV = 100
[[
int connectable = 0,
]],

float scaleU_max = 0
[[
string widget = "null",
int connectable = 0,
]],
float scaleV_max = 0
[[
string widget = "null",
int connectable = 0,
]],

// Position
int PosUniform =1
[[
string widget = "null",
int connectable = 0,
string help ="Offset the XYZ projections uniformly."
]],

float PosU = 0
[[
string widget = "null",
int connectable = 0
]],

float PosV = 0
[[
string widget = "null",
int connectable = 0
]],

float PosU_max = 0
[[
string widget = "null",
int connectable = 0
]],

float PosV_max = 0
[[
string widget = "null",
int connectable = 0
]],

// Rotation
int RotUniform  =1
[[
string widget = "null",
int connectable = 0,
string help = "Rotate the XYZ projections uniformly."
]],

float Rotx = 0
[[
string widget = "null",
int connectable = 0
]],

float Roty = 0
[[
string widget = "null",
int connectable = 0
]],

float Rotz = 0
[[
string widget = "null",
int connectable = 0
]],

float Rotx_max = 0
[[
string widget = "null",
int connectable = 0
]],

float Roty_max = 0
[[
string widget = "null",
int connectable = 0
]],

float Rotz_max = 0
[[
string widget = "null",
int connectable = 0
]],

// General inits
float Gamma = 2.2
[[
string widget = "null",
int connectable = 0,
string help = "Set the texture gamma, default 2.2."
]],

output color ColRGB = 0
[[
string label = "Col(RGB)",
string help ="Output of the Triplanar map."
]],
)
{
// Skip empty files
if (Tex1 == "")
return;


// Various inits
point uv = transform("object",P);
color BitX,BitY,BitZ;
point uvx, uvy, uvz;
vector x = vector(1,0,0);
vector y = vector(0,1,0);
vector z = vector(0,0,1);

// Blend area
float Blender = Blend(blur+0.07,BlendMap);

// Normal
vector Nor = pow(abs(transform("object",N)),Blender);
Nor = Nor/(Nor[0]+Nor[1]+Nor[2]);

// ID and Random generator
float ratio = ID(InstanceMode,RandFlip);

// Random Rotation
float RandX = mix(Rotx, Rotx_max, ratio);
RandVal < 1 ? RandX = 0 : RandX = RandX;
float RandY = mix(Roty, Roty_max, ratio);
RandVal < 1 ? RandY = 0 : RandY = RandY;
float RandZ = mix(Rotz, Rotz_max, ratio);
RandVal < 1 ? RandZ = 0 : RandZ = RandZ;

// Rotation
if ( RotUniform == 1)
{
uvx = rotate(uv, radians(Rotz-90+RandZ),0,x);
uvy = rotate(uv, radians(90+-Rotz+RandZ),0,y);
uvz = rotate(uv, radians(Rotz+RandZ),0,z);
}
else
{
uvx = rotate(uv, radians(Rotx-90+RandX),0,x);
uvy = rotate(uv, radians(90+-Roty+RandY),0,y);
uvz = rotate(uv, radians(Rotz+RandZ),0,z);
}

// Random Position
float RandX_pos = mix(PosU, PosU_max, ratio);
RandVal < 1 ? RandX_pos = 0 : RandX_pos = RandX_pos;
float RandY_pos = mix(PosV, PosV_max, ratio);
RandVal < 1 ? RandY_pos = 0 : RandY_pos = RandY_pos;

// Position
if (PosUniform == 0 )
{
uvx[2] +=PosU+0.5+RandX_pos;
uvx[1] -=PosV+0.5+RandY_pos;
uvy[0] -=PosV+0.5+RandY_pos;
uvy[2] +=PosU+0.5+RandX_pos;
uvz[0] -=PosU+0.5+RandX_pos;
uvz[1] -=PosV+0.5+RandY_pos;
}
else
{
uvx[2] +=PosU+0.5+RandX_pos;
uvx[1] -=PosU+0.5+RandX_pos;
uvy[0] -=PosU+0.5+RandX_pos;
uvy[2] +=PosU+0.5+RandX_pos;
uvz[0] -=PosU+0.5+RandX_pos;
uvz[1] -=PosU+0.5+RandX_pos;
}

// Random scale
float RandX_scale = mix(scaleU, scaleU_max, ratio);
RandVal < 1 ? RandX_scale = 0 : RandX_scale = RandX_scale;
float RandY_scale = mix(scaleV, scaleV_max, ratio);
RandVal < 1 ? RandY_scale = 0 : RandY_scale = RandY_scale;
float RandZ_scale = mix(scaleU, scaleU_max, ratio);
RandVal < 1 ? RandZ_scale = 0 : RandZ_scale = RandZ_scale;

// Scale
if ( scaleUniform == 0 )
{
uvx[2] /= scaleU;
uvx[1] /= scaleV;
uvy[2] /= scaleU;
uvy[0] /= scaleV;
uvz[0] /= scaleU;
uvz[1] /= scaleV;

uvx += RandX_scale;
uvy += RandY_scale;
uvz += RandZ_scale;
}
else
{
uvx /= scaleU+RandZ_scale;
uvy /= scaleU+RandZ_scale;
uvz /= scaleU+RandZ_scale;
}

Override == "UVMap" ? BitX = Texture(UVC,uvx[2],uvx[1]) : BitX = Texture(Tex1,uvx[2],uvx[1]);
Override == "UVMap" ? BitY = Texture(UVC,uvy[2],uvy[0]) : BitY = Texture(Tex2,uvy[2],uvy[0]);
Override == "UVMap" ? BitZ = Texture(UVC,uvz[0],uvz[1]) : BitZ = Texture(Tex3,uvz[0],uvz[1]);

// Map to projections
BitX *=Nor[0];
BitY *=Nor[1];
BitZ *=Nor[2];

// Collect
ColRGB = pow(BitX+BitY+BitZ,Gamma);

// RGB Debug
Override == "RGB" ? ColRGB = Nor[0]*x + Nor[1]*y + Nor[2]*z :0;
}

Seems to be working for me so far...


What would be the appropriate input type in OSL if I wanted to plug in an other arnold node (like ramp or noise) instead of loading a texture?


Thank you so much!


triplanarthreeinputstest.jpg

Message 9 of 13
madsd
in reply to: ulrichGTAV3

You cannot push in data and post evaluate the UVW.
So while you can open raw color channels, you wont be able to post align them, it has to occur at the source.

This means your ramp or whatever must come with the correct orientation pr call.

So you need to bake out the input. Render to texture.
Alternatively, forward both a color and a UVW to the triplanar map instead of just the color.

Message 10 of 13
ulrichGTAV3
in reply to: ulrichGTAV3

I guess you are referring to this:

"What would be the appropriate input type in OSL if I wanted to plug in an other arnold node (like ramp or noise) instead of loading a texture? "

I will read more into OSL writing in order to fully understand your comment.

Thx...

Message 11 of 13
madsd
in reply to: ulrichGTAV3

Correct.
It's impossible to reproject a noise for example.
But a noise is typically living in worldspace, so it would always map correctly regardless of being parsed through the 3 normal filters.

But, converting a 2D procedural is not possible, it must live as a field expression. ( and then it gives little meaning to parse through a triplanar if its living in world space )

Message 12 of 13
ulrichGTAV3
in reply to: madsd

I understand (I think). I was only asking, because all I needed was black and white for the different planes. I quickly created a black and a white png for that. As I said: thats working for me now. But OSL is something I will dive into some more when there is time...

Message 13 of 13
madsd
in reply to: ulrichGTAV3

You should, it's quite an awesome TD tool + things that generate eye candy.


You can join my Facebook group for OSL.
2,600 people.
Larry is there, a handful of Autodesk personel, lots of highend TDs.
https://www.facebook.com/groups/OSL.Shaders

Can't find what you're looking for? Ask the community or share your knowledge.

Post to forums  

Autodesk Design & Make Report