• RuntimesGodot
  • How to use shaders for SpineSprites?

Hello! I exported the character to godot 4.4.1. I created a normal map for the atlas. Everything is working well. Then I wanted to add a shadow from the character using a shader that I found on the Internet. The shader works fine with regular sprites. But I couldn't use it with SpineSprite. If I load the shader in the material in the Canvas Item section, then nothing happens, if I load the shader in SpineSprite Materials in Normal Material, then some kind of distorted shadow appears. I tried to approach the problem from a different angle and added a SubVewport to the scene, made SpineSprite a child, added a Sprite2d node and set its SubVewport texture. Next, I loaded shadow shaders for Sprite2d and the shadow is displayed normally, but in this case I lose the effects of the normal map and the character becomes flat. Can you help with this problem? (I don't know how to write shaders)
SpineSprite screen without shadows and shaders
SpineSprite screen with shader in Normal Material
Sprite2d screenshot using SubVewport with SpineSprite and shaders
The screen where Spine Sprite + Sprite2 is used (with SubVewport and shaders)

Related Discussions
...

To use shaders with SpineSprite in Godot, you need to ensure that the shader is compatible with the way SpineSprite handles materials. When you apply a shader to a SpineSprite, it should be set in the Materials property section for the appropriate blend mode. If the shader is not working as expected, it might be due to the shader not being designed to work with the specific rendering setup of SpineSprite.

If you are experiencing issues with distorted shadows, it could be due to the shader not accounting for the specific transformations or the way SpineSprite handles its rendering pipeline. Using a SubViewport and rendering the SpineSprite to a texture can be a workaround, but as you noticed, it might not preserve all effects like normal mapping.

To resolve this, you might need to modify the shader to ensure it works correctly with the SpineSprite's rendering context. This could involve adjusting how the shader calculates lighting or shadows based on the SpineSprite's properties. If you are not familiar with writing shaders, you might consider seeking help from someone with shader programming experience to adapt the shader for use with SpineSprite.

I'm afraid it's not possible to give advice without knowing what specific shader you are trying. Generally, in 2D, just downloading any old shader and applying it to a node is often not as straight forward, specifically, if the node is a complex 2D mesh with alpha translucency.

From your screenshot, it also looks like you are using a SpineSprite in a 3D scene via the sub viewport texture. That is an entirely different rendering pipeline than a standard 2D Godot scene.

Ideally, you can provide a simple Godot project that reproduces the issue.

    Mario
    The given example is in 2d, but on the screenshot it may seem that it is 3d. Here is my shader that works with Sprite2d.

    shader_type canvas_item;
    
    
    uniform vec2 shadow_offset = vec2(0.06, 0.03);
    uniform vec4 shadow_color = vec4(0.0, 0.0, 0.0, 0.4);
    
    void fragment() {
        vec2 shadow_uv = UV + shadow_offset;
    
        vec4 shadow_sample = texture(TEXTURE, shadow_uv);
    
        shadow_sample *= shadow_color;
    
        vec4 original_color = texture(TEXTURE, UV);
    
        COLOR = original_color + shadow_sample * (1.0 - original_color.a);
    }

    Thanks for the repro. As expected, the shader will only work with rectangular sprites. Let me explain.

    I took your character and turned it into a single image, with the background pixels being fully transparent.

    I then slapped that into a 2D scene and applied your shader as a material. Works as expected. Kind of.

    If you look at the top of the character, you'll see that its shadow is cut off. The reason for this is that the shader is only applied to pixels that are part of the sprite. It can not extend past the boundaries of the sprite (or more correctly, passed the bounds of the triangles making up its mesh).

    There's another problem. I've rotated the image by 90°:

    The shadow still kind of works, but is now pointing in the oposite direction.

    The reason for this is that the offsetting in the shader is done in texture space.

    Now let's look at your SpineSprite. First of all, it's not a single rectangle. It's a mix of many small rectangles and a mesh for the mouth and the leave stem.

    The individual regions (rectangles) and meshes are pretty tight, so there's not enough space around the colored pixels to also draw the shadow via the shader. Just like in the case above, where the top of the shadow is cut off.

    Worse, the regions and meshes map to images in the texture atlas, that are rotated.

    Since the shader works in texture space, you get all kinds of weird shadows. This would look even less correct when the character is being animated.

    TL;DR: you can not use this shader for anything but the simplest image. You can definitely not use it for SpineSprite.

    Your sub viewport idea is the way to go. You need to render the character to its own texture (with enough whitespace around it), then use your shadow shader on the resulting image.

    I'm afraid I'm not enough of a Godot expert to help fix the normal map issue with the sub viewport approach. I suggest you hop over to the Godot chat or forum and ask for advise there for that.

      Mario
      Thanks for the reply. I'll try to look for more solutions.