GMS 2 Deferred Normal Map Lighting w/ Spine

Discussion in 'Marketplace' started by Strawbry_jam, Jan 4, 2018.

  1. Strawbry_jam

    Strawbry_jam Member

    Joined:
    Jun 20, 2016
    Posts:
    217

    Description

    This is a normal map lighting engine using deferred rendering, made and designed for GameMaker Studio 2.

    Features:
    • Easy to drop into any project and implement lighting
    • Highly optimized
    • Supports sprites and tilemap layers
    • Supports Spine Animations with a tool to streamline the process! (download)
    • No set limit on number of lights (and performance doesn't take much of a hit for more)
    • Includes functionality for Specular Highlights and Ambient Occlusion
    What does this do for you?

    Normal map lighting makes your flat 2d world pop out and look 3d. Specular highlights give sprites shininess like metallic armor or glass. Ambient occlusion gives sprites realistic shadows that disappear in light.

    How do you use it?

    Manual covering all the functions in detail.
    Code:
    \\\create event lighting controller object
    if(view_enabled){
        lighting_init(camera_get_view_width(view_camera[0]),camera_get_view_width(view_camera[0]),true,true);
    } else {
        lighting_init(room_width,room_height,true,true);
    }
    
    \\\draw end event lighting controller object
    lighting_draw_all();
    
    \\\create event player
    lighting_instance_create();
    lighting_instance_normals(sprPlayerNormals);
    lighting_instance_specular(sprPlayerSpecular); //optional
    lighting_instance_ao(sprPlayerOcclusion); //optional
    
    \\\draw event player
    draw_self();
    lighting_instance_add();
    
    \\\create event light
    lighting_light_create(true);
    lighting_light_radius(64);
    
    \\\draw event light
    lighting_light_add();
    It's as easy as that!

    For spine support, I created a tool (download) to generate the required code and materials. Takes the spine atlas image, atlas file, and json file and exports the creation code for the object and a map. Spine lighting has never been easier to add!
    Functions:
    Code:
    lighting_init(view_width,view_height,specular,ao)
    lighting_draw_all()
    lighting_set_ambience(color)
    
    lighting_instance_create()
    lighting_instance_add()
    lighting_instance_normals(sprite/layerName)
    lighting_instance_specular(sprite/layerName)
    lighting_instance_ao(sprite/layer name)
    lighting_instance_tilemap()
    lighting_instance_spine()
    lighting_instance_bonemap(sprite)
    
    lighting_light_create(pointLight)
    lighting_light_add()
    lighting_light_color(color,alpha)
    lighting_light_radius(radius)
    lighting_light_direction(x,y,z)
    
    lighting_spine_get_bone_states()
    lighting_spine_draw(instance_id,function)
    
     
    Last edited: Jan 21, 2018
    Nux, Lonewolff, rIKmAN and 1 other person like this.
  2. Strawbry_jam

    Strawbry_jam Member

    Joined:
    Jun 20, 2016
    Posts:
    217
    Updated version. Manual created. Changelog added. Link to demo source code included with asset.
     
  3. jf_knight

    jf_knight Member

    Joined:
    Dec 23, 2016
    Posts:
    86
    Is the example project source included with the asset?
     
  4. Strawbry_jam

    Strawbry_jam Member

    Joined:
    Jun 20, 2016
    Posts:
    217
    If you're asking if the asset itself includes the example objects then no. If you're asking if there is an example project source available with the asset then yes. It's just a download link to the project and spine files outside game maker so you can see everything. Although I can upload the example objects and room into the asset if that is more convenient.
     
  5. Zerb Games

    Zerb Games Member

    Joined:
    Jun 27, 2016
    Posts:
    259
    Haha wow, I was actually looking for your old example to download it :). I recall you doing this before. Oof I really need to buy this, but idk if it's exactly what I want. I'll see! Looks great tho.

    Do you think you could include an example file somewhere? I really want to test it before using it. For performance reasons. I need to support a few hundred lights of all different colors.
     
  6. Strawbry_jam

    Strawbry_jam Member

    Joined:
    Jun 20, 2016
    Posts:
    217
    Working on shadows and depth. Right now, the lighting calculations treat the depth as 0. Here is a new demo of my progress. :) (LINK)

    Controls:
    • Numbers 1-4 Enable/Disable options (Specular,AO,Depth,Shadows)
    • C toggles cel shading
    • F toggles full screen
    • Left mouse button adds a light at the mouse with the mouse light z value
    • Right mouse button adds a spine instance at the mouse
    • Mouse wheel moves the mouse light z value up or down
    • Arrow keys move the view
    • ESC ends the demo
    This demo should give a good idea on performance with each of the different options.
     
    rIKmAN likes this.
  7. Strawbry_jam

    Strawbry_jam Member

    Joined:
    Jun 20, 2016
    Posts:
    217
    Almost done!
    [​IMG]
    [​IMG]
     
  8. Luke Peña

    Luke Peña Member

    Joined:
    Jul 6, 2016
    Posts:
    104
    It's possible to run this without the drop-shadows on the background, right? I know the answer is probably yes, but I just want to make sure.
     
  9. Strawbry_jam

    Strawbry_jam Member

    Joined:
    Jun 20, 2016
    Posts:
    217
    Yep. Everything is optional but the normal map.
     
  10. Alsekond

    Alsekond Member

    Joined:
    Oct 21, 2016
    Posts:
    36
    Hi! Could you please add simple demo code to asset?
     
    Last edited: Mar 6, 2018
  11. AndrewN

    AndrewN Member

    Joined:
    Aug 17, 2016
    Posts:
    11
    Hello,

    Quick question. Does this also work with tile and asset layers?

    Thanks,


    Andrew
     
  12. Strawbry_jam

    Strawbry_jam Member

    Joined:
    Jun 20, 2016
    Posts:
    217
    It works well with the tile layers. Asset layer should work too (just treat it the same a tile layer). Asset layer might be harder to work with though. For tiles, you create a duplicate layer and swap the tileset for the one with normals. For sprites, that won't be as easy, but it's still possible.
     
  13. FYVE

    FYVE Member

    Joined:
    Sep 23, 2017
    Posts:
    27
    Was wondering if I could get some help importing this into my project? I'm getting errors following the instructions...
     
  14. joakimFF

    joakimFF Member

    Joined:
    Jun 20, 2016
    Posts:
    119
    Does anyone know how to apply normal mapping to a tile layer as a whole, when I assign a tile layer the shader treats each tile separately creating a "bump seam" around every tile.
     
  15. Strawbry_jam

    Strawbry_jam Member

    Joined:
    Jun 20, 2016
    Posts:
    217
    The shader treats the all the normal maps as one image, not per tile. All the normal maps are drawn to a surface. Then that normal map surface is used by the shader.

    If you have a bump seam, you should check the normal maps to make sure they are tilable. A lot of programs that build normal maps for you are unable to deal with individual tiles because they use the image shape along with differences in luminosity. The shape part creates edges.

    If they are tilable, check if you have edge color bleed turned on (especially if the view is scaled or your view is not exactly snapped to the pixels).
    https://www.yoyogames.com/blog/3/seamless-tile-scaling-in-gamemaker
     
  16. joakimFF

    joakimFF Member

    Joined:
    Jun 20, 2016
    Posts:
    119

    Thank you! it was indeed a scaling issue, everything works like a charm now. :)
     
  17. joakimFF

    joakimFF Member

    Joined:
    Jun 20, 2016
    Posts:
    119
    Hi maybe someone here can spot were I am going wrong, I am trying to add and populate Diffuse / Normal and normal map control layer trough GML instead of using the IDE. But the lights just turns out as black splotches...
    I have confirmed the layers are created / populated and at the right depth from each other.
    It works fine if I manually add the tiles / layers in the IDE.

    Code:
     lidd = layer_get_id(layerName);
            
                    if lidd == -1 {
                       
                      lay_idd = layer_create(layerDepth,layerName);
                       map_idd = layer_tilemap_create(lay_idd,0,0,tDungeonTiles,32,32)
                    
                       nrm_lay_id = layer_create(layerDepth+100,layerName+string("_NRM"));
                       nrm_map_idd = layer_tilemap_create(nrm_lay_id,0,0,tDungeonTilesNRM,32,32);
                      
                       nrm_lay_ctrl_id = layer_create(layerDepth+200,layerName+string("_NRM_CTRL"));
                      
                       tile_nrm_controler = instance_create_layer(0,0,nrm_lay_ctrl_id,obj_normalmap_tile_controller);
                         with tile_nrm_controler {
                            lighting_instance_create();
                            lighting_instance_tilemap(other.lay_idd);
                            lighting_instance_normals(layer_get_name(other.nrm_lay_id));
                            
                        }
                      
                     }
                  
                       if layer_get_name(lay_idd) == layerName {
                          if layer_tilemap_exists(lay_idd,map_idd) {
                              t =  tilemap_set(map_idd, ind, mx, my);
                              n =  tilemap_set(nrm_map_idd, ind, mx, my);
                              
                           }
                       }
    
    [​IMG]
     
  18. BigGrimm

    BigGrimm Member

    Joined:
    Dec 26, 2016
    Posts:
    1
    It's a very good asset.
    I have two questions.

    1. Can I use natural colors instead of black in the shadows?

    2. Normal map works well for instance. Can I apply a normal map to draw_sprite?
     
  19. Strawbry_jam

    Strawbry_jam Member

    Joined:
    Jun 20, 2016
    Posts:
    217
    Sorry for the delay. I didn't see this post for some reason. I'm not sure if you figured it out yet. I'm not too familiar with generating layers during runtime. I know the IDE flips the draw order on instances. I'll run some tests myself and see what I can find out.

    The shadows are naturally created in that they use depth to block light from pixels from each light source. If you have 1 light, the shadow color will end up being whatever color you set as the ambient lighting. In the video I show, I chose no ambient lighting so it was black. I've played around with a dark blue/purple as well but it can really be any color.

    draw_sprite should work. You may need to add some code to the scripts but I believe there is a custom draw option you can use.
     
  20. Luk

    Luk Member

    Joined:
    Sep 9, 2018
    Posts:
    2
    I have a question
    What did you use for the shadows?
    Primitives?
     
  21. Strawbry_jam

    Strawbry_jam Member

    Joined:
    Jun 20, 2016
    Posts:
    217
    It's built into the lighting shader using ray tracing. Shadows are the result of the pixel being occluded by another pixel closer to the light source and therefore add no light at that point.
     
  22. Luk

    Luk Member

    Joined:
    Sep 9, 2018
    Posts:
    2
    Thanks for answering.
    And it works with curved surfaces through normal mapping?
    I mean, are the shadows deformed?
    Can they climb the walls?
     
  23. Strawbry_jam

    Strawbry_jam Member

    Joined:
    Jun 20, 2016
    Posts:
    217
    Yeah but there is a lot of limitations since there is only the depth map to compare to. The shadows work much like the 2d shadow engines out there like this. But I use the depth map and the light source height to determine if the light is occluded. Also light doesn't pass behind an object even if the object isn't connected to the ground. The depth map only gives the height at a pixel so it treats all pixels as connecting to the ground.

    Aside from the limitations though, the resulting effect looks good.
     
  24. Molestium

    Molestium Member

    Joined:
    Jul 5, 2018
    Posts:
    2
    Hello. I try your asset, this works well. I want divide by two shadows height for fake 3d effect. I tried to use Depth map, but it looks bad.
    [​IMG]
    How I can do that?
    Thanks.
     
  25. Strawbry_jam

    Strawbry_jam Member

    Joined:
    Jun 20, 2016
    Posts:
    217
    To divide the height by 2, you can either change the angle of the light to more straight down or change the depth maps by making them 50% darker. There are 2 functions to deal with instances:
    Code:
    lighting_instance_depthScale(depthScale) //scales depth. for example 0.5 will make the depth divide by 2.
    lighting_instance_depthVertex(z1,z2,z3,z4) //adds to depth z value per vertex in pixels. 255 is the max by default. this is useful for instances like the character in the picture to have more height at his head. 
    
    Until Yoyo adds color options to tiles, you will have to manually add depth tiles for the correct depths if you have multiple different depth options per tile. So to create the effect, in an image editor, you would first lock the alpha channel of the depth map, then add a layer with transparency, make it black and set the alpha to the scale you want (0.5 for half height), merge the layers, add a layer, using colors between black and white with gradients or not, merge this layer with additive blending to add to the z value.
     
  26. RagTagRadical

    RagTagRadical Member

    Joined:
    Feb 8, 2017
    Posts:
    9
    Hey Strawbry_jam, i love the effect of your lighting system, and I'm hoping to use it in my next project, but I'm running into an issue where the Depth map and its shadow does not seem to be drawing from the origin of the sprite, but always from the top left.

    I've just been doing small tests with it to learn the system and see if it will work for how I intend.
    I changed the origin of all of the sprPlayer sprites to the center, and the shadows still seem to be cast as though it's from the top left.
    You can also see that with the paw print test image I was playing with.

    The other maps seem to be drawing from their origin, even when rotating the sprite.
    Screenshot 2018-11-21 20.28.35.jpg
     
  27. Strawbry_jam

    Strawbry_jam Member

    Joined:
    Jun 20, 2016
    Posts:
    217
    Sorry for the late reply. I don't get any sleep from work during this time of the year.

    :) Good catch. I didn't notice since all the sprites had origins at 0,0 for all of my tests. I'll upload a fix. If you want to patch your version now though, you just need to change 1 line of code.

    In lighting_draw_all() change the following line:
    Code:
    draw_sprite_general(depthSprite,image_index,0,0,sprite_width/image_xscale,sprite_height/image_yscale,x-cam_x,y-cam_y,image_xscale,image_yscale,image_angle,z1col,z2col,z3col,z4col,1);
    to
    Code:
    var off_len = point_distance(sprite_xoffset,sprite_yoffset,0,0);
    var off_ang = point_direction(sprite_xoffset,sprite_yoffset,0,0);
    var x_off = lengthdir_x(off_len,image_angle+off_ang);
    var y_off = lengthdir_y(off_len,image_angle+off_ang);
    draw_sprite_general(depthSprite,image_index,0,0,sprite_width/image_xscale,sprite_height/image_yscale,x-cam_x+x_off,y-cam_y+y_off,image_xscale,image_yscale,image_angle,z1col,z2col,z3col,z4col,1);
    It's in the section
    //////setup depth map
     
  28. RagTagRadical

    RagTagRadical Member

    Joined:
    Feb 8, 2017
    Posts:
    9
    That did it! Thanks so much. Get some sleep! I'll post my results here when I get a bit further along on the project visuals
     

Share This Page

  1. This site uses cookies to help personalise content, tailor your experience and to keep you logged in if you register.
    By continuing to use this site, you are consenting to our use of cookies.
    Dismiss Notice