Swift: Make translucent overlapping lines of the same color not change color when intersecting
Asked Answered
S

4

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


Stellular answered 21/12, 2016 at 8:10 Comment(6)
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.Obsolete
@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.Desimone
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.Crustaceous
Yeah they are moving so it is a bit difficult and they do update several times a second but thanks anyway.Stellular
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.Lizliza
There is a coloured background. This is just a test project to demonstrate my point.Stellular
D
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

Desimone answered 21/12, 2016 at 12:7 Comment(9)
Thanks!! Since this is a way that works, I'll somehow try to implement this into my program.Stellular
If you have any problems, let me know. I'm no expert... but understand graphics a little.Desimone
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.Stellular
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)Desimone
@Stellular 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.Desimone
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?Stellular
Don't worry I figured it out myself. I'm going to edit my original post now with what has been solved.Stellular
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 answerInaccurate
I don't do it several times a second or every second so it should be alright.Stellular
D
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.

Desimone answered 21/12, 2016 at 8:47 Comment(3)
I'll see the effect of these in my program. Thanks!Stellular
As I checked, you're right since none of them give the desired effect. There must be a way to achieve this though since I'd think that it wouldn't be a rare issue.Stellular
I have created a new post now which if it is solved it could substitute this problem.Stellular
D
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)
Duty answered 21/12, 2016 at 12:24 Comment(1)
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.Stellular
I
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)
Inaccurate answered 21/12, 2016 at 15:48 Comment(11)
Weird, it has to be a SKEffectNode only..... ahhhh it renders the children into a separate buffer, that is whyInaccurate
You may want to just apply a shader to cross then, not sure how much resources an effect node will consumeInaccurate
What is a shader and how do I use it?Stellular
it is code that is used to on the GPU, but doesn't matter, since SKShapeNode does not support itInaccurate
Is there any possible to make your original post work? If it could that would be really helpful.Stellular
I mean your original post without the SKEffectorNode. When you added the lines to a parent node, then changed the alpha of the parent. Shouldn't that change the alpha value of all the children too.Stellular
Cant, i tried writing a shader. But i do not know too much about themInaccurate
Should I try creating a new post regarding this?Stellular
No it is not suppose to, but i guess because it is optimizing using a single draw pass it is blending this wayInaccurate
Other people may be able to write a shader though right? Since you said you don't know too much about them, other people do and might be able to write one.Stellular
Absolutely dont see why notInaccurate

© 2022 - 2024 — McMap. All rights reserved.