SKShader #extension directive

What is correct way to use #extension in .fsh shader file?

TestShader.fsh:

#extension GL_EXT_shader_framebuffer_fetch : require
void main() {
     lowp vec4 destColor = gl_LastFragData[0];
     ...
}

in Swift:

spriteNode.shader = SKShader(fileNamed: "TestShader.fsh")


By this way shader compilation fails with

error: invalid preprocessing directive 
#extension GL_EXT_shader_framebuffer_fetch : require 
error: use of undeclared identifier 'gl_LastFragData' 
lowp vec4 destColor = gl_LastFragData[0];


Without extension the shader is compiled successfully.

I'm using Xcode 9.2, running on iphone SE iOS11, PrefersOpenGL is not setted in Info.plist.

Accepted Reply

The documentation fairly strongly states that OpenGL extensions are not supported:


Warning

SKShader
does not support OpenGL Extensions. SpriteKit will return an error if you compile a project containing a fragment shader using extensions.


The link you provided cites the extension (framebufffer fetch) is used for programmable blending. In SpriteKit, programmatic blending can be achieved by snapshotting a sprite node with SKView.texture( from: sprite ) and updating the sprite's texture with the result.


let sprite = SKSpriteNode( texture: texture, color: nil, size: texture.size )
let sprite.shader = SKShader( ... )

// every loop that the shader is active, update the sprites texture with the shaded result:
let shadedTexture = mySKView.texture( from: sprite )
sprite.texture = shadedTexture

Replies

The documentation fairly strongly states that OpenGL extensions are not supported:


Warning

SKShader
does not support OpenGL Extensions. SpriteKit will return an error if you compile a project containing a fragment shader using extensions.


The link you provided cites the extension (framebufffer fetch) is used for programmable blending. In SpriteKit, programmatic blending can be achieved by snapshotting a sprite node with SKView.texture( from: sprite ) and updating the sprite's texture with the result.


let sprite = SKSpriteNode( texture: texture, color: nil, size: texture.size )
let sprite.shader = SKShader( ... )

// every loop that the shader is active, update the sprites texture with the shaded result:
let shadedTexture = mySKView.texture( from: sprite )
sprite.texture = shadedTexture

Thank you for your answer. Can you give me some more advice? I need to create "transparent holes" in the colored with opacity shape. The holes are radial gradients and are dynamicaly added/scaled.

i.e.

let shapeNode = SKSpriteNode(color: UIColor.green.withAlphaComponent(0.3), ...)
addChild(shapeNode)

and adding holes with

let holeTexture = SKTexture(imageNamed: "hole") // radial gradient
let holeNode1 = SKSpriteNode(texture: holeTexture, ..)
addChild(holeNode1)


I've tried to acchieve this by using blendmodes but with not much success - holes overlapped each other adding color at the edges of gradient, or not cutted holes in shapeNode. Then I tried to buffer holes in SKEffectNode (edges overlapped in right maner), but then I could not cut holes in shapeNode. And with shaders I can not (if I understand you right) to do manual color blending.


Doing texture snapshotting will not be efficient for this situation because of dynamic nature of holes, am I right? Can the holes be achieved by blending only? Just in theory - I'm not very confident that I understand totaly correct how SKBlendMode work. Or can you direct me where I can read more about blending modes used in SpriteKit? Doing experiments with .subtract mode confused me a lot when the colors have transparency 😕.


Thanks

Ok, I got it

1. Blend holes (white -> black gradients) in SKEffectNode with screen.

2. Add shader to SKEffectNode that will replace colors to final color (UIColor.green.withAlphaComponent(X))

3. Add SKEffectNode with .replace blending mode to scene