📜 ⬆️ ⬇️

Lightning bolts



You fly your ship through a cave, dodging enemy fire. However, pretty soon you realize that there are too many enemies and it looks like this is the end. In a desperate attempt to survive, you press the button. Yes, that button. On the one that you have prepared for a special occasion. Your ship is charging and releases deadly lightning at enemies, one after another, destroying the entire enemy fleet.

At least that is the plan.
')
But how exactly do you, as a game developer, render this effect?

We generate lightning


As it turned out, generating lightning between two points can be a surprisingly simple task. It can be generated as an L-System (with a small random number during generation). Below is an example of a simple pseudo-code (this code, like everything else in this article, refers to 2d lightning. This is usually all you need. In 3d, just generate a lightning so that its displacements relate to the camera plane. Or you can generate a complete lightning in all three dimensions - the choice is yours)

segmentList.Add(new Segment(startPoint, endPoint)); offsetAmount = maximumOffset; //     for each iteration // (  ) for each segment in segmentList //    ,       segmentList.Remove(segment); //      midPoint = Average(startpoint, endPoint); //  midPoint       midPoint += Perpendicular(Normalize(endPoint-startPoint))*RandomFloat(-offsetAmount,offsetAmount); //    ,      //    ()  segmentList.Add(new Segment(startPoint, midPoint)); segmentList.Add(new Segment(midPoint, endPoint)); end for offsetAmount /= 2; //               end for 


In fact, each iteration of each segment is divided in half, with a slight shift of the center point. Each iteration of this shift is halved. So, for five iterations the following will turn out:

image
image
image
image
image

Not bad. Already looks at least like a zipper. However, lightning often has branches running in different directions.

To create them, sometimes when you divide a lightning segment, instead of adding two segments, you need to add three. The third segment is just a continuation of the lightning in the direction of the first (with a small random deviation).

 direction = midPoint - startPoint; splitEnd = Rotate(direction, randomSmallAngle)*lengthScale + midPoint; // lengthScale   < 1.  0.7  . segmentList.Add(new Segment(midPoint, splitEnd)); 


Then, at the following iterations, these segments are also divided. It would also be nice to reduce the brightness of the branch. Only the main lightning should have full brightness, since only it is connected to the target.

Now it looks like this:

image
image
image

Now it looks more like a zipper! Well ... at least the form. But what about the rest?

Add light


Originally, the system developed for the game used rounded rays. Each lightning segment was rendered using three quadrilaterals, for each of which a light texture was used (to make it look like a rounded line). The rounded edges intersect, forming joints. It looked pretty good:

image

... but, as you can see, it turned out pretty bright. And, as the lightning decreases, the brightness only increased (as the intersections became closer). When trying to reduce brightness, another problem arose - the transitions became very noticeable, like small dots throughout the lightning.
If you have the ability to render a lightning bolt on the voice-over buffer, you can render it by applying the maximum mix (D3DBLENDOP_MAX) to the voice-over buffer, and then simply add the result to the main screen. This will avoid the problem described above. If you do not have such an opportunity, you can create a vertex cut from a lightning by creating two vertices for each point of the lightning and moving each of them in the direction of the 2D normal (normal is perpendicular to the middle direction between two segments going to this vertex).

You should get something like this:

image

Animating


And this is the most interesting. How do we animate this thing?

Having a little experimenting, I found the following useful:

Each zipper is actually two zippers at a time. In this case, every 1/3 of a second, one of the lightning ends, and the cycle of each lightning is 1/6 of a second. With 60 FPS it will turn out like this:


That is, they alternate. Of course, a simple static attenuation does not look very good, so each frame makes sense to shift a little each point (it looks especially cool to move end points more strongly - this makes it all the more dynamic). The result is:



And, of course, you can move the end points ... say, if you are aiming at moving targets:



And it's all! As you can see, making a cool-looking zipper is not that difficult.

Source: https://habr.com/ru/post/230483/


All Articles