Marcin Żmigrodzki
Marcin Żmigrodzki

Reputation: 393

How to draw gradient in SKShapeNode?

I am trying to draw gradient in SKShapeNode object.

I draw a triangle with a color using alpha component and it works fine. And I would like to add gradient so one edge of the triangle disappears slowly to the background. What I want to achieve is to simulate sight range of a character.

I have found one answer regarding this challenge: How to apply a gradient to SKShapeNode created from a path, but the answer doesn't work in XCode 13.1. The reason is that proposed solution of adding SKTexture can't be compiled:

var testTexture = SKTexture(size: CGSize(width: 200, height: 1), color1: CIColor(red: 1.0, green: 0.0, blue: 1.0, alpha: 1.0), color2: CIColor(red: 1.0, green: 0.0, blue: 1.0, alpha: 0.0), direction: GradientDirection.Left)

It results in the error "Cannot find 'GradientDirection' in scope...". And I cannot find any other method to add gradient.

I have found another answer which says that it isn't possible, but it is from 2013, so maybe something changed (How to create a Gradient in Spritekit?).

I also tried this solution: https://augmentedcode.io/2017/11/12/drawing-gradients-in-spritekit/, but it doesn't produce any effect and decreases performance of the application.

Upvotes: 1

Views: 520

Answers (1)

Marcin Żmigrodzki
Marcin Żmigrodzki

Reputation: 393

Some sources from the internet directed my to OpenGL Shading Language: https://www.khronos.org/opengl/wiki/Core_Language_(GLSL).

Here is partly solution:

let sight = SKShapeNode()
let gradientShader = SKShader(source: "void main() {" +
                                          "float distanceFromCenter = distance(v_tex_coord, vec2(0.5,0.5));" +
                                          "gl_FragColor = vec4(0.6, 0.3, 0.0, distanceFromCenter*6.0);" +
                                          "}")
sight.fillColor = .clear
sight.fillShader = gradientShader

Here are helpful links: Drawing a circle with a shader in SpriteKit, https://thebookofshaders.com/02/, Add shader for SKShapeNode, https://developer.apple.com/documentation/spritekit/skshapenode/controlling_shape_drawing_with_shaders.

The above solution was half way, but after another couple of hours I managed to achieve what I wanted to. enter image description here

let pos = n.sprite.position
let frame = n.sight.frame
let x = (pos.x - frame.minX) / frame.width
let y = (pos.y - frame.minY) / frame.height
let gradientShader = SKShader(source: "void main() {" +
                                          "float distanceFromCenter = distance(v_tex_coord, npcLocation);" +
                                          "float alpha = 0.8 - distanceFromCenter;" +
                                          "if (alpha < 0) { alpha = 0.0;}" +
                                          "gl_FragColor = vec4(0.0, 0.0, 0.0, alpha);" +
                                          "}")
  gradientShader.uniforms = [SKUniform(name: "npcLocation", vectorFloat2: vector_float2(Float(x), Float(y)))]
  n.sight.fillColor = .clear
  n.sight.fillShader = gradientShader

Upvotes: 4

Related Questions