Author Topic: Shader: Glitter / Sequin (Pt.2)  (Read 2087 times)

2020-01-22, 07:14:24
Reply #15

pokoy

  • Active Users
  • **
  • Posts: 1507
    • View Profile
If you're on max 2019+ you can also try the glitter OSL shader that chaosgroup provides, google it.

2020-01-22, 07:57:04
Reply #16

cjwidd

  • Active Users
  • **
  • Posts: 607
    • View Profile
    • Artstation

2020-01-22, 08:48:41
Reply #17

pokoy

  • Active Users
  • **
  • Posts: 1507
    • View Profile
Bummer. It saved the day for me on a material that needed a glitter effect. Whatever you end up with, it should probably be a normal map, not bump, since the glitter particles have to have different orientation. Bump can't do this in the same way a normal map can.

2020-01-22, 10:02:33
Reply #18

niljut

  • Active Users
  • **
  • Posts: 13
    • View Profile
This is the setup I used. Going to try the Chaosgroup shader!


2020-01-22, 10:08:06
Reply #19

cjwidd

  • Active Users
  • **
  • Posts: 607
    • View Profile
    • Artstation
I'm not very familiar with this map: OSL noise. Is this a Max 2019+ feature or is it available in pre-2019 release?

I thought the OSL additions were only 2019+(?)

2020-01-22, 10:18:21
Reply #20

niljut

  • Active Users
  • **
  • Posts: 13
    • View Profile
That is probably the case, I'm on 2020. it's called OSL Noise (Gabor).

2020-01-22, 11:28:42
Reply #21

pokoy

  • Active Users
  • **
  • Posts: 1507
    • View Profile
Sorry, it's called flakes, not glitter, link
https://docs.chaosgroup.com/display/OSLShaders/Flakes+normal+map

You can probably use a (OSL) noise, but the good thing about this OSL shader is it creates circles and tints them with random RGB values which gives each circle a different orientation. Might be possible with bump to some extent but then you need to take care of various maps at the same time - the noise that produces the gradients and the masking which 'cuts out' the flakes... it's way more finicky...

2020-01-22, 13:33:22
Reply #22

niljut

  • Active Users
  • **
  • Posts: 13
    • View Profile
Here's a version based on my old shader using the Chaos Group's OSL flakes shader for random normal directions.



And here I'm substituting the OSL Noise (Gabor) shader entirely for the Chaos Group flakes one.


There are pros and cons to each one. Using only the Chaos Group Flakes shader has the benefit of the flakes lining up with the normal directions, the con is that the colors of the flakes are the same as their normal direction, so all the flakes with left-facing normal directions will have the same color.

I think I prefer the mix, the color variation is nice to have.

2020-01-22, 14:33:24
Reply #23

pokoy

  • Active Users
  • **
  • Posts: 1507
    • View Profile
In theory, you could key out each RGB component of the normal map and 'redirect' to a multi-colored noise, then comp again for the diffuse slot.
Also, for anyone digging a bit into OSL it would probably be easy to take the shape creator code and make it get random colors which could then be used as diffuse.

OSL really gives us some cool options now. It can't do everything but it can get us quite far... understanding OSL and some coding required, though.

2020-01-22, 17:29:57
Reply #24

niljut

  • Active Users
  • **
  • Posts: 13
    • View Profile
I ended up modifying the Chaos Group flakes shader to add a randomized color output. I have no experience with OSL so I mostly just plugged in something that seems to have worked. Changing the material parameters have such a large impact on the final result

Code: [Select]
shader
flakes
(
float flake_scale = 10.0 [[ string description = "Smaller values zoom into the flake map, larger values zoom out" ]],
float flake_size = 0.5 [[ string description = "Relative size of the flakes" ]],
float flake_size_variance = 0.0 [[ string description = "0.0 makes all flakes the same size, 1.0 assigns random size between 0 and the given flake size" ]],
float flake_normal_orientation = 0.0 [[ string description = "Blend between the flake normals (0.0) and the surface normal (1.0)" ]],

output color NormalResult = 1.0,
output color ColorResult = 1.0,
output float alpha  = 1.0
)
{

float safe_flake_size_variance = clamp(flake_size_variance, 0.1, 1.0);

vector cellCenters[9] = {
vector( 0.5,  0.5,  0.0),
vector( 1.5,  0.5,  0.0),
vector( 1.5,  1.5,  0.0),
vector( 0.5,  1.5,  0.0),
vector(-0.5,  1.5,  0.0),
vector(-0.5,  0.5,  0.0),
vector(-0.5, -0.5,  0.0),
vector( 0.5, -0.5,  0.0),
vector( 1.5, -0.5,  0.0)
};

point position = vector(u, v, 0.0);
position = flake_scale * position;

point base = floor(position);

point nearestCell = point(0.0, 0.0, 1.0);
int nearestCellIndex = -1;
for(int cellIndex = 0; cellIndex < 9; ++cellIndex) {
point cellCenter = base + cellCenters[cellIndex];

vector centerOffset = cellnoise(cellCenter) * 2.0 - 1.0;
centerOffset[2] *= safe_flake_size_variance;
centerOffset = normalize(centerOffset);

cellCenter += 0.5 * centerOffset;

float cellDistance = distance(position, cellCenter);
if(cellDistance < flake_size && cellCenter[2] < nearestCell[2]) {
nearestCell = cellCenter;
nearestCellIndex = cellIndex;
}
}

NormalResult = color(0.5, 0.5, 1.0);
ColorResult = 0.0;
alpha = 0.0;

if (nearestCellIndex != -1) {
vector randomNormal = cellnoise(base + cellCenters[nearestCellIndex] + vector(0.0, 0.0, 1.5));
randomNormal = 2.0 * randomNormal - 1.0;
randomNormal = faceforward(randomNormal, I, randomNormal);
randomNormal = normalize(mix(randomNormal, vector(0.0, 0.0, 1.0), flake_normal_orientation));
ColorResult = color(cellnoise(base + cellCenters[nearestCellIndex]));
NormalResult = color(0.5*randomNormal[0]+0.5, 0.5*randomNormal[1]+0.5, randomNormal[2]);
alpha = 1.0;
}
}


2020-01-22, 18:43:06
Reply #25

Br0nto

  • Active Users
  • **
  • Posts: 47
    • View Profile
Note: Noise size and contrast modified to emphasize the sparkle effect.
Flakes are achieved with similar technique as that which is described in papers. But instead of circular flakes, i'm using cell voronoi noise (C4D) which drives normal map in a bump channel of a metal layer for a carpaint layered material (below).

Burnin, would you mind posting your shader/scene? We're using C4D too, and while this sounds very similar to what we're working with, I'd love to see exactly how you have yours set up.

2020-01-22, 21:42:19
Reply #26

cjwidd

  • Active Users
  • **
  • Posts: 607
    • View Profile
    • Artstation
I REALLY do not want to upgrade Max right now - for obvious reasons - even if the OSL shader is doing more or less exactly what I need :/

Pokoy, it sounds like you are saying there might be a way to replicate the OSL shader behavior, but it would be more demanding to prepare?

2020-01-23, 16:05:48
Reply #27

pokoy

  • Active Users
  • **
  • Posts: 1507
    • View Profile
I have set up a demo for this in a way I'd do it. I'm not 100% happy but it might be helpful, a zip with the scene (Corona 5, Max 2018) and the result is attached.

Open Slate material editor.
The material is a CoronaLayer mtl:
- base layer - just a black matte
- glitter 1 layer
- glitter 2 layer
- glitter 3 layer
- coat layer

It's a multi color glitter mat here, but you can just disable the layers 'glitter 1' and 'glitter 2' for a single glitter layer.
The base is black for a reason. If you give it a light color, you will have to take care of the glitter materials too, they will appear too dark.
The coat is just a fully reflective material with no diffuse, masked by a fresnel map.

Some controls are exposed (only visible in Slate!), they're all on the left side:
- reflection colors for each glitter material (gold, purple, blue), these are corona color maps
- bump noise size, one bezier float for all glitter materials
- bump amount, one bezier float for all glitter materials
- reflection glossiness, one bezier float for all glitter materials
- glitter size, one bezier float for all glitter materials
- glitter spread, one bezier float for all glitter materials

The glitter material works like this:
Each glitter layer is a colored reflective material, masked with a cellular map set to produce small circles. Each has a noise map as bump to make sure the glitter catches reflections form different places (to mimic the glitter particles being scattered and rotated randomly). Bump noise size and glitter size/spread are related, if one of them is too big/too small you'll start to see the bump structure which is not good. So if you change the glitter size you probably need to take care of the bump noise size too.

Each of the noise maps for bump and the cellular maps for the layer mask need to have different coordinates, otherwise they'd all sit in the same place - so these maps need to be unique, not instanced. That's why they have a bezier float controller so you can set the respective parameter in one go for all 3 maps, without having to do dial in the same value 3 places each time you need to change the settings.

Then there's glossiness for the glitter materials, using perfectly sharp reflections will produce a lot of fireflies and not spread the reflection enough. Make sure to play with that.
And there's a falloff multiplied over the cellular map used for the layer masks. Without it, the glitter would get way too much reflections from the surroundings. Play with the output curve if you feel it's too much (or too little) reflection on grazing angles.

The problem with this setup - which would also be there with the OSL map - is that the geometry is flat and has no volume. I guess in reality glitter particles are scattered on a surface, so some of them will be facing the camera, some not. Here, they all map as a circle onto the geometry which makes them visible at all times - that's why the falloff in the composite node is needed. Also, in reality there's a coating over the glitter particles which causes the reflections of some glitter particles to be invisible to the viewer due to total reflection within/under the coat. So it's a pretty complex effect and doing it all on a 2D surface without any sophisticated ray simulation can't properly mimic it... I think.

The advantage of the OSL map is that it correctly 'rotates' each particle in a normal map and is not faked by bump, plus you need less knobs, it's all in one map. But it's also a 2D map, no volumetric effects so it is limited, too.

It's a rather finicky material so make sure to play with only one glitter layer first.
« Last Edit: 2020-01-23, 17:40:23 by pokoy »

2020-01-23, 16:07:13
Reply #28

pokoy

  • Active Users
  • **
  • Posts: 1507
    • View Profile
I ended up modifying the Chaos Group flakes shader to add a randomized color output. I have no experience with OSL so I mostly just plugged in something that seems to have worked. Changing the material parameters have such a large impact on the final result

Code: [Select]
shader
flakes
(
float flake_scale = 10.0 [[ string description = "Smaller values zoom into the flake map, larger values zoom out" ]],
float flake_size = 0.5 [[ string description = "Relative size of the flakes" ]],
float flake_size_variance = 0.0 [[ string description = "0.0 makes all flakes the same size, 1.0 assigns random size between 0 and the given flake size" ]],
float flake_normal_orientation = 0.0 [[ string description = "Blend between the flake normals (0.0) and the surface normal (1.0)" ]],

output color NormalResult = 1.0,
output color ColorResult = 1.0,
output float alpha  = 1.0
)
{

float safe_flake_size_variance = clamp(flake_size_variance, 0.1, 1.0);

vector cellCenters[9] = {
vector( 0.5,  0.5,  0.0),
vector( 1.5,  0.5,  0.0),
vector( 1.5,  1.5,  0.0),
vector( 0.5,  1.5,  0.0),
vector(-0.5,  1.5,  0.0),
vector(-0.5,  0.5,  0.0),
vector(-0.5, -0.5,  0.0),
vector( 0.5, -0.5,  0.0),
vector( 1.5, -0.5,  0.0)
};

point position = vector(u, v, 0.0);
position = flake_scale * position;

point base = floor(position);

point nearestCell = point(0.0, 0.0, 1.0);
int nearestCellIndex = -1;
for(int cellIndex = 0; cellIndex < 9; ++cellIndex) {
point cellCenter = base + cellCenters[cellIndex];

vector centerOffset = cellnoise(cellCenter) * 2.0 - 1.0;
centerOffset[2] *= safe_flake_size_variance;
centerOffset = normalize(centerOffset);

cellCenter += 0.5 * centerOffset;

float cellDistance = distance(position, cellCenter);
if(cellDistance < flake_size && cellCenter[2] < nearestCell[2]) {
nearestCell = cellCenter;
nearestCellIndex = cellIndex;
}
}

NormalResult = color(0.5, 0.5, 1.0);
ColorResult = 0.0;
alpha = 0.0;

if (nearestCellIndex != -1) {
vector randomNormal = cellnoise(base + cellCenters[nearestCellIndex] + vector(0.0, 0.0, 1.5));
randomNormal = 2.0 * randomNormal - 1.0;
randomNormal = faceforward(randomNormal, I, randomNormal);
randomNormal = normalize(mix(randomNormal, vector(0.0, 0.0, 1.0), flake_normal_orientation));
ColorResult = color(cellnoise(base + cellCenters[nearestCellIndex]));
NormalResult = color(0.5*randomNormal[0]+0.5, 0.5*randomNormal[1]+0.5, randomNormal[2]);
alpha = 1.0;
}
}

Neat!! Need to test it out, currently no time but I do wanna see it in action ;) Thanks!

2020-01-25, 08:59:40
Reply #29

cjwidd

  • Active Users
  • **
  • Posts: 607
    • View Profile
    • Artstation
Hey Pokoy, this is a ton of work and I really appreciate it. I haven't had a chance to dig into your shader just yet, but thank you for taking the time to prepare an example. I will report back as soon as I've had the chance to explore it more thoroughly.