saturday06 / VRM-Addon-for-Blender

VRM Importer, Exporter and Utilities for Blender 2.93 to 4.2
https://vrm-addon-for-blender.info
MIT License
1.3k stars 142 forks source link

Status of the Blender shader node group? #314

Open aVersionOfReality opened 1 year ago

aVersionOfReality commented 1 year ago

Describe the bug

There appears to be a variety of issues in the Blender Shader. It does not match Unity when using features like Shading Shift textures, and behaves strangely with certain light angles. Can you tell us the current status of the shader group? Is it working as intended? I'm looking at fixing some of the issues, but I'm not sure if I've misunderstood how it is supposed to work.

For example, the Light Vector Emulation section's output changes a lot depending on the light angle, as seen here. This also effects all the shading.

https://github.com/saturday06/VRM-Addon-for-Blender/assets/36803956/18408b1c-928b-4f73-bda4-7f4bba704ff2

To Reproduce

View the output of the Divide node in the Light Vector Emulation section.

Additional context

I'm guessing the purpose of the light vector emulation is to compensate for different Sun lamp strength and color settings (the color part is working.) Is this correct? Or is a certain Sun strength required? If this is what it's trying to do, that's really neat. I haven't seen this before, and it solves a common problem if we can get it working fully.

There are also some node redundances. The Mix Light node is multiplying by 1, which does nothing. image

The first few nodes of the Parametric Rim setup do exactly the same thing as the Layer Weight node's facing output. Could those nodes be replaced by it? image

Thank you.

saturday06 commented 1 year ago

Thanks for your bug report.

There appears to be a variety of issues in the Blender Shader. ... Can you tell us the current status of the shader group?

It's under development. It is far from finished.

Is it working as intended?

No. It's good enough to see my data, but I really want it to look just like Unity.

I'm guessing the purpose of the light vector emulation is to compensate for different Sun lamp strength and color settings (the color part is working.) Is this correct? Or is a certain Sun strength required? If this is what it's trying to do, that's really neat. I haven't seen this before, and it solves a common problem if we can get it working fully.

It tries to emulate the dot product of _MainLightPosition and the normal in Unity. The result is a composite of multiple lights.

https://github.com/vrm-c/UniVRM/blob/v0.114.0/Assets/VRMShaders/VRM10/MToon10/Resources/VRM10/vrmc_materials_mtoon_lighting_unity.hlsl#L36

I too think this would be pretty good if it worked. Right now I'm looking into a way to set the light parameters directly on the shader node and calculate them.

There are also some node redundances. The Mix Light node is multiplying by 1, which does nothing.

This node is left for lighting = direct + indirect, which will be added in the future.

https://github.com/vrm-c/UniVRM/blob/v0.114.0/Assets/VRMShaders/VRM10/MToon10/Resources/VRM10/vrmc_materials_mtoon_lighting_mtoon.hlsl#L155-L157

const half3 direct = GetMToonLighting_DirectLighting(unityLight, input, shade, shadow);
const half3 indirect = GetMToonLighting_GlobalIllumination(unityLight, input);
const half3 lighting = direct + indirect;

However, nothing is currently in effect and can be removed.

The first few nodes of the Parametric Rim setup do exactly the same thing as the Layer Weight node's facing output. Could those nodes be replaced by it?

Thanks. I didn't know that. I like the simplicity. I will replace it with the node you mentioned.

aVersionOfReality commented 1 year ago

Thank you for the response.

For the project I'm working on, we want to be able to setup our mtoon materials within Blender, so we care a lot about having the material be accurate. I have been working on the shader group and have overhauled most of it to better match Unity. Due to Eevee's limitations some parts can only be matched under specific circumstances, and others not at all (although if you are willing to use drivers or the addon to fill in nodes, then more info about the Light could be used.)

The biggest restriction is that Eevee only let's us get the full Diffuse output using Shader to RGB. To then get a clean toon shader, we have to compensate for the light in the scene, or turn as much off as possible. For the toon shader to work properly, the Light Intensity needs to be a linear gradient from black to white. If it isn't lit enough then it will have the terminator in the wrong place.

To get even shading from Diffuse in Eevee, you need to use a single Sun Lamp with:

So the only condition where the shader can match Unity is if they both use these light settings.

Here is the code for the lightIntensity, shadingGrade and toon shading in Unity: image

I have recreated it in Blender, which looks like this: image

This now matches Unity when the behavior of the light, color, shadingShift, etc. I have used Sun Angle 180 as that is the only way to match dotNL from the code after its been remapped to [0,1] range (diffuse node's output does not go below 0.) I am still working on other parts of the node tree and will share in a day or two when I finish.

It is technically possible for blender to support more and multiple light sources. But it gets very complicated. You need to have a node group per light and use drivers or an addon to get the transforms and settings of each light, and then rebuild the whole light pipeline in nodes. It works but the compile times become a problem, and you still won't be able to use Shadows.

A couple other things:

Thank you again for your work!

saturday06 commented 1 year ago

Thanks for sharing various deep thoughts!

It is technically possible for blender to support more and multiple light sources. But it gets very complicated.

Ultimately, I will implement this over time.

Of course, it is very difficult, so other implementations may be added along the way. Currently, I think this is the only way to match the display with Unity.

..., then rebuild the whole light pipeline in nodes. It works but the compile times become a problem ...

I think the compile time issue can be resolved. To do this, use the following method:

First, create a shader node group to store the light parameters.

スクリーンショット 2023-09-17 221438

Store the light parameters inside a shader node group. Unity's URP affects 8 lights, so I think a maximum of 16 lights is sufficient for practical purposes.

Update this internal value directly using Python. I know that changing values ​​inside a node group will not cause a recompilation. I may also be able to use a driver instead.

For updates, detect light changes using depsgraph_update_post, frame_change_post, bpy.msgbus.subscribe_rna, etc. and set them in the node group.

Implement them to match the processing that Unity's URP is doing.

I'm planning to use this method first by implementing UV animation. If it goes well, I would like to apply it to shader nodes.

aVersionOfReality commented 1 year ago

I think the method you show in our image is the right approach. I've had to build a shader like this before.

Changing values within a group can still cause a recompile. When Eevee compiles, it expands all node group in the material. Groups don't matter to the compiler. The way it works is that certain things trigger a recompile if they cut off part of the tree.

For example, if you have a mix node that has a value greater than 0 and then set it to 0, it will then recompile the whole tree ignoring everything before the 0 socket on that mix node. The same can happen for some big nodes like Principled BSDF that contain mix logic within them. (Both the mixed and unmixed shader can cache, but that cache won't last if you reopen Blender.)

Even if you avoid recompiles when changing values, compiling for the first time can still get quite slow. If there's only a single material per scene it'll be okay, but once people make several it'll quickly become a mess. And Eevee has no instancing or anything to help the compiling. Its a big problem, and they will hopefully improve this soon.

I have submitted the new version of the shader group I've made. And I almost have the Matcap vector working like in Unity, but that will require inputting information about the Camera Object into the nodes. Probably worth it though as mtoon in unity uses a very specific matcap method that's particularly good for things like anime hair highlights.

Thank you again.