• RuntimesUnity
  • How to make 2D rim light in 3D pipeline?

I switched from Unity 2D URP to 3D URP and managed to rebuilt the whole scene with a new 2D lighting plugin called 'Smart Lighting 2D' almost 1:1.

But how do I add 2D rim light like in 2D pipeline now that the Spine materials are changed to Universal render pipeline/Spine/Sprite with no light mask option?

Also, what light should I use now that there's no red channel R to be used as the rim light channel?

Thx in advance

Related Discussions
...

To create a 2D rim light effect in a 3D URP pipeline, you can follow these steps:

  1. Shader Selection: Since you are using the Universal Render Pipeline/Spine/Sprite shader, you need to ensure that it supports the lighting effects you want. This shader is designed to work with URP lights, but it does not have a direct light mask option like in the 2D pipeline.

  2. Rim Light Setup:

    • Create a Custom Shader: You might need to create a custom shader that mimics the rim light effect. This can be done by modifying the existing Universal Render Pipeline/Spine/Sprite shader to include a rim light calculation.
    • Shader Graph: Alternatively, you can use Unity's Shader Graph to create a custom shader that includes a rim light effect. You can add a rim light by calculating the dot product between the view direction and the normal, and then using this value to control the intensity and color of the rim light.
  3. Lighting:

    • Use a Directional Light: In the 3D URP pipeline, you can use a directional light to simulate the rim light effect. Position the light in such a way that it highlights the edges of your 2D objects.
    • Light Settings: Adjust the light settings to achieve the desired rim light effect. You can play with the intensity, color, and angle of the light to get the right look.

Here is a basic example of how you might modify a shader to include a rim light effect:

Shader "Custom/RimLightShader"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
        _RimColor ("Rim Color", Color) = (1,1,1,1)
        _RimPower ("Rim Power", Range(0.5, 8.0)) = 3.0
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        LOD 200

        CGPROGRAM
        #pragma surface surf Standard fullforwardshadows

        sampler2D _MainTex;
        fixed4 _RimColor;
        float _RimPower;

        struct Input
        {
            float2 uv_MainTex;
            float3 viewDir;
        };

        void surf (Input IN, inout SurfaceOutputStandard o)
        {
            fixed4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color;
            o.Albedo = c.rgb;

            // Rim light calculation
            float rim = 1.0 - saturate(dot(normalize(IN.viewDir), o.Normal));
            o.Emission = _RimColor.rgb * pow(rim, _RimPower);
        }
        ENDCG
    }
    FallBack "Diffuse"
}

This shader includes a basic rim light effect by calculating the dot product between the view direction and the normal, and then using this value to control the emission color.

By using a custom shader or Shader Graph, you can achieve a similar rim light effect in the 3D URP pipeline as you had in the 2D pipeline.

Use a Directional Light: In the 3D URP pipeline, you can use a directional light to simulate the rim light effect. Position the light in such a way that it highlights the edges of your 2D objects.

@Cheshirecrow In additiona to what Spinebot said above, you could set the normals in the skeleton's normalmap to point 90 degrees sideways at the edge, so that an almost forward-facing light used as rim light would illuminate the perimeter slightly (or you can use two lights). You could also modify a shader and your normal-maps so that the blue channel values [0..255] don't map to [0..1] forward but instead include backward mapping to [-1..1].

    Harald Thanks, I'm stuck figuring out a way of doing it. I'll try the normal map method you mentioned

    Harald Harald Unfortunately, we couldn't figure out a viable solution using the normal or metallic maps, mainly because we used a 2d light plugin that doesn't support layer sorting(we can only mask out non-spine objects). This would mess up the render with many 3d lights in the scene.

    We also had problems importing materials to the Spine character in runtime and it just won't take the additional material we made. What do you suggest we do if we need to make a custom shader based on the Spine sprite shader? Preferably a shader graph that I can modify

      Cheshirecrow I am afraid that Harald is on vacation and will not be able to answer until next Wednesday at the earliest. We appreciate your patience until he returns.

      4 Tage später

      Cheshirecrow Unfortunately, we couldn't figure out a viable solution using the normal or metallic maps, mainly because we used a 2d light plugin that doesn't support layer sorting(we can only mask out non-spine objects).

      Could you please describe what you mean by "doesn't support layer sorting"? Why would that be related to lighting and normal maps?

      What shader are you using? Why Metallic maps? These should not be related to rim lights.
      In general you should be able to use e.g. the Universal Render Pipeline/Spine/Sprite shader and set the respective normal map there. Did you envounter any problems with it?

      Cheshirecrow We also had problems importing materials to the Spine character in runtime and it just won't take the additional material we made.

      What did you do to import materials at runtime, what code did you use and how did you provide Materials and Textures? What problems did you encounter?

      In general you should always be able to programmatically create Materials and assign Textures at Materials at runtime. Please note that this is unrelated to the spine-unity runtime and can be done with the Unity API as normal.

      Thanks for the reply. As an artist, I'll try to explain why it is what it is right now but I will have to ask our programmer to join the conversation about why programmatically creating Materials became an issue.

      1. The issue of "doesn't support layer sorting". We use a 2d lighting plugin Smart Lighting 2D from the Unity assets market to recreate the effects of the default 2d lighting system, as 2d lighting is not supported in 3d pipeline. This has proven the easiest way to fix our 2d lighting issue and save performance. The fundamental difference is that this plugin projects all lighting information on one overlay layer and thus doesn't support layer sorting. It's possible to use masks to hide 3d lights for static and Unity's sprite objects, doing this to Spine characters will result in extremely high-performance costs as I tested (the game drops from 300FPS to 3FPS with one instance for a i9-13900 RTX4070 PC).

      2. Why would that be related to lighting and normal maps? We are using both 2d lights and 3d lights in the scene, and the Spine character can be affected by both casting shadows. This results in 3d lights lighting character normal maps in an undesirable way, breaking the illusion of the rim light effect (if the normal map was made to create a rim light effect as you suggested).

      3. What shader are you using? We are using Universal Render Pipeline/Spine/Sprite with casting shadow on as our base shader for all sprites in the scene

      4. Why Metallic maps? We had a try by tweaking the metallic map and position of the camera to achieve a rim light effect. Think of it as the intense reflection of the metallic map when the viewer and light is positioned at an angle. Unfortunately, this approach won't be viable because other lights would mess up the illusion, and the camera position can drastically change during the game.

      5. Did you encounter any problems with it? The shader works great and all problems has been solved by the time of this post. The only problem left now is how to recreate a rim light effect similar to using a light mask in a 2d render pipeline in 3d.

      6. What did you do to import materials at runtime, what code did you use and how did you provide Materials and Textures? The importing issue has been fixed. We haven't managed to code any custom shader yet.

      7. What problems did you encounter? My thought is to put a custom material that can only be lit by the plugin 2d light on top of the existing Spine character material, using the light mask map we had before.
        But no matter what material I put in there, it doesn't affect the Spine character at all

      If this approach won't work, do you have any suggestions on how to achieve it?

      • Harald hat auf diesen Beitrag geantwortet.

        Cheshirecrow 1 [..] The fundamental difference is that this plugin projects all lighting information on one overlay layer and thus doesn't support layer sorting.

        Thanks for the added information. Unfortunately you still ended the explanation saying "and thus doesn't support layer sorting", but I still don't clearly know what exactly you mean by that, and why you would need such sorting. Do you mean you are using SortingGroups which are ignored by your plugin, or something else?

        Cheshirecrow It's possible to use masks to hide 3d lights for static and Unity's sprite objects, doing this to Spine characters will result in extremely high-performance costs as I tested (the game drops from 300FPS to 3FPS with one instance for a i9-13900 RTX4070 PC).

        This sounds like your lighting plugin is doing something wrong then.

        Cheshirecrow 2. [...] This results in 3d lights lighting character normal maps in an undesirable way, breaking the illusion of the rim light effect (if the normal map was made to create a rim light effect as you suggested).

        How did you create the normalmap? What does the normalmap look like?

        Cheshirecrow 7. What problems did you encounter? My thought is to put a custom material that can only be lit by the plugin 2d light on top of the existing Spine character material, using the light mask map we had before.
        But no matter what material I put in there, it doesn't affect the Spine character at all

        Adding materials to an already complete list will not change anything. Each atlas page corresponds to one Material entry. An attachment references an atlas page, and the atlas page references one material. If you have 2 atlas pages but 3 Materials, the 3rd one will be ignored. There are no magically added render passes or anything like that.

        Cheshirecrow 5. Did you encounter any problems with it? The shader works great and all problems has been solved by the time of this post. The only problem left now is how to recreate a rim light effect similar to using a light mask in a 2d render pipeline in 3d.

        Unfortunately it's hard to say anything in regards to the thirdparty 2D lighting plugin. If standard Unity shaders or shader graph shaders are supported by your 2D lighting plugin, it might be the easiest solution to just use such standard shaders instead of Spine shaders. You would then need to export your skeleton atlas as straight-alpha instead of PMA and not use the tint-black feature (or implement it on the shader side in e.g. shader graph).

        @Cheshirecrow One thing which might perhaps be helpful is the RenderExistingMesh component which allows you to re-render the same skeleton mesh with a different material. This is used e.g. to render a skeleton again with an outline-only shader, which might be somewhat related to your 2d rim light use-case.