Material girl

Wednesday, 26 July 2017

I've recently started familiarizing with Houdini and came across the fit function.
To be more accurate I met it in the VOP context, so we'll look at it with its VOP node costume on but under the hood it's all the same the VEX function.

Here's how the fit parameters look inside the VOP:

Note that if you promote the parameters (= expose them so they can be editable outside the VOP network), the parameters are named differently:

I started fiddling with the sliders but wasn't entirely sure what the
values were doing. Sure my density looked more or less dense but that's
not mathematical enough to be entirely sure how it works.

Of course I looked up the node's documentation but

This
operator takes the value in the source range (srcmin, srcmax) and
shifts it to the corresponding value in the destination range (destmin,
destmax).

was not quite enough for my non-mathematical brain to truly get it. I need a visual representation of maths to understand them.

So I started fresh with a uniform volume, tested different inputs in the Fit range node with
different density values in my volume and represented the results in a way that was
fathomable for me: sketches.

This is what my base input looks like:

I'm using a 0 to 1 range to make it clear and simple but of course your range could be completely different.
We'll assume our current value is 0.5 and we'll see what that value becomes after modifying the different
inputs the fit range offers.
Note that we're using the clamped fit range, there's also an unclamped version.

Source min (Minimum value in source range)The value you enter becomes
your new 0, therefore all the info between 0 and your input is discarded.

Source max (Maximum value in source range)The value you enter becomes
your new 1, therefore all the info between your input and 1 is discarded.

Destination min (Minimum value in destination range)
0 becomes your new value.
The original range is not affected, no data is lost.

Destination max (Maximum value in destination range)
1 becomes your new value.
The original range is not affected, no data is lost.

If you're familiar with this blog (as if…) this setup might ring a bell. Yup, just the same as what I described here: http://mklvfx.blogspot.fr/2014/02/fade-close-to-camera.html
Same tricks everywhere.

Wednesday, 20 May 2015

I often
have to create linear or radial gradients to use as masks or UV input. I’ve
always created them in photoshop using the gradient tool and I’ve always been
frustrated by the fact that you cannot place them numerically.

Today I’m
rolling up my sleeves and I’m going to sort this out instead of grumbling
internally.

This might seem a bit extreme but this sort of issue gets on my nerves if I encounter them every single time.

Case study
1: Photoshop and the gradient tool.

My problem
is: I’m eyeballing it. The snapping doesn’t work on the gradient tool so I have
to use the grid or some guides andzoom in as much as
possible to try and be accurate but I cannot guarantee I’m not a couple of
pixels off.

I can prove
myself it’s not perfect however. It’s subtle but look at the gif below (yes
it’s animated I promise). I’ve flipped the ramp horizontally and vertically,
and you can see it jump back and forth a few pixels. I’m not happy.

Case study
2: Photoshop again

I asked our UI guy for advice and he uses layer styles to create gradients.

Nice one.
It’s editable, you can modify it at will and the align with layer makes it
clean and fully accurate.

But hang
on, what does the flipping test tell us? Dammit! Still not perfect!

Case study
3: Illustrator

I normally
have a thing for illustrator, just like after effects. All that numerically
placing everything and oh my, those smart guides… They’re just heaven.

But in this
case Illustrator the Great disappointed me. The gradient tool is very neat, the
UI is clear, contextual just where need be and super clever as usual but the
smart guides don’t work with it. Shame on you Illustrator. I just have to leave
you there.

(I still
appreciate that your gradient can be either edited numerically in the gradient
window or intuitively on the fly with the mouse cursor.)

Case study
4: After effects

Aaah, shape
layers. What a bliss.

The
gradient fill won’t disappoint you. The shape layer path will define the
bounds, you can tweak the start / end positions on top of it using the
well-named start and end options, and the whole gradient is editable.

Yet there again, the flipping test shows that
the gradient is not perfectly centred. I’m starting to see a pattern here.
Could it be I am asking for the moon?

Case study
5: Substance

I'm lucky enough to know a
beta tester so I was able to quickly try it in substance. I definitely want to
spend more time with substance which looks mind blowing but I’m still very
limited with using it right now.

Using the
Tile_generator with a paraboloid pattern type I was able to create a radial
gradient. I have no control over the shape of the gradient with that option.
There are obviously much better ways of doing it but that was the easiest way
to test quickly it.

And you
know what ? It flips perfectly. Tadaaa.

Conclusion.

I thought
about it for a second and it hit me. I always work with power of two textures.
If I’ve got a 128*128 texture, surely that’s great caus’ my centre is at 0.
Yeah but no. With any adobe method, it looks like the gradient won’t start in between two
pixels. It needs a pixel to use as a centre, it seems. I tried it on a 129*129
square and the flipping would leave the gradient unchanged.

I suppose
substance handled it better because the creation of the gradient comes from
maths rather than a tool.

Epilogue.

Unfortunately, substance isn't part of my day-to-day workflow. I hope it will some day.I currently go with the Photoshop gradient overlay layer styles for all my gradients. If I use the gradient tool to create a gradient and want to modify it months later but I've forgotten to save it, then I'm screwed and I have to try and recreate it. The layer styles mean my gradient always remains editable.

Monday, 20 April 2015

Example 4: texturing an object.

Finally, to close this topic here's a little experiment.

When powers or magic are involved, you will often face
the issue of having to overlay some FX material on characters although their UVs
were not designed for it. What you want is your Fx UVs to be independent
from the textured model's.

You
could animate your material in screen space. That's not too bad an option but
that'll be visible as the camera and the animated models move.

You
could create a second UVs set with cylindrical UV mapping. This is the
method I used on the doppelganger model in DmC 5 DLC: Vergil Downfall.

That comes at a cost (that of an extra UVS set) and depending on the shape of your model, you can experience some stretching in certain areas.
(It was mostly ok on that slim character but still not fully satisfying).

Another
method would be to have a material that is not UV dependant but
that would still move with the object.

I can't
guarantee this is production worthy, I only made it for fun. It might be too expensive to actually be usable but I
still like the idea.

This is a capture of the first test I made… wow, almost two years ago already.

I'll recreate something similar now. (I wish I had saved my packages at the time.)

We'll start it simple and gradually add layers of complexity.

Mapping each axis.

Right now we want to hide the result on the faces not pointing towards z. We'll check which direction the vertices are facing. Those that face z return a value of 1, the others of 0. (Sure, that's a bit simplified but it's ok for now since we're working with a cube.)

White and black? Yay we got ourselves a mask. We just need to multiply this with our remapped texture.

(Note that we use a scalar parameter to adjust the size, not just a
constant. You'll use that same parameter on each axis so that the sizes
are consistent. Besides, when comes the time for actually texturing an object
it's better to adjust your values within a material instance than editing your original material.)

The value of the normal is -1 on the bottom. We use the abs node to make negative values absolute (that is to say, positive) and now we see both the top and bottom of the cube.

We duplicate the above set up 3 times and tick the correct channels to appropriately map along x, y and z and then we add everything together. We can safely do so with nothing overlapping since we've only retained what's happening along a single axis every time.

Mis-ticking a box is easy. To double check that a section of material is fine you can right clic any node and go ‘Start previewing node’. Saves you the hassle of having to temporarily plug that bit in your output.

Here we go. We've got all our faces textured.

It doesn't look like much so far, because it's only a cube but if you move it around you can see how the texture sort of belongs to the world. We're not using the UVs space so we could texture any object, not just a cube.

Like this chair for instance:

Making the material local.

If you want the object to be animated, it's not good to see it move through the texture. We'll make it local, as we did before.

Separating positive and negative axis.

We've got the basics in place. Now we want this material to look a bit more interesting.

We'll start by panning our texture.

Right now, the positive and negative axis display the same thing. Since we are pretending this is happening in space we need to invert the panning direction in positive and negative for the texture to appear to rotate around the object.

In order to get the positive and negative both white and the rest black, we've just made the vertex normal absolute. Now we're going to use a ceil node so that the positive axis is white and the rest is black. Then we can use the output as a mask.

When I want to check whether my connections are right, I often use a lerp with obvious colours. I might have mentioned this before, I'm not sure.

With this we can double check that +x is green while rest is red (even the back, I promise you). We're good to go.

Here's the whole thing with the inverted panners in x and the ceiled result used as a mask to drive the linear interpolation:

Now, this works on a cube which normals conveniently face a single axis
but that's not good on a sphere for instance. The textures projected along different axis overlap because each vertex is not just facing x, y or z. It's a bit of each.

We're going to need to
modulate the transitions between our masks.

(Too many gifs, I'm dizzy. Oh, and by the way I'm changing to a more contrasted texture. I had picked a random one to start with.)

Transition between facing axis.

On a very smooth object such as a sphere, the normals are not going to get to a full 0 until they reach the next axis. That's why you can see the textures overlap.

Here is the preview of one single texture. In this case, you would want to see the texture on the sides, but not all the way to the front.

We'll just add a power on what we use as a mask: after the abs on the vertex normals that is.

Power of 1 (same as no power):

Power of 5:

We replicate this for each axis, only this time we're renaming the parameter to have a different control for x, y and z. We'll need different adjustments per axis depending on our object.

Here's the result on a sphere. The x and y work ok, the z not so much because its panning speed can't match in every direction. We'll leave it as is for now.

Now I want to start making some actual art. To do so more easily I want to be able to control my parameters from a material instance. That material is too spread out to work from inside.

My problem is, I've got the texture's scale exposed but not its panning speed. I would like to be able to control the speed in x and y independently but I can't do so right now.

You see, both my x and y panning values are contained within the same panner. If I multiply time, both x and y will be affected the same. And I can't multiply time by a vector 2 constant because the panner expects a constant 1 time input. We'll need to make more changes to our material.

Controlling x and y panning speed from the material instance.

Here's a simple way of splitting both speeds:

Just link two panners in a row. The first one has a speed of x = 1, y = 0, the second one is x = 0 , y = 1.

Now for the complicated one.

Why so? Because I built it before I realised I had a very simple and easy option and I don't want my hard work wasted. Besides, as it turns it seems to be 2 instructions cheaper according to the material editor stats.

Basically, we'll recreate the functionality of a panner. Panning is adding a value to our coordinates to shift them over time. We'll just do that: add time to our coordinates.

In the following screenshot, the input coming from off screen on the left are the coordinates in world space. We filter them and keep only the x or y, and add time that is multiplied by a parameter to which we'll have access in the material instance. Easy enough.

The only problem is: it looks kinda messy. Well not messy but big. Way too big. We need controls over x and y for the vertices facing worldspace x and an inverted speed for the vertices facing worldspace -x. Even though we can reuse the block controlling the y speed it's still a lot of nodes.

And that's where some cool guys come into play:

Material functions.

We'll make a function out of this. A material function is a bunch of instructions you put together and nestle within a single node to keep things neat and easily propagate changes. You can use a material function in any given material.

I won't go in depth on how to make one, it's all in the documentation, I don't think I'd have anything to add to it, but feel free to ask questions if you have any.

Here's my function:

I've got several exposed parameters; three inputs:

- texture coordinates

- speed x

- speed y

And a single output: that'll be the coordinates going through the panners.

Here's the function used twice to replace our groups of nodes. I didn't have to duplicate the ‘speed y’ parameter but I like to avoid connections crossing.

You see:

Much clearer isn't it?

Finally, here's the whole thing:

Arted example.

With the above screenshot you've got the basis to make the material work. Now I'll make an arted version but this will only be one of the possible outcomes. You could imagine doing anything you usually do with a texture to make this more interesting.

My advice would be: don't get too carried away because this thing can quickly scale up and get out of hands.

Before you go too far with this and add a lot of details, preview it in actual game conditions. If there's a lot of action / post processes / particles going on, if that material doesn't stay on for too long, if you don't have any slomo involved… well a simple version is probably enough. The patterns of a couple of moving textures can be spotted quite easily if you stare at them but perhaps a player won't be staring and it'll just be parts of his landscape. It really depends on the context of your game.

Looking back at my old video capture, I was probably multiplying an animated texture with a mask.
The choice I've made this time is to pan a texture and offset its UVs with two panning textures added together. Don't pay attention to the model itself, it's a default example model from zbrush.

The transitions are seamless, our previous issue with the z axis not matching is not visible so I basically ignored it and didn't try to fix it. (This means I can simplify the material and remove the bits about inverting the panning direction for the z axis by the way.)

You can see that this happens in world space, not in UV space since I can scale the model without the texture scaling along.

Wednesday, 25 February 2015

Example 3: Starry night

Now, this is a little experiment I run over a year ago as I got started with UE4 vector fields. I wanted to create an animated version of Van Gogh's famous painting ‘Starry Night’ with GPU particles.
I hand painted the vector field in maya, created a
flat initial location box (same ratio as the painting's), and thousands
of small rectangular particles followed the vector field. As
they moved, they would pick the colour of the painting used as a texture
mapped in world space.

I was really quite happy with this idea but it turned out that someone had already created this as an app and
they'd done a good job of it. Once I found out it seemed pointless to
complete the project. Great minds think alike, they say. Fair enough but really,
you wanna be the first of those minds.

Oh well, nevermind. I still had some good fun doing the tests plus it gives me an example for you people to enjoy.

So.
We have the dimensions we want the painting to be displayed at, and we
want them to be translated to texture coordinates. I've decided I'd keep
the centre of the painting in the middle; I could have decided for it
to be in a corner, I would just have had to use different values for the
min and max.

Here's what's happening in the material: I'm ‘projecting’ the texture in world space (as seen in the first part) and making its coordinates local (as seen previously), and then I use my favourite little snippet (described here) to remap the 0 to 1 texture coordinates to the size I want the texture to appear in world units.

Then
in cascade I create a box with an initial location that matches my min
and max and as the sprites move, they pick the colour of the painting
underneath.

It's rather
basic, close up you can see it is a cut out and it doesn't feel like moving paint
strokes. I was thinking of trying to see if I could use the ParticleSpeed to offset the texture but as I said I gave up on the project quite early on.

Painting this vector field again in maya has actually been very interesting, it's going to be helpful for my personal project. I've found out a few things about how to get a vector field UE4 ready. I'll keep that for a later post, still lots I don't
know about.

This is a capture of the animated particles in cascade preview: (for those who don't know, cascade is the name of unreal's particle editor)

If I place this emitter in the world, and I add more particles around my initial location area, you can see how the last pixels are stretched. It shows that the texture is mapped to a precise area in space (centered around the emitter position).

Same thing from the side, the pixels are stretched because we only projected along one axis.

Hope you enjoyed this, folks. The next and last example will actually be my favourite one, so stay tuned.