Search code examples
iossprite-kitskspritenodefragment-shaderskeffectnode

Dissolve SKShader works as expected on simulator, strange behaviour on actual device


I encountered weird behaviour when trying to create dissolve shader for iOS spritekit. I have this basic shader that for now only changes alpha of texture depending on black value of noise texture:

let shader = SKShader(source: """
        void main() {\
            vec4 colour = texture2D(u_texture, v_tex_coord);\
            float noise = texture2D(noise_tex, v_tex_coord).r;\
            gl_FragColor = colour * noise;\
        }
        """, uniforms: [
            SKUniform(name: "noise_tex", texture: spriteSheet.textureNamed("dissolve_noise"))
        ])

Note that this code is called in spriteSheet preload callback.

On simulator this consistently gives expected result ie. texture with different alpha values all over the place. On actual 14.5.1 device it varies:

  1. Applied directly to SKSpriteNode - it makes whole texture semi-transparent with single value
  2. Applied to SKEffectNode with SKSpriteNode as its child - I see miniaturized part of a whole spritesheet
  3. Same as 2 but texture is created from image outside spritesheet - it works as on simulator (and as expected)

Why does it behave like this? Considering this needs to work on iOS 9 devices I'm worried 3 won't work everywhere. So I'd like to understand why it happens and ideally get sure way to force 1 or at least 2 to work on all devices.


Solution

  • After some more testing I finally figured out what is happening. The textures in the shader are whole spritesheets instead of separate textures on devices, so the coordinates go all over the place. (which actually makes more sense than simulator behaviour now that I think about it)

    So depending if I want 1 or 2 I need to apply different maths. 2 is easier, since display texture is first rendered onto a buffer, so v_text_coord will take full [0.0, 1.0], so all I need is noise texture rect to do appropriate transform. For 1 I need to additionally provide texture rect to first change it into [0.0, 1.0] myself and then apply that to noise coordinates.

    This will work with both spritesheets loaded into the shader or separate images, just in later case it will do some unnecessary calculations.