I’ve upgraded my watercolour shader again. I found it was performing badly, so I rewrote it entirely. It worked better, for a while, but seems to have become slow again. I’m starting to think it’s just a Blender issue. I have also noticed, it’s become so complex, even recompiling it just for the material preview is very slow. It’s quite terrible.
I modified it to work better with retaining the colour on dark colours. Basically, it does the standard interpolation, then uses that for a second interpolation with a set border, only falling off from the retention amount to white after that point.
I also added ambient occlusion. It wasn’t difficult, but it’s harder to get right than normal shading. I may just end up faking it instead.
Lastly, I changed my interpolation function. It used to just switch between them, but now I’ve given it the ability to interpolate between different types of blending, for more varied results and more control. It’s not performing well, though; I should interpolate between their respective factors rather than doing all of those interpolations and then blending the result.
Somehow, this shader is a lot slower than V6 and V7. It might be the AO, or something else; I need to get to the bottom of it, because I need it to be fast and responsive.
I’m also finding, I need to work on the transparency. Things like sleeves are still an issue. I can bypass it by masking out bits manually that would be covered by worn clothing, but it’s inconvenient. I’d prefer not to have to deal with such things. I need a way to determine which things are occluded from view by others and not render them, but that seems like it would require ray tracing, which Eevee is not capable of at the moment. The edge soak is also rather weak on the skin-coloured monkey head.
A way to determine if it’s being viewed in perspective or orthographic would also be nice. It doesn’t display right in ortho anymore, because I used my depth-detecting function to scale the edge soak and edge noise textures so that they’ll stay relatively consistent whether they’re up close or far away.
I still have a very long way to go before this is what I want it to be. Still, this is progress. I also finished a character model of Maya, from Persona 2. By doing that, I worked out things I need to change. Next, I’ll make a post about that, then next I want to make Mariku, Ryou and the others.
I’ve nearly finished a Ryou model recently. Although, having done that, I had the problem that I didn’t have a good shader to apply to it.
I’ve spent quite a lot of time in research and experimentation for various aspects of my shaders, for fun and to use in my art, but I’ve never really got something I would say is complete and reliable. Perhaps because I was too perfectionist about it, and nitpicked a lot over 3d elements. Making 3d that’s a convincing facsimile of 2d has been a goal of mine for a long time. I always wanted to be able to draw well, but the amount of work it needed was daunting, and I got put off. It’s ironic that with the amount of effort I’ve put into NPR to imitate it, I could’ve just learned the real stuff by now.
Still, I’ve been trying to progress. I want to achieve what I set out to do this year, making more art and models, so I made a Ryou base mesh. From that, I want to derive several, like Yami Bakura, fem Ryou, au versions. With that, I needed a shader, so I went back to my previous efforts.
My previous work in steps; it may look similar, but a lot of them derive their appearance differently.
I was dissatisfied with what I had previously, though, so I considered my approach. The biggest problem was the silhouette. It was too 3D, with fresnel, the easiest real time method I knew of to get something resembling an outline, capturing too much detail. I considered editing the normals, but then, I also need a correct set of original normals for the shading. Having multiple sets baked seemed like too much of a pain. I also experimented with using nodes to blur a texture that defines the normals to try and get a blurry, and thus less detail-accurate, fresnel, but it didn’t work when viewing it from different angles. Then I came across this, a discussion on how to, essentially, replicate a depth pass with a shader.
I replicated the effect, so now I have that available to me. But more importantly, it made me remember a previous technique I had, using depth to get a shade.
At that time, I wrote this off for the time being because the results weren’t accurate enough, but I did remember it. The depth pass made me consider that I could use it as part of the solution, if not the whole thing itself. The most problematic details from fresnel were at the front. So, I could use the model’s depth as a mix factor to get the nice smoothness it has at the front, and keep the overall shape detail the fresnel captures.
Doing it that way, I was able to get a much more pleasing result.
The green one; the others represent steps in the process.
This new shader uses the depth to get smoothness at the front, and be controllable, while keeping most of the silhouette. I also made some more node groups to add a false paper texture according to how coloured parts are, small variation, and again, noise at the edges. I also added more features to control the shading, like the ability to set the minimum amount of colour an area must always have, for tricky areas or ones needing constant detail. Although, this depth+fresnel method does need tweaking when the view is changed, and relies on the object’s origin being its centre of mas. That does give it some flexibility, too, though, if I wanted to move that to centre a specific point instead of the actual centre.
I’m quite happy with how this looks, for once. It’s not perfect, but it is a step up from previous attempts. There is something else I did differently here, too. The transparency. Ideally, I’d like to use Blender’s hashed transparency. It provides most natural looking results, and looks smooth.
However, I find it’s not as responsive in the viewport. It also takes longer to be able to see what’s going on, since it’s grainy for a moment, and is harder to judge. The difference isn’t too bad, but I’m on a laptop; I’d like to buy all the performance I can get. Especially since it’ll likely multiply significantly in performance loss in a scene with many objects.
So, I considered using blended transparency. It’s quick, it’s smooth. But, it doesn’t seem to work quite right. I have to make sure to set backfaces to be rendered, and then it doesn’t show correctly. Switching that off, however, seems to work.
Backfaces/No backfaces
But it hadn’t worked quite right for me before, and I want to keep performance as good as I can get it, so I thought about the simplest, which is alpha clipping. For that to work, it needs a game type transparency, called Screen Door Transparency. It’s not really transparent, but by hiding more pixels, you give the illusion of it.
Blender doesn’t have anything like that by default, so I made a group node myself, that does it in six steps, converting a Value input to work with it. I think the results are decent, and it’s more responsive in the viewport than hashed. It’s more digital and 3d, though; I may end up using Blend, if it works sufficiently. Disappointingly, the performance at render time compared to hashed is about the same, which is strange. It would be frustrating if Blend works just fine after all the time I spent working out how to do that. Although I do think this way gives distinction to it.
The overall size of the node set is massive, though. I could put it in a group by replacing the colour ramps with Remap Value nodes I made, but the control would be linear and insufficient.
In any case, I’m fairly satisfied with it for now. I can choose the canvas colour, main colour and shadow colour, control the falloff from the centre to emulate watercolour spreading, control transparency to emulate it being thinner the further from that point it is, and control the way the HSV is modified by the fake paper texture.
Next, I want to add more. I want to be able to add fake brush strokes by using a texture as a vector input, which I believe I know how to do but just never did yet; similar way to how a normal map works. I also want the colours to be dynamic, responding to coloured light. That I know how to do already, but need to check more how it would appear in Eevee.
Those can wait, though. My next task is to apply it to a model and make some art. But for now, I’m going to go to bed because my brain is slowly packing it in. It is 1AM, after all.
An advantage Blender Internal had, and that a particular modified Blender build has with Cycles, was Light Groups. A light group allowed you to cause a light to only affect certain objects. It was convenient for NPR purposes. The other useful, and sorely missed feature Internal had, was the ability to define, in the material, whether it took shadows or not. That’s incredibly useful, because it means you can use it to isolate shadows, not just regular shading, and define which areas you want to take shadows or not.
For example, anime character’s eye and brow, if shaded normally, would likely have a strong and ugly shadow cast by polygon hair, which doesn’t look good. You could, if you could isolate it that way, mask it out. That’s not possible by default in Eevee or Cycles.
But, using Eevee, I’ve found a way. I wanted to be able to isolate the shading from any given light. Especially since it was one of the techniques used in Guilty Gear Xrd to achieve such convincing false 2d.
They used a light on each individual character to give them specific control, rather than scene only lighting. I’d like to explore scene-based shading, lights in the world, etc, but it’s good to have this as an option. That made me consider isolating lighting results again. I did some research, and came across this Polycount thread.
I already knew shading could be faked by using the dot product of a direction and the surface normal, but that can’t capture shadows. I’d thought beforehand, I could use the RGB node to get the difference between fake shading and real shading if I could just make them match; what I lacked was the ability to work out what direction the light is coming from in order to do that, and that’s what that thread taught me.
Node setup by Polycount user Jekyll
By using this technique, I was able to get the light direction. Then, by simply using a Shader to RGB node with a diffuse shader and subtracting the false shading from it, I was able to isolate the shadows.
With this, I can catch shadows and then use whatever technique I’d like to mask them out.
The advantages of this techniques are:
Able to identify shadows.
By using multiple lights, you can identify specific shadow types, like using two to distinguish contact shadows and shadows.
Can use multiple to distinguish hard and soft lights, then mix between them to preference.
The disadvantages:
It’s dependent on drivers. You have to manually select the light, and set up drivers for its location.
If the light is hidden, I’ve discovered the driver will break, meaning you have to reassign the object.
If you move it into a different scene and don’t bring the light, you’ll have to replace it and redo the drivers again.
It’s inconvenient to have to use multiple lights to get multiple types of shadow.
Using multiple lights makes the whole scene brighter. Objects not using the shadow catcher can’t account for this, and may be undesirably bright.
It’s inconvenient if the lights have to be changed significantly, like the number.
I believe it only works correctly with directional lights.
Having to set it up in such a time consuming way is the big problem for me. It’s not as if there’s just one box to select it in. It’s inconvenient, and especially when it breaks just from the light being hidden. That might be fine if we use the Guilty Gear Xrd method and only use a specific light or set of lights for it, but if we want to change things a lot, it’ll be a pain.
So, I’ve been doing some more experimenting, trying to recover that information somehow using the Texture Coordinates node, which can select the object. If I can, it will be much easier and quicker to modify. I’ve only had mixed results so far, and not worth showing yet. Still, I’ve learned from this, so it’s not wasted. I’m hoping to get a convenient way working of shadow catching this way. The ability to modify how it’s shaded and shadowed, in real time, not compositing, would be extremely useful.