[SOLVED] For the love of god, SURFACES. Help.

S

Sake_v2

Guest
I used to have a working colored lightning system in Game Maker 8.0 (I think it was that one) some time ago. Then, on the Studio version, the exact same code simply wouldn't work, and after a lot of trying around, I was able to get SOMETHING, but not as before. I was going to worry about this after the game was finished, but whatever, here is a image.



The image on the RIGHT is how it was before, there was no noticeable harsh border on the light, I was able to create a smooth blue lightning out of that with no border. Also the green light. The image on the left, is how it is now. It is KINDA blue and green, but I can't get the smooth lightning effect working again, always with that border. (Yes, I tried using bm_subtract and drawing a circle_colour from green to black and blue to black, that is the effect its creating).

The code before on the obj_light was this:
Create event
Code:
globalvar light;
light = surface_create(view_wview,view_hview);
Step event

Code:
surface_set_target(light);
draw_set_color(make_color_rgb(100,100,100));
draw_rectangle(view_xview,view_yview,view_wview,view_hview,false);
surface_reset_target();
Draw event

Code:
draw_set_blend_mode(bm_subtract);
draw_surface(light,view_xview,view_yview);
draw_set_blend_mode(bm_normal);
Then on the blue, green light etc:
End step event:
Code:
size = 300;

draw_set_blend_mode(bm_subtract);
surface_set_target(light);
draw_ellipse_color(x-size/2-view_xview,y-size/2-view_yview,x+size/2-view_xview,y+size/2-view_yview,c_blue,c_black,false);
surface_reset_target();
draw_set_blend_mode(bm_normal);
Unfortunately on Studio this stopped working, even using now the draw_circle_coloUr and everything. I'm pretty sure because the draw_set_color(make_color_rgb(100,100,100)); to decide how dark it is doesn't work anymore, instead I have to draw the rectangle with draw_set_color(c_black) and then draw_set_alpha(0.8) or something to determine how dark. Any help on creating the effect on the RIGHT again, would be appreciated.
 

dphsw

Member
If I understand correctly, you're saying that some of your drawing code (drawing to surfaces) is in Step events. I don't know how things were different in previous versions (I've only used GMS) but as far as I know you can't do any drawing at all in step events, only in draw events. If you need to make sure some stuff is drawn before the draw event, there's also the 'Draw Begin' event for that purpose.
 
S

Sake_v2

Guest
If I understand correctly, you're saying that some of your drawing code (drawing to surfaces) is in Step events. I don't know how things were different in previous versions (I've only used GMS) but as far as I know you can't do any drawing at all in step events, only in draw events. If you need to make sure some stuff is drawn before the draw event, there's also the 'Draw Begin' event for that purpose.
Yes. Thanks for replying. But I fixed that and the actual code are using draw events only, still not creating the effect I'm trying to, though.
 
F

frumple

Guest
You can draw to surfaces in other events and use some draw functions outside Draw. Without surface juggling draw functions will not have visible results. Another issue is the use of globalvar which may be deprecated in Studio. global.light should be used to declare light as global and to access/modify it.

But this is all a bit pointless since this is the old code, and not the actual code that isn't working. Seeing that could help identify issues. Maybe.

http://docs.yoyogames.com/source/da...uage overview/variables/global variables.html
 
S

Sake_v2

Guest
Pretty sure its not a global variable problem, though. The global var light is working just fine, it probably wouldn't even run the game or draw at all if it wasn't. I posted the old code so that maybe someone would be able to see which modifications would have to be made to work on Studio. This is how I'm drawing it now:
obj_light draw event:

Code:
if surface_exists(light)
{
    surface_set_target(light);
    draw_set_color(c_black);
    draw_set_alpha(0.2);
    draw_rectangle(-12,-12,view_wview[0]+12,view_hview[0]+12,false);
    draw_set_blend_mode(bm_subtract);
    with(obj_greenlight1)
     {
          draw_circle_colour(x-view_xview[0]+12,y-view_yview[0]+12,143,c_lime,c_black,false);
      }
    surface_reset_target();
    draw_set_alpha(1);
    draw_surface(light,view_xview[0]-12,view_yview[0]-12);
    draw_set_blend_mode(bm_normal);
}
Tried different variations with draw end events, still can't create the smooth effect :(
 

dphsw

Member
You seem to start drawing your light surface now with the code
Code:
   surface_set_target(light);
   draw_set_color(c_black);
   draw_set_alpha(0.2);
   draw_rectangle(-12,-12,view_wview[0]+12,view_hview[0]+12,false);
where previously you just had:
Code:
surface_set_target(light);
draw_set_color(make_color_rgb(100,100,100));
draw_rectangle(view_xview,view_yview,view_wview,view_hview,false);
Right now it looks like you're not clearing the light target to a constant colour anywhere - and drawing a rectangle that's black with alpha 0.2 each frame will make the surface darker and darker each frame, until it's nearly black (I think once it's RGB(2,2,2), drawing black on it with an alpha of 0.2 will just make it RGB(1.6,1.6,1.6) which will round back up to 2, so it will stop getting any darker.) You should probably clear the surface to something like rgb(100,100,100) as before.
BTW, if you're clearing the whole surface, you don't need to figure out the coordinates for it - there's a draw_clear function for that.

And I'm not sure if it makes a difference (it might get reset automatically when you change surfaces, I'm not certain), but you might want to set the drawing colour back to white after drawing your lights, in case having it set to drawing darkly affects the drawing of other things. (Or have it set to rgb(100,100,100), if that was actually happening before.)
 

Nocturne

Friendly Tyrant
Forum Staff
Admin
Code:
draw_set_color(c_black);
draw_set_alpha(0.2);
draw_rectangle(-12,-12,view_wview[0]+12,view_hview[0]+12,false);
Don't do that. Use simply:
Code:
draw_clear_alpha(c_black, 0.2);
the other way, what you are doing is drawing a transparent black rectangle over the previous contents of the surface, when what you want to do is clear the surface. I would say as well that normally you'd draw all the lights to an opaque black surface and then draw the surface itself at the alpha value you require (and with the blending you require).
 

Joe Ellis

Member
I would use additive rather than subtractive, simply do draw_clear(c_black) on the lighting surface just before you draw the lights, then set blendmode to bm_add, draw the lights(circles with white or light color in the middle which fade out to black)<-- black is important here, it has to be black, as this means NOTHING is getting ADDED, any other color will result in the hard borders your seeing

I would use a shader for this though as its alot more versatile and you can easily invert values and also use multiply blendmode like on photoshop, which is what you want for something like this, and gamemaker doesnt have a built in multiply blendmode, in a shader you simply do -

Pixelcolor = Normalsurfacecolor * (LightsurfaceColor + Ambient)

also aswell, I dont know if you already know any of this but I just thought I'd write abit of help about surfaces

Its fine to draw to a surface in any event, however only the draw events will make the things visible, as this is where the screen buffer is submitted,
So if your drawing the lighting in the step event thats fine, the color data gets written to the lighting surface, but the part where the lighting surface gets mixed with the normal surface needs to be in the draw event, well you could theoretically mix them together in the step event, then draw the combined result as a surface in the draw event, but that would be wasting memory
Another thing to bear in mind is that if your using views, or projections, you need to set the projection to fit on the surface just after its been set as target, cus if you think, say the view/camera is in the middle a 5000x5000 room, and the surface you wanna draw to is only 1600x900, all the draw functions you do on the surface will "miss" the surface as theyre getting offset by the view/camera position, so to fix this you need to temporarily set the camera projection to over the surface while your drawing to it, I usually use d3d_set_projection_ortho, x&y at 0,0, as this is where the surface begins, and the width/height to the width/height of the surface, so anything you draw then will be drawn on the surface, providing the coordinates you specify are in the surface dimensions, this could cause a problem if lights are in a room bigger than the surface but its easy to fix by calculating offset values eg. lightXonsurface= LightX-CameraX
lightYonsurface= LightY-CameraY
 
Last edited:
S

Sake_v2

Guest
You seem to start drawing your light surface now with the code
Code:
   surface_set_target(light);
   draw_set_color(c_black);
   draw_set_alpha(0.2);
   draw_rectangle(-12,-12,view_wview[0]+12,view_hview[0]+12,false);
where previously you just had:
Code:
surface_set_target(light);
draw_set_color(make_color_rgb(100,100,100));
draw_rectangle(view_xview,view_yview,view_wview,view_hview,false);
Right now it looks like you're not clearing the light target to a constant colour anywhere - and drawing a rectangle that's black with alpha 0.2 each frame will make the surface darker and darker each frame, until it's nearly black (I think once it's RGB(2,2,2), drawing black on it with an alpha of 0.2 will just make it RGB(1.6,1.6,1.6) which will round back up to 2, so it will stop getting any darker.) You should probably clear the surface to something like rgb(100,100,100) as before.
BTW, if you're clearing the whole surface, you don't need to figure out the coordinates for it - there's a draw_clear function for that.

And I'm not sure if it makes a difference (it might get reset automatically when you change surfaces, I'm not certain), but you might want to set the drawing colour back to white after drawing your lights, in case having it set to drawing darkly affects the drawing of other things. (Or have it set to rgb(100,100,100), if that was actually happening before.)
I changed the make_color_rgb code because it stopped working on Studio. I learned back on the day from a tutorial, and that was the way instead of c_black to determine how dark the light was. On studio, its completely dark, that's why I changed it.

Don't do that. Use simply:
Code:
draw_clear_alpha(c_black, 0.2);
the other way, what you are doing is drawing a transparent black rectangle over the previous contents of the surface, when what you want to do is clear the surface. I would say as well that normally you'd draw all the lights to an opaque black surface and then draw the surface itself at the alpha value you require (and with the blending you require).
I used that, still no change or am I doing something wrong?

Swap out lime for white and see how that looks.
No change.

I would use additive rather than subtractive, simply do draw_clear(c_black) on the lighting surface just before you draw the lights, then set blendmode to bm_add, draw the lights(circles with white or light color in the middle which fade out to black)<-- black is important here, it has to be black, as this means NOTHING is getting ADDED, any other color will result in the hard borders your seeing

I would use a shader for this though as its alot more versatile and you can easily invert values and also use multiply blendmode like on photoshop, which is what you want for something like this, and gamemaker doesnt have a built in multiply blendmode, in a shader you simply do -

Pixelcolor = Normalsurfacecolor * (LightsurfaceColor + Ambient)

also aswell, I dont know if you already know any of this but I just thought I'd write abit of help about surfaces

Its fine to draw to a surface in any event, however only the draw events will make the things visible, as this is where the screen buffer is submitted,
So if your drawing the lighting in the step event thats fine, the color data gets written to the lighting surface, but the part where the lighting surface gets mixed with the normal surface needs to be in the draw event, well you could theoretically mix them together in the step event, then draw the combined result as a surface in the draw event, but that would be wasting memory
Another thing to bear in mind is that if your using views, or projections, you need to set the projection to fit on the surface just after its been set as target, cus if you think, say the view/camera is in the middle a 5000x5000 room, and the surface you wanna draw to is only 1600x900, all the draw functions you do on the surface will "miss" the surface as theyre getting offset by the view/camera position, so to fix this you need to temporarily set the camera projection to over the surface while your drawing to it, I usually use d3d_set_projection_ortho, x&y at 0,0, as this is where the surface begins, and the width/height to the width/height of the surface, so anything you draw then will be drawn on the surface, providing the coordinates you specify are in the surface dimensions, this could cause a problem if lights are in a room bigger than the surface but its easy to fix by calculating offset values eg. lightXonsurface= LightX-CameraX
lightYonsurface= LightY-CameraY
Tried the same code but with bm_add. With bm_add, the green color works fine, and the smooth effect kinda works too, but the dark color with draw_clear(c_black) does not.


What I tried to do now was this:

Code:
if surface_exists(light)
{
    surface_set_target(light);
    draw_clear_alpha(c_black,0.5);
    draw_set_blend_mode(bm_subtract);
    with(obj_lamp)
    {
        draw_circle_colour(x-view_xview[0]+12, y-view_yview[0]+12,30,c_green,c_black,false);
    }
    surface_reset_target();
    draw_surface(light,view_xview[0]-12,view_yview[0]-12);
    draw_set_blend_mode(bm_normal);
}
Now not only it isn't smooth, it also isn't green.

With bm_add, it creates this.

Green, kind of smooth, but no dark color around.

This is too painful. Is there a tutorial for this on Studio somewhere (with working colored lightning)? I can't find it. Thanks for the replies though.
 
Last edited by a moderator:

Joe Ellis

Member
the thing is if your using bm_add still when you overlay the lighting onto the normal layer, it cant darken it, only lighten it, so I would learn how shaders work and then you can easily multiply the 2 surfaces together, eg. the blackness will be (0.0,0.0,0.0), so multiply any rgb value by 0 and it becomes 0 and the darkening will work, if its half darkness(half way through the light patch) it will multiply the level's colors by 0.5, the only problem with this is that multiplying can only darken stuff if its values are always between 0 and 1, so thats why I suggested adding an ambient shade to it, this will make the lighter patches actually light up the stuff its over and the darkness will not be 100% black either, alternatively you can multiply the lighting layer by a certain amount thats above 1 to increase the ceiling/max brightness, and the blackness will remain black, but you will lose a tiny bit of color depth, most of the time its not noticable tho
The shader code would look like this:
Code:
varying vec2 v_vTexcoord;

uniform float Brightness;

uniform sampler2D Lighting;

void main()
{
    gl_FragColor = texture2D( gm_BaseTexture, v_vTexcoord ) * (texture2D( Lighting, v_vTexcoord ) * Brightness);
}
I can send you an example if your interested
 
S

Sake_v2

Guest
the thing is if your using bm_add still when you overlay the lighting onto the normal layer, it cant darken it, only lighten it, so I would learn how shaders work and then you can easily multiply the 2 surfaces together, eg. the blackness will be (0.0,0.0,0.0), so multiply any rgb value by 0 and it becomes 0 and the darkening will work, if its half darkness(half way through the light patch) it will multiply the level's colors by 0.5, the only problem with this is that multiplying can only darken stuff if its values are always between 0 and 1, so thats why I suggested adding an ambient shade to it, this will make the lighter patches actually light up the stuff its over and the darkness will not be 100% black either, alternatively you can multiply the lighting layer by a certain amount thats above 1 to increase the ceiling/max brightness, and the blackness will remain black, but you will lose a tiny bit of color depth, most of the time its not noticable tho
The shader code would look like this:
Code:
varying vec2 v_vTexcoord;

uniform float Brightness;

uniform sampler2D Lighting;

void main()
{
    gl_FragColor = texture2D( gm_BaseTexture, v_vTexcoord ) * (texture2D( Lighting, v_vTexcoord ) * Brightness);
}
I can send you an example if your interested
The reason why I prefer surfaces over shaders is simply because I have pratically ZERO knowledge with shaders. And if you analyze it, the code I'm using is quite short and simple, I'm sure just a couple of lines would fix the problem. I just don't know what it is, so still needing help. But an example would be nice (for someone who doesn't know shaders).
 
S

Sake_v2

Guest
Ok I'll try and do one for you, and if you send me what you've done so far, I can try and fix the surface code, send it to my email if you want
[email protected]
Thank you for the help. Not emailing because I don't use email that often and the lightning object is quite simple, really.
Current code.

obj_light

Create event
Code:
globalvar light;
light = surface_create(view_wview[0]+24,view_hview[0]+24);
Step event
Code:
if !surface_exists(light)
{
    light = surface_create(view_wview[0]+24,view_hview[0]+24);
}
Room end event

Code:
if (surface_exists(light))
{
    surface_free(light);
}
Draw event
Code:
if surface_exists(light)
{
    surface_set_target(light);
    draw_clear_alpha(c_black,0.5);
    draw_set_blend_mode(bm_subtract);
    with(obj_lamp)
    {
        draw_circle_colour(x-view_xview[0]+12, y-view_yview[0]+12,30,c_green,c_black,false);
    }
    surface_reset_target();
    draw_surface(light,view_xview[0]-12,view_yview[0]-12);
    draw_set_blend_mode(bm_normal);
}
Above code not working because its creating the effect I posted on the other post.
The reason why I used those view_wview[0]+24 "offsets" is simply because I use smooth camera movement. If the surface is exactly the size of the view, sometimes the surface wouldn't cover one line of pixels.
And the obj_lamp code doesn't have any code.
 

Joe Ellis

Member
To be honest it doesnt seem like its possible to do it using subtract in that way,
first you need to draw_clear(c_white) not black, if your using subtract, black is white & white is black, as your taking whatever color away from whats already there, same with the green light, it will be removing the green thats already there so everything will end up magenta, if you want the light green you need to set it as magenta, but this is not only annoying but also quite inadequate, as it can only darken stuff and take magenta out of stuff causing it to be left with green which will look like your in a green version of a darkroom, you need to set every different color to its inverse in order to get the correct color,
you could add an extra step, after the light surface has been drawn, invert it, but again thats more complicated than it needs to be

I just made the example of how to do it with shaders, it does everything you want and it only took me about an hour,

https://www.dropbox.com/s/knj9zcl5mymf9gf/Lighting Surfaces.gmx.zip?dl=0

Even though you'll have to learn how to use shaders, this is probably the easiest & most professional way to do an effect like this

Do you want me to make a commented version of it?
 
Last edited:
S

Sake_v2

Guest
To be honest it doesnt seem like its possible to do it using subtract in that way,
first you need to draw_clear(c_white) not black, if your using subtract, black is white & white is black, as your taking whatever color away from whats already there, same with the green light, it will be removing the green thats already there so everything will end up magenta, if you want the light green you need to set it as magenta, but this is not only annoying but also quite inadequate, as it can only darken stuff and take magenta out of stuff causing it to be left with green which will look like your in a green version of a darkroom, you need to set every different color to its inverse in order to get the correct color,
you could add an extra step, after the light surface has been drawn, invert it, but again thats more complicated than it needs to be

I just made the example of how to do it with shaders, it does everything you want and it only took me about an hour,

https://www.dropbox.com/s/knj9zcl5mymf9gf/Lighting Surfaces.gmx.zip?dl=0

Even though you'll have to learn how to use shaders, this is probably the easiest & most professional way to do an effect like this

Do you want me to make a commented version of it?
Hello 321looloo, thank you for taking your time to make an example like this. No need for a commented version, I plan on learning shaders eventually, and your example for sure is gonna help. For now, I found out what the problem was. It wasn't with the code and it wasn't because of the Studio version modifications or anything like that. The problem was with the method I use for setting the game resolution/view_size by drawing the application surface manually. I drew the application surface on a Post Draw event to make sure everything was done drawing, and the lights were also being drawn on a End Step event. Now that I figured out the problem, I can find a way to fix it. I'm going to use this surface method until I learn more about shaders. Problem solved, thanks guys.
 
Top