Jump to content

[TOPIC: topicViewTemplate]
[GLOBAL: userSmallPhoto]
Photo

Shadows for polygons and rects
Started by XeduR @Spyric Aug 01 2018 09:03 AM

41 replies to this topic
shadows
[TOPIC CONTROLS]
Page 2 of 2 1 2
This topic has been archived. This means that you cannot reply to this topic.
[/TOPIC CONTROLS]
[modOptionsDropdown]
[/modOptionsDropdown]
[reputationFilter]
[TOPIC: post.html]
#26

XeduR @Spyric

[GLOBAL: userInfoPane.html]
XeduR @Spyric
  • Contributor

  • 839 posts
  • Corona SDK

I'm currently in the process of analysing some such cases. The gains might not seem (or even be) all that fancy or worth thinking about, but I'd still like to optimise this particular module as much as I reasonably can.

One thing I've just done is changing math.atan2 functions to math.atan, which is much faster than the aforementioned. Atan2 is the safer option and handles more scenarios than atan, but in my case, I've swapped all uses of atan2 to atan when I know that these scenarios are impossible.

From my tests, atan seems to be about 40% to 50% faster, which is great, but when you consider that atan2 takes roughly 0.000125ms to run, the gains don't seem particularly impressive. If I create dynamic shadows for 20 objects per frame, with roughly 12 uses of atan2 per object, then using atan saves me about 0.0165ms per frame.



[TOPIC: post.html]
#27

sporkfin

[GLOBAL: userInfoPane.html]
sporkfin
  • Contributor

  • 500 posts
  • Corona SDK

Hmmm. . . I always use Atan2.  Does using Atan instead of Atan2 mean that you are calculating quadrant info somewhere else or is Atan sufficient by itself?  I've been running some calculations and Atan and Atan2 seem to be roughly equivalent for me but, surprisingly, math.round is a processing time hog compared to the other math functions (1.5 to 3 times more time intensive than the trig, floor, and ceiling functions).  Who knew?



[TOPIC: post.html]
#28

XeduR @Spyric

[GLOBAL: userInfoPane.html]
XeduR @Spyric
  • Contributor

  • 839 posts
  • Corona SDK

In certain cases, such as math.atan(0/0), Atan can result in -1.#IND, which will may lead to a crash if you try to do something with it. By using math.atan2(0,0), according to the documentation, you won't run into these kind of issues.

I actually came to the same realisation as you regarding math.round versus math.floor, which is why I've changed the rounding functions to instead use mathFloor(number*10)*0.1, which still keeps the numbers sufficiently accurate for my use.



[TOPIC: post.html]
#29

Michael Flad

[GLOBAL: userInfoPane.html]
Michael Flad
  • Contributor

  • 231 posts
  • Corona SDK

That's interesting, round is not a function in the original Lua math library as most of the other functions is, so I guess it was added by Corona devs as a convenience function.

 

I don't understand the replacement with mathFloor(num*10)*0.1 as this is something completely different, you don't get an integer at the end but a float with a fractional part, but of course it's what works/you need, that's a great find (I probably wouldn't have benchmarked round myself as I would've expected this to be way faster than trig function).

Btw. did you try the "classical" way to round a float mathFloor( value + 0.5 ) - should be slightly faster as it's just less operations to be done and the result is the actual rounded integer.



[TOPIC: post.html]
#30

XeduR @Spyric

[GLOBAL: userInfoPane.html]
XeduR @Spyric
  • Contributor

  • 839 posts
  • Corona SDK

The reason why I use mathFloor(num*10)*0.1 is because I require more accuracy than a floored integer provides, but unless I reduce the original decimal count drastically, I need to implement other checks at various points to ensure that certain problems don't pop up later.

That classical way is actually quite a neat trick.



[TOPIC: post.html]
#31

davebollinger

[GLOBAL: userInfoPane.html]
davebollinger
  • Corona Geek

  • 1,348 posts
  • Enterprise

 round is not a function in the original Lua math library as most of the other functions is, so I guess it was added by Corona devs as a convenience function.

 

and it's in Lua not C (thus performance diff vs other lib fns)

 

[edit: retracted, apparently fixed]



[TOPIC: post.html]
#32

sporkfin

[GLOBAL: userInfoPane.html]
sporkfin
  • Contributor

  • 500 posts
  • Corona SDK

"classical" method - Ah, now I understand this snippet I got from one of you guys a while back.  I only use it when I'm rounding to larger numbers or decimals less than 1 but I should probably start using it for everything.

 

local function round(num, idp)
     local mult = 10^(idp or 0)
     return mathFloor(num * mult + 0.5) / mult
end


[TOPIC: post.html]
#33

XeduR @Spyric

[GLOBAL: userInfoPane.html]
XeduR @Spyric
  • Contributor

  • 839 posts
  • Corona SDK

A brief update on the module.

I've managed to improve the module's performance by quite a bit. For instance, creating 1000 shadows for convex shapes with around 4-5 vertices in a single loop took around 60ms on my computer. Creating 1000 shadows for concave shapes with 12 vertices in the same loop took around 400ms. Concave shapes with around 6-7 vertices took around 200ms. I know a few additional places where I can further optimise the module's performance.

The current shadow for circular shapes is actually based on a cylinder, so next I am going to try to create a shadow caster for spheres as well.

Another major improvement since the last update is the proper inclusion of the z-axis, which turns this into a pseudo 3D shadow system. What this means is that every object needs to have a height value and the light will also need a z-coordinate. This method guarantees that the shadows are actually cast realistically and that their behaviour is consistent between different objects. It also means that if the light's z-coordinate is less than an object's height, then the shadows that it casts will extend beyond the edges of the screen.

Finally, I created an option to choose whether to insert the shadows into a snapshot group or a regular display group. The two main differences between these two options. Firstly, shadows that are inserted into a regular display group overlap each other, creating darker regions, whereas shadows that are in a snapshot group do not create darker regions if they overlap. Secondly, as many of you already know, image effects, like blur, blend modes, etc., can be applied to the snapshot group.

The linked gif shows the new z-axis and blurred snapshot group in action.



[TOPIC: post.html]
#34

Michael Flad

[GLOBAL: userInfoPane.html]
Michael Flad
  • Contributor

  • 231 posts
  • Corona SDK

This keeps getting better and better ... really looking forward/hope to see this as a plugin in the future.



[TOPIC: post.html]
#35

sporkfin

[GLOBAL: userInfoPane.html]
sporkfin
  • Contributor

  • 500 posts
  • Corona SDK

@Xedur - Great work - I’m lovin’ this!

 
Questions:
1. When you create the sphere shadow castor will that correct or the oversized shadow when the light source is directly above the sphere?
 
2. Did you find any speed benefits using inline trig values from a table rather than using math calls for each event?


[TOPIC: post.html]
#36

XeduR @Spyric

[GLOBAL: userInfoPane.html]
XeduR @Spyric
  • Contributor

  • 839 posts
  • Corona SDK

@Michael Flad, this will definitely come out as a plugin for Corona at some point. I'm uncertain about the specifics yet.

​@sporkfin, there is a great difference between how cylinders and spheres cast their shadows. I am quite certain that the current shadow for the cylinder is not too large for when the light is on top of the object. The shadow's length depends on how high the light is (z-coordinate) and how tall the cylinder is. For instance, if the cylinder is 100 pixels tall and the light is only a few pixels above it, then the resulting shadow's radius would easily be over 3000 pixels.

If I do manage to create the shadow caster for a sphere, then it'll always create a smaller shadow for lights that are on top of it (as seen in the attached image).

As for the inline trigonometry, I tried using optimised Taylor series inline, but the performance didn't really improve. I'm still looking into a few other possibilities. One nifty trick that many probably already knew is that multiplication is 2-3 times faster than using exponents, for instance, writing Pythagoras theorem as "a*a + b*b = c*c" is much faster than "a^2 + b^2 = c^2".

Attached Files



[TOPIC: post.html]
#37

sporkfin

[GLOBAL: userInfoPane.html]
sporkfin
  • Contributor

  • 500 posts
  • Corona SDK

@Xedur  I can't wait to play whatever game you are working on.  Yes, I use multiplication everywhere as that seems to be to go for efficiency even for division - ha!

 

Our discussion about about atan and atan2 lead to an a-ha moment where I visited an old module that sometimes misfires and found that I had used atan instead of atan2.  Thanks!



[TOPIC: post.html]
#38

XeduR @Spyric

[GLOBAL: userInfoPane.html]
XeduR @Spyric
  • Contributor

  • 839 posts
  • Corona SDK

@sporkfin, I'm glad to hear our discussion has already benefited you as well! Speaking of which, after spending a few days reading up on Lua's trigonometric functions as well as trying some things out, I am confident that improving the speed of those calculations is not feasible in Lua. While some methods, like Taylor series, can come very close, the little increase in speed comes at the expense of too much accuracy.


Also, regarding the shadow module, I am in awe at how basic things I sometimes forget. About the questions regarding adding gradients to the shadows, I just realised that the easiest method to implement them is to add a mask to the shadowGroup. By attaching a gradient mask (see attachment) to the shadowGroup and moving it along with the light, then the gradient effect works much better (and more efficiently). Plus, it can also be used to fake how distant objects cast shadows.

 

The linked gif shows the shadow module using the gradient mask.

Attached Files



[TOPIC: post.html]
#39

sporkfin

[GLOBAL: userInfoPane.html]
sporkfin
  • Contributor

  • 500 posts
  • Corona SDK

Awesome, I came to the same conclusion and use a gradient mask as a vignette to cover the landscape and background.  My shader works great on small and medium objects but large objects like walls or landscapes don't look good with normal map composite paint effects unless you break them down into little units and then it becomes computationally expensive.

 

An added benefit to having to separate shading solutions is that I can easily produce custom effects like a dark room with only the characters slightly visible.



[TOPIC: post.html]
#40

SGS

[GLOBAL: userInfoPane.html]
SGS
  • Corona Geek

  • 2,110 posts
  • Corona SDK

do be aware of the masking limits



[TOPIC: post.html]
#41

sporkfin

[GLOBAL: userInfoPane.html]
sporkfin
  • Contributor

  • 500 posts
  • Corona SDK

Limits as in nesting limits?



[TOPIC: post.html]
#42

XeduR @Spyric

[GLOBAL: userInfoPane.html]
XeduR @Spyric
  • Contributor

  • 839 posts
  • Corona SDK

The current maximum nested masks that the module can create is 2.

The shadows for rects and simple convex and concave polygons are all made out of a single polygon. In the case of cylinders, shadows are either made out of a single circle or a circle and a polygon, depending on if the light source is on top of the cylinder. In the case that the light is outside of the cylinder, a container is used to clip the circle shadow shape (see attached image). This means that there will almost certainly be one mask, although at this point it isn't nested yet, per each cylinder.

As previously mentioned, all of these shadows are then placed in either a normal display group or a snapshot group. Then, if a gradient mask is applied to the group, we reach a grand total of 2 nested masks, leaving room for one more mask, if someone somehow has the need for it.

Attached Files




[topic_controls]
Page 2 of 2 1 2
 
[/topic_controls]