# GMLscripts.com

Discuss and collaborate on GML scripts

You are not logged in.

## #21 2013-06-08 22:48:19

icuurd12b42
Member
Registered: 2008-12-11
Posts: 303

Good good

Offline

## #22 2013-07-09 18:33:01

xot
Registered: 2007-08-18
Posts: 1,202

### Re: NAILS prototype - raster-based light and shadow engine

A small update, if you can believe it.

I was checking this out again in preparation for building a shader-based version for GM:Studio. I thought I might as well port the original as well. That's when the trouble started.

GM:Studio does a funny thing. When it draws d3d models, it hijacks the blue channel's least-significant bit for a flag. To quote Mike Dailly:

Mike Dailly wrote:

One bit is reserved so that when you change colour and draw, it knows which verts are effected. It needs to know which vertex is a global colour (draw_set_colour) and which is set as a prim colour. This is also why its VERY slow to change colour and draw a model again - but it was needed for compatibility.( pre-shaders )

Of red, green, and blue, the human eye is least sensitive to blue. It seems sensible to steal one bit from blue for this flag since the change would be imperceptible under normal circumstances.

However, this has an unfortunate effect on the "smear" feedback loop. Any channel which is not 100% brightness will become darker on each pass. After several passes the blue channel is virtually erased and the resulting shadow map will remove any blue from the lights it is used with, so everything ends up looking yellow. The quick workaround is to restore a tiny bit of blue to the warped surface before beginning the feedback loop. The better solution is to use GM:Studio's new vertex builder feature (thanks for the suggestion, Mike).

GM:Studio also inserted brand new artifacts into the warped surface rendering. Parts at the bottom edge of the rectangular-to-polar map (the edge farthest from the light source) were wrapping around to the top edge (at the light source). These artifacts were casting long spurious shadows from the light source. I was able to overcome the problem by turning off texture repeating at that stage. I tried the same thing in the GM8 version and lo and behold the dreaded single pixel artifact that appears at the light source in that version was finally cured.

Here is an updated GM8 version:

http://host-a.net/u/xot/raster-transform9.gmk

And here is a GM:Studio version:

A shader version should hopefully be coming soon. I have it worked out in my head but it uses some techniques I've not tried yet. I think it will be a lot faster since it will replace the feedback loop with a single pass.

Abusing forum power since 1986.

Offline

## #23 2013-08-15 08:03:22

Miki1990
Member
Registered: 2013-08-15
Posts: 2

### Re: NAILS prototype - raster-based light and shadow engine

Hi xot, I have downloaded your newest example for the raster shadows for GM: Studio and tried to understand how exactly it's implemented, but unfortunately my expertise in 3d is very little and i didn't fully grasp how your code is implemented. What I want to accomplish is to make the light source follow the mouse for example, but I didn't understand which variables I need to change, hopefully you'll clear that out for me. And some more comments in the script itself could be useful.

Offline

## #24 2013-08-15 14:30:20

xot
Registered: 2007-08-18
Posts: 1,202

### Re: NAILS prototype - raster-based light and shadow engine

Sounds like you need some help understanding d3d transforms. This doesn't use any 3D but the d3d transform functions can be very useful for simplifying many 2D drawing tasks. Thay can be confusing until you understand how to visualize them. Maybe this topic will be helpful:

http://gmc.yoyogames.com/index.php?showtopic=465513

For your specific request, you need to make a few small changes.

First, you'll probably want to remove objMouseFollower from the room.

Second, for the shadows_build() script, in the // Paint Shadows section after:

Expandd3d_transform_set_scaling(scale,scale,1);

Expandd3d_transform_add_translation(0.5*w-mouse_x,0.5*h-mouse_y,0);

That places the drawing of the shadow casters relative to the position of the mouse pointer (or any light source) placed at the center of the surface.

Third, for the shadows_draw() script, change it to:

Expanddraw_surface(surf,mouse_x-0.5*w,mouse_y-0.5*h);

This is like the inverse transformation of the code above to draw the surface in the correct position relative to the mouse pointer (or any light source). I might have done this with d3d transforms, but this is simpler. That also fixes a stupid bug where I put the draw_surface() arguments in the wrong order. I'll update the example with that fix and a couple of others. You should download the new version before making these changes.

Finally, I use mouse_x and mouse_y directly here for simplicity, but ideally you should be storing those values in a variable each step so that they are consistent throughout the frame. Otherwise moving the mouse quickly may display a mismatch between where the shadows are created on the surface and where the surface is drawn.

Abusing forum power since 1986.

Offline

## #25 2013-08-16 01:17:35

Miki1990
Member
Registered: 2013-08-15
Posts: 2

### Re: NAILS prototype - raster-based light and shadow engine

Thanks for the quick reply. I followed your explanation and managed to get it to work . now I will try to add it to the project i'm working on, hopefully I won't have any difficulties with it. BTW I just wanted to say that I have looked for the best way to implement dynamic shadows and it seems to me that this is the best way to do it, so again thank you.

Offline

## #26 2013-08-16 14:43:02

xot
Registered: 2007-08-18
Posts: 1,202

### Re: NAILS prototype - raster-based light and shadow engine

I hope you find it useful. As long as you have a decent fill-rate on your GPU, it works pretty good. By reducing the size/resolution of the surface you can gain some speed.

I tried to adapt this method to shaders but I didn't have any luck. The compiler complained that it couldn't determine when the "smear" loop ended or something. It still doesn't make sense to me, but I know more about shaders now so I'll probably give it another try.

Abusing forum power since 1986.

Offline

## #27 2013-11-21 10:24:49

pcarras
Member
Registered: 2013-11-21
Posts: 1

### Re: NAILS prototype - raster-based light and shadow engine

Great job with this method for casting 2D shadows, it completed blown my mind away!.   Have you made any advances in the importing this approach using shaders?.  I'm trying also to get used to shaders but with limited success.

Offline

## #28 2013-11-21 20:16:10

xot
Registered: 2007-08-18
Posts: 1,202

### Re: NAILS prototype - raster-based light and shadow engine

Not yet but I believe I understand how to get around the problem I was having with smear loop. If a loop has many iterations the compiler will give up. The trick is to use two smaller nested loops.

To be honest, even with this information, I find working with GM:Studio irritating for a number of reasons. A few of these are apparently fixed in the update released today. Fingers crossed, I'll be inspired.

Abusing forum power since 1986.

Offline

## #29 2013-12-24 13:52:44

DAG
Member
Registered: 2013-08-03
Posts: 1

### Re: NAILS prototype - raster-based light and shadow engine

Nice! But I can not set the alpha (transparency) for the shadow.

Offline

## #30 2013-12-24 16:51:12

xot
Registered: 2007-08-18
Posts: 1,202

### Re: NAILS prototype - raster-based light and shadow engine

This works by producing a traditional light map. You draw your scene fully illuminated and then draw the light map on top of everything using a multiplicative blend mode, eg. draw_set_blend_mode_ext(bm_dest_color, bm_zero) or draw_set_blend_mode_ext(bm_zero, bm_src_color).

If you need multiple lights, each of their light maps should be composited to a master light map surface (cleared to black) using an additive blend mode, eg. draw_set_blend_mode_ext(bm_one, bm_one). This master light map is used to light your entire scene in one pass. You may want something like this even with one light, if the light map doesn't entirely cover the view. Lighting a scene with multiple light maps in successive passes will not work, as the shadows of each successive light will remove any existing light.

If what you are expecting is a surface with transparent areas where there is light and opaque areas where there is shadow, it may be possible to alter this to do that, but I'm not sure. It would certainly be more complicated and take a few extra steps. While it is also possible to convert the image from grayscale to an alpha mask using sprite_create_from_surface() and sprite_set_alpha_from_sprite() (or their background equivalents), it probably would be too slow for practical use.

Is there a specific reason you can't use it as designed? Am I understanding your need correctly?

Abusing forum power since 1986.

Offline

## #31 2015-05-11 18:49:45

mattyrage
Member
Registered: 2015-05-11
Posts: 1

### Re: NAILS prototype - raster-based light and shadow engine

Hey xot, great concept you got here! I'm having trouble getting it working in GM Studio (v1.4.1567).

The error that returned was:

surface stack is full - ensure surface_reset_target() is called for each surface_set_target().

This occurs in shadows_build lines 57-60.

So I added the resets in relevant spots... and sure, it runs, but all I get is a plain rendered background. Could I have configured something in GM wrong?

Offline

## #32 2015-05-15 01:25:29

xot
Registered: 2007-08-18
Posts: 1,202

### Re: NAILS prototype - raster-based light and shadow engine

Welcome to the forum, mattyrage.

The problem seems to be a bug in the draw_circle() family of functions. It appears circles are being drawn offset by one pixel/unit. However, since this particular circle is only one unit in diameter and is being scaled to the correct size by a d3d transform, the error is magnified and it is drawn in completely the wrong place. This happens to be entirely outside of the target surface, so the exact problem isn't obvious at first.

By drawing the circle outside of the d3d transform and scaling explicitly, it can be made to work.

Expand    // Unwarp Shadows
surface_set_target(surf);
draw_clear_alpha(c_black,1);
draw_set_circle_precision(res);
draw_circle_colour(0.5*w*scale,0.5*h*scale,0.5*w*scale,c_white,c_black,false);
d3d_transform_stack_push();
d3d_transform_set_scaling(w*scale,h*scale,1);
texture_set_repeat(false);
texture_set_interpolation(true);
draw_set_color(c_white);
draw_set_blend_mode_ext(bm_src_alpha_sat,bm_src_color);
d3d_model_draw(modelR2P,0,0,0,surface_get_texture(work));
draw_set_blend_mode(bm_normal);
d3d_transform_stack_pop();
surface_reset_target();

Other options are to draw the circle gradient using a sprite or background image. That's what I would normally do. You could also add a linear gradient (top-to-bottom) to the warped shadow surface after the "smear" using some blending, or you could build the gradient into the second warping model with vertex coloring.

Just a final note. This method is pretty slow in GM:Studio compared to previous versions. A shader would give you much better performance. The game I'm working on requires such a shader. It already has one written by someone else but I may write a new one. If I do, I'll update this topic with it.

EDIT:

This post was edited several times as I tried and failed to correctly diagnose the problem.

A bug report has been filed. http://bugs.yoyogames.com/view.php?id=17937

Last edited by xot (2015-05-15 02:40:38)

Abusing forum power since 1986.

Offline

## #33 2015-10-01 07:37:30

Juju
Member
Registered: 2015-10-01
Posts: 44

### Re: NAILS prototype - raster-based light and shadow engine

Thought I'd chip in here with an update for GM:S integrating the suggestions you made earlier this year. Additions I've made are twofold:

Firstly, I've swapped over the d3d model method for one that uses a stripped-back vertex buffer. This obviates the need for the blue-bit fix and only increases reliability (I was having random crashes if I was creating/destroying lights repeatedly, not using a d3d model seems to improve the situation).

Secondly, I implemented a quick n' dirty pair of shaders, raycast and smear, to replace the surface-based fill. Frame rates for me jump from ~550 to >1700 which is a considerable improvement. Self-shadow is also implemented, though it looks a bit weird in 2D.

...unfortunately, I ran into that for loop issue as well. It seems that trying to using an increment of less than 1/256 causes problems. This could be chip-dependent, this could be some kind of limit on the accuracy of the floating point number system. It could be a bug in GM, shock-horror. I have no idea. This limits the vertical (i.e. distance-from-light) resolution of the raycasting shader which isn't a catastrophe but it is annoying to have continual little pixel glitches everywhere. The shader can probably be improved to massage these errors.

Also, I'm finding it conceptually very difficult to see how you'd use this technique for non-circular lights e.g. a flashlight. I think you'd create a different P2R/R2P model pair and limit the angular range of the model creation function but how would you go about having a non-centred origin on the surface for the light? Once of the assumptions of the model generator is that the maximal distance/radius can be fully represented over the angular range which, for a non-circular or non-centred light, isn't the case because of the confines of the lighting surface (surf). There must be some kind of transform that can be used in the model generator but my brain refuses to stretch that far...

Last edited by Juju (2015-10-03 09:25:20)

Offline

## #34 2015-11-18 00:10:47

xot
Registered: 2007-08-18
Posts: 1,202

### Re: NAILS prototype - raster-based light and shadow engine

I finally had a chance to look at this. Nice work, man.

A non-circular light works exactly the same as a circular one. Just draw the light in the shape you want, the shadows are the same. A flashlight has a conical beam which in 2D is just a circular arc.

If you want a purely directional light, like sunlight, that's a different matter. In that case I think you would skip the transformation models and just rotate the shadow casters relative to the direction of the light.

I've not given any of this much thought and it has been a long time since I messed with this prototype but those are my initial impressions.

Sorry about the delay in responding to your awesome post and thanks for the demo!

Abusing forum power since 1986.

Offline

## #35 2015-11-18 17:27:04

Juju
Member
Registered: 2015-10-01
Posts: 44

### Re: NAILS prototype - raster-based light and shadow engine

Aye, I understood the necessary adjustments for non-circular light. The issue is that you always need to have the origin of the light in the precise middle of the surface. If there was a way to use non-square, non-centred orientations then there would be less raycasting and, by extension, better performance.

Offline

## #36 2015-11-19 22:40:35

xot
Registered: 2007-08-18
Posts: 1,202

### Re: NAILS prototype - raster-based light and shadow engine

Ah, I see what you're getting at. It should be possible with an altered model and maybe some extra steps to transfer the shadow casters into the appropriate space of a rotated surface. You've inspired me to work on this some more and have some ideas about doing it differently but I just don't have time to work on it right now. Things are hectic here.

Abusing forum power since 1986.

Offline

## #37 2015-11-20 09:32:17

Juju
Member
Registered: 2015-10-01
Posts: 44

### Re: NAILS prototype - raster-based light and shadow engine

When you can, when you can. Probably worth talking to icuurd about meshing either NAILS or YAILSE (probably the latter) with the contemporary work they're doing on normal/specular/occlusion mapping.

Last edited by Juju (2015-11-20 09:32:37)

Offline

## #38 2015-11-22 22:17:00

xot
Registered: 2007-08-18
Posts: 1,202

### Re: NAILS prototype - raster-based light and shadow engine

I work closely with icuurd12b42 and The Mojo Collective. TMC Lux is coming along well.

Abusing forum power since 1986.

Offline