4

Currently I have drawn 2 lines on the screen which are the same color but both have an alpha value less than 1. When these lines intersect, the intersection is a different color than the rest of the lines. There was a previous post addressing the same issue: swift drawing translucent lines, how to make overlapping parts not getting darker? However, this post wasn't sufficiently answered. I draw the lines currently like this:

    var points = [CGPoint]()

    points = [CGPoint(x: -100, y: 100), CGPoint(x: 100, y: -100)]
    let FirstLine = SKShapeNode(points: &points, count: points.count)
    FirstLine.strokeColor = UIColor.init(red: 0.25, green: 0.62, blue: 0.0, alpha: 0.5)
    FirstLine.lineWidth = 30
    addChild(FirstLine)

    points = [CGPoint(x: 100, y: 100), CGPoint(x: -100, y: -100)]
    let SecondLine = SKShapeNode(points: &points, count: points.count)
    SecondLine.strokeColor = UIColor.init(red: 0.25, green: 0.62, blue: 0.0, alpha: 0.5)
    SecondLine.lineWidth = 30
    addChild(SecondLine)

Here is what it looks like:

I understand why this occurs, but is there any way to make the intersection look the same so it looks much better?

Edit: I have decided to implement @Confused's answer. However, the problem now is that the texture always centres to the middle of the screen. This is an example:

The red cross is in its correct position as it is connecting the specified points together. However, once I make the red cross a texture, it always centres to the middle of the screen (the green cross is the red cross as a texture). Can I use any code that could re-position the texture to its correct position. Note: this code can't just work for this example, I need one that works all the time regardless of the red cross' position.


FINAL EDIT FOR PEOPLE WITH THE SAME PROBLEM

First, setup everything like this:

var points = [CGPoint]()
let crossParent = SKNode()
addChild(crossParent)

Please note THAT YOU MUST create a parent SKNode for the texture otherwise everything on the screen will become the texture and not just the node that you want. Then, add that parent node to the scene.

After, create the lines that you want (in this case the green cross):

//The first line of the green cross
points = [CGPoint(x: -300, y: 300), CGPoint(x: -100, y: 100)]
let FirstLine = SKShapeNode(points: &points, count: points.count)
FirstLine.strokeColor = UIColor.init(red: 0.25, green: 0.62, blue: 0.0, alpha: 1.0)
FirstLine.lineWidth = 30
crossParent.addChild(FirstLine)

Remember to NOT add the first line that you create to the scene, but rather to the parent SKNode that you made at the beginning. Also, set the alpha value to 1.0 for every line that you draw. Then add your other lines:

//The second line of the green cross
points = [CGPoint(x: -100, y: 300), CGPoint(x: -300, y: 100)]
let SecondLine = SKShapeNode(points: &points, count: points.count)
SecondLine.strokeColor = UIColor.init(red: 0.25, green: 0.62, blue: 0.0, alpha: 1.0)
SecondLine.lineWidth = 30
FirstLine.addChild(SecondLine)

Note that you MUST add that line to the first line like this and not to the scene. If you are adding more than one line after adding the first line, then add it to the first line like I have done here too for each consecutive line that you add.

Now, create the texture like this:

 let tex = view?.texture(from: FirstLine)
 let cross = SKSpriteNode(texture: tex, color: .clear, size: (tex?.size())!)
 cross.alpha = 0.5
 addChild(cross)

After doing this, whatever you call cross will be your texture, and you can change the alpha value like I have done here to anything you like and the image will not have varying colors. Remember to then add that texture to the scene.

Finally, you may notice that the texture is not in the same position as where you originally put the points. You can put it back into the same position like this:

cross.position = CGPoint(x: (FirstLine.frame.midX), y: (FirstLine.frame.midY))

Hope this helps :) Thank you to @Confused for the texture part of the program :D


Community
  • 1
  • 1
J.Treutlein
  • 963
  • 8
  • 23
  • 1
    This is happening because at intersection point colors are overLapped. So you can handle it by not using Alpha < 1. Choose a sold color with alpha 1.0 and this will not happen. – Umair Afzal Dec 21 '16 at 08:14
  • @UmairAfzal "I understand why this occurs, but is there any way to make the intersection look the same so it looks much better?" He wants alpha, doesn't want blending. – Confused Dec 21 '16 at 08:37
  • Are these lines static or moving? Is there some way to combine the 2 SKShapeNodes into a single node and then redraw it as a single shape? I know that there is a way to extract the texture created from a sprite and a crop node and you could generate a new node from that. Possible performance hit though, especially if this is having to be done many times per second. – Steve Ives Dec 21 '16 at 09:49
  • Yeah they are moving so it is a bit difficult and they do update several times a second but thanks anyway. – J.Treutlein Dec 21 '16 at 10:25
  • Is there anything behind them? Why do they need to be transparent? Can't you just use the solid colour instead? If they're just on a solid black background then adding the transparency doesn't give you anything. – Fogmeister Dec 21 '16 at 12:10
  • There is a coloured background. This is just a test project to demonstrate my point. – J.Treutlein Dec 21 '16 at 19:56

4 Answers4

2

This is a technique. It does not solve your problem, nor directly answer your question. Instead it offers a way to have the desired result, but without the flexibility and inherent nature of lines you actually want.

You can create textures from anything you draw with any number of nodes, with any number of techniques.

You do this (easiest way) by attaching all the drawing elements to a single node, within an SKView space that you have access to, and then render the "parent" node of your drawn objects to a texture.

How does this help?

I'm glad you asked:

You can draw everything at an opacity level of 100%, and render it to a texture, then take that texture of your drawings, put it where you like, and reduce its opacity to any percentage you like, and get an even result. No bright spots where things overlay each other.

Here's code that does all the above:

   var points = [CGPoint]()

   points = [CGPoint(x: -100, y: 100), CGPoint(x: 100, y: -100)]
   let FirstLine = SKShapeNode(points: &points, count: points.count)
   FirstLine.strokeColor = UIColor.init(red: 0.25, green: 0.62, blue: 0.0, alpha: 1)
   FirstLine.lineWidth = 30
//      ^^ Note the FirstLine is not added to the Scene

   points = [CGPoint(x: 100, y: 100), CGPoint(x: -100, y: -100)]
   let SecondLine = SKShapeNode(points: &points, count: points.count)
   SecondLine.strokeColor = UIColor.init(red: 0.25, green: 0.62, blue: 0.0, alpha: 1)
   SecondLine.lineWidth = 30
   FirstLine.addChild(SecondLine)
// ^^ Note SecondLine being added to FirstLine, and that they both have alpha of 1

// Now the magic: use the view of the SKScene to render FirstLine and its child (SecondLine)
// They are rendered into a texture, named, imaginatively, "tex"

   let tex = view.texture(from: FirstLine)
   let cross = SKSpriteNode(texture: tex, color: .clear, size: (tex?.size())!)
       cross.alpha = 0.5

// ^^ The alpha of the above sprite is set to your original desire of 0.5
// And then added to the scene, with the desired result.
   addChild(cross)

and here's the result:

enter image description here

Confused
  • 6,048
  • 6
  • 34
  • 75
  • Thanks!! Since this is a way that works, I'll somehow try to implement this into my program. – J.Treutlein Dec 21 '16 at 20:24
  • If you have any problems, let me know. I'm no expert... but understand graphics a little. – Confused Dec 21 '16 at 20:27
  • Thanks so much! I have edited my post with my problem now. Thanks for the solution though, it works really well. I just am trying to figure out how to fix my issue now. – J.Treutlein Dec 21 '16 at 21:05
  • In my example code above, `cross` is an SKSpriteNode, so it has a position. You can tell it where you want it to go by: `cross.position.x = 300, cross.position.y = 400` or `cross.position = CGPoint(x: 300, y: 400)` – Confused Dec 21 '16 at 21:13
  • @J.Treutlein you can also adjust the zPosition of each cross object you make. And you can add crosses to another node, to, so that you can move them all together. Kind of like linking in Photoshop or After Effects. – Confused Dec 21 '16 at 21:15
  • I have actually noticed that by moving the position of cross, the red cross moves too. I think this is because the texture is taken from the view, so everything in the view. Is there anyway to solve this? – J.Treutlein Dec 21 '16 at 21:26
  • 1
    Don't worry I figured it out myself. I'm going to edit my original post now with what has been solved. – J.Treutlein Dec 21 '16 at 21:43
  • Only issue with this is creating a new texture is very slow, you are better off with SKEffectNode for speed purposes like in Nikolai's answer – Knight0fDragon Dec 21 '16 at 22:45
  • I don't do it several times a second or every second so it should be alright. – J.Treutlein Dec 22 '16 at 01:50
1

NO! I think is, unfortunately, the answer.

There are the following blend modes, none of which provide the result you're looking for:

case alpha // Blends the source and destination colors by multiplying the source alpha value.

case add // Blends the source and destination colors by adding them up.

case subtract // Blends the source and destination colors by subtracting the source from the destination.

case multiply // Blends the source and destination colors by multiplying them.

case multiplyX2 // Blends the source and destination colors by multiplying them and doubling the result.

case screen // Blends the source and destination colors by multiplying one minus the source with the destination and adding the source.

case replace // Replaces the destination with the source (ignores alpha).

The only option that might work is a custom shader that figures out if it's overlapping itself, somehow. But I have NO CLUE where to even start making something like that, or if it's even possible.

Confused
  • 6,048
  • 6
  • 34
  • 75
1
  1. Make the stroke color fully opaque.
  2. Add your path nodes to an SKEffectNode.
  3. Set the alpha of the effect node to the desired value.
  4. Put the effect node into your main scene.

While doing this you can also put all lines in one shape node:

let path = CGMutablePath()
path.move(   to: CGPoint(x: -100, y:  100))
path.addLine(to: CGPoint(x:  100, y: -100))
path.move(   to: CGPoint(x:  100, y:  100))
path.addLine(to: CGPoint(x: -100, y: -100))

let shape = SKShapeNode(path: path)
shape.strokeColor = UIColor(red: 0.25, green: 0.62, blue: 0.0, alpha: 1)
shape.lineWidth = 30

let effect = SKEffectNode()
effect.addChild(shape)
effect.alpha = 0.5
addChild(effect)
Nikolai Ruhe
  • 81,520
  • 17
  • 180
  • 200
  • Thanks for that. The only problem with doing it this way is that I don't think that I can change individually change the color of each line that I add to the path. – J.Treutlein Dec 21 '16 at 19:58
1

The problem is you are doing the alpha blend at the color level, do the alpha blending at the node level

var points = [CGPoint]()
var cross = SKEffectNode()
points = [CGPoint(x: -100, y: 100), CGPoint(x: 100, y: -100)]
let FirstLine = SKShapeNode(points: &points, count: points.count)
FirstLine.strokeColor = UIColor.init(red: 0.25, green: 0.62, blue: 0.0, alpha: 1)
FirstLine.lineWidth = 30
cross.addChild(FirstLine)

points = [CGPoint(x: 100, y: 100), CGPoint(x: -100, y: -100)]
let SecondLine = SKShapeNode(points: &points, count: points.count)
SecondLine.strokeColor = UIColor.init(red: 0.25, green: 0.62, blue: 0.0, alpha: 1)
SecondLine.lineWidth = 30
cross.addChild(SecondLine)
cross.alpha = 0.5
addChild(cross)
Knight0fDragon
  • 16,609
  • 2
  • 23
  • 44