Is possible to create ambient light in SDL2?

This is something I’ve been trying to do for really long long time, this last few days I managed to get to what I want (see screenshot below).

My next question is how do you blend or tint the mask, say I want the black portion to be blend as a blue-purple-ish to give more of a night ambient rather than just being super dark with a candlelight. The screenshot in the bottom was done by changing the render draw color to something else make it it look transparent and blend into it, but it never goes to tint the actual color selected.

Here is an example in pokemon second generation, it just a purplish tint that blends with actual color instead of just an overlay color on top. I have tried putting an purple texture on top with blend mode, it’s too far from this, and I’m not expecting the same.

pokemon

Using a white painted brush on a black background I was able to create that effect, and it looks “tinted” when using different colors instead of white.

Here are the light spots I used

light-white light-yellow light-yellow-small

Changing the render draw color looks like this:
SDL_SetRenderDrawColor(renderer, 0x36, 0x45, 0x9b, 0xff);


At the end of the day I want to be able to use different color to simulate different time of the day, not like this, but you get an idea in this link: https://www.gamasutra.com/blogs/SvyatoslavCherkasov/20181023/329151/Graveyard_Keeper_How_the_graphics_effects_are_made.php

In summary:

  • How do I change the black color to be transparent while having the “bright” spot?

  • How I blend another color or tint a texture to look like the screenshot of pokemon or the last game linked to gamasutra?

  • Or How do I create a tint in the light black color that blends to the game background to look like tint?

  • Is possible to do all this to create ambient light?

    #include <SDL2/SDL.h>
    #include <SDL2/SDL_image.h>
      
    int main(int argc, char ** argv)
    {
      SDL_Event event;
      int quit = 0;
    
      SDL_Init(SDL_INIT_VIDEO);
    
      SDL_Window * window = SDL_CreateWindow(
              "2D Light test",
              SDL_WINDOWPOS_UNDEFINED,
              SDL_WINDOWPOS_UNDEFINED,
              640,
              480,
              0
      );
      SDL_Renderer * renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_PRESENTVSYNC);
      SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_BLEND);
    
      // layers
      SDL_Texture * backgroundLayer = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, 640, 480);
      SDL_Texture * lightLayer = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, 640, 480);
      SDL_Texture * resultLayer = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, 640, 480);
    
      // "game" graphics rendered
      SDL_Surface * bgSurface = IMG_Load("game.jpg");
      SDL_Texture * bgTexture = SDL_CreateTextureFromSurface(renderer, bgSurface);
      SDL_FreeSurface(bgSurface);
    
      // light spot
      SDL_Surface * lightSurface = IMG_Load("spot.jpg");
      SDL_Texture * lightTexture = SDL_CreateTextureFromSurface(renderer, lightSurface);
      SDL_FreeSurface(lightSurface);
    
      while (quit != 1)
      {
          while( SDL_PollEvent(&event) != 0 )
          {
              switch (event.type)
              {
                  case SDL_QUIT:
                      quit = 1;
                      break;
              }
          }
    
          SDL_SetRenderDrawColor( renderer, 0x00, 0x00, 0x00, 0x00 );
          SDL_RenderClear( renderer );
    
    
          // fake game tiles, objects, players rendering
          SDL_SetRenderTarget( renderer, backgroundLayer );
          SDL_SetRenderDrawColor( renderer, 0x00, 0x00, 0x00, 0x00 );
          SDL_RenderClear( renderer );
    
          SDL_Rect gameRect = {0, 0, 640, 480};
          SDL_RenderCopy(renderer, bgTexture, NULL, &gameRect);
    
          SDL_SetRenderTarget(renderer, NULL);
    
    
          // draw light points
          SDL_SetRenderTarget(renderer, lightLayer);
          SDL_SetTextureBlendMode(lightLayer, SDL_BLENDMODE_MOD);
          SDL_SetRenderDrawColor(renderer, 0x00, 0x00, 0x00, 0x00);
          // change the black color to a more transparent one
          // SDL_SetRenderDrawColor(renderer, 0x36, 0x45, 0x9b, 0xff);
          // ----
          SDL_RenderClear(renderer);
    
          SDL_Rect spot1 = {10, 10, 200, 200};
          SDL_Rect spot2 = {240, 240, 200, 200};
    
          SDL_RenderCopy(renderer, lightTexture, NULL, &spot1);
          SDL_RenderCopy(renderer, lightTexture, NULL, &spot2);
    
          SDL_SetRenderTarget(renderer, NULL);
    
          // merge all layers
          SDL_SetRenderTarget(renderer, resultLayer);
    
          SDL_SetRenderDrawColor(renderer, 0x00, 0x00, 0x00, 0x00);
          SDL_RenderClear(renderer);
    
          SDL_SetTextureBlendMode(resultLayer, SDL_BLENDMODE_BLEND);
          SDL_RenderCopy(renderer, backgroundLayer, NULL, &gameRect);
    
          SDL_RenderCopy(renderer, lightLayer, NULL, &gameRect);
    
          SDL_SetRenderTarget(renderer, NULL);
          SDL_RenderCopy(renderer, resultLayer, NULL, &gameRect);
    
          SDL_RenderPresent(renderer);
      }
    
      SDL_DestroyTexture(bgTexture);
      SDL_DestroyTexture(lightTexture);
      SDL_DestroyRenderer(renderer);
      SDL_DestroyWindow(window);
      SDL_Quit();
    
      return 0;
     }
    

I have played with different options of blending and creating custom blendmode with all the possible combinations I could come up.

what about rendering your lights as SDL_BLENDMODE_ADD to a texture with a blend mode set to SDL_BLENDMODE_MOD then render that on top of the scene?

SDL_Texture lights; /* blend mode as SDL_BLENDMODE_ADD, imported from a file or something*/

SDL_Texture shadow; /* blend mode as SDL_BLENDMODE_MOD, should be created with SDL_CreateTexture with SDL_TEXTUREACCESS_TARGET */

/*rendering the shadow*/ 

SDL_SetRenderTarget(shadow);

SDL_SetRenderColor(0, 0, 0, 255);  /* put your desired tint , (the alpha value has no effect here) */

SDL_RenderFillRect(nullptr); /* draw the color to the entire shadow*/

SDL_RenderCopy(lights); /* your spotlights */

SDL_SetRenderTarget(0) /* set the render back to your scene*/;

SDL_RenderCopy(shadow); /* shadow */

SDL_RenderPresent(); /* show everything */

example photos
tint color (60, 0, 100, 255) with lights

tint color (0, 0, 0, 255) with lights

normal scene

tint color (60, 0, 100, 255) no lights

is this what you wanted?

1 Like

Thank you very much @obeah, this is exactly what I wanted, after I read your example I had some issues that the color wasn’t the color of the tint, but then I realized your image is black and white, and that act nicely tinting the color.

Using tint color (60, 0, 100, 255)

Using tint color (0, 0, 255, 255)

Looking only good when the color blending match with the colorful background otherwise it doesn’t paint the expected color.

Using the same image black and white the result looks way better

Using tint color (60, 0, 100, 255)

Using tint color (0, 0, 255, 255)

The only drawback now, it’s that the light spots are black and white, I tried using a yellow spot instead of a white, but still not as expected with the original colors. I also tried to blend the original colored picture on top (MUL, ADD, MOD, BLEND), but the result was the same as without using the black and white image.

This is an extra question, do you know if there’s any techniques for this.

Again thank you very much this is going to the direction I wanted.

2 Likes

I am not entirely sure how it works, but from the docs, you can write your own blend functions.

I tried blend mode as SDL_ BLENDMODE_MUL, it seems to work. Can you show me the full source code in your solution?

Did anyone managed to do this?
I tried this idea, and it works, but I can’t adjust the dark area alpha, so it is more dark than I really want, and is hard to see anything there. It amuzes me that, after all this time, I could not find a light source engine for SDL2. So, if I’m able to do this effect, I’ll publish one in github.
BTW I was able to make it work by creating a white texture, screensize, with an alkpha, then I ADD this one before I ass the circles for light sources. That works, but I imagina that would kill performance a bit.

What I do is I create two textures/surfaces: one for just the lights and one for the game’s content. In the game loop I fill the light texture/surface with the ambient color (black would be fully dark and white fully bright). Then, I blit the lights to this texture/surface using the ADD blend mode. After blitting/drawing all the game’s visuals to the corresponding texture/surface, blit the light texture onto the game’s visuals using the MULT blend mode.
This should produce the desired effect.

Oh, I totally forgot to update this thread, sorry.
Yes, I did manage to get it working as I wanted after a while, with transparent darkness including blue hue, and lightsource for the player and others that blend with it.

It’s been a while, so I do not recall 100% what I did, but the code, not all direct SDL calls and including some animation in the light sources circle size, so it trembles a bit, but fairly easy to understand, is as follows:

    st_imageData alpha_mask_dark_effect;
    ImageView::get_instance()->init_target_image(alpha_mask_dark_effect, RES_W, AREA_H);
    ImageView::get_instance()->clear_texture_area(0, 0, RES_W, RES_H, 255, 255, 255, 100, alpha_mask_dark_effect);


    SDL_SetTextureBlendMode(dark_effect_surface.texture, SDL_BLENDMODE_BLEND);
    ImageView::get_instance()->clear_texture_area(0, 0, RES_W, RES_H, 0, 0, 80, 255, dark_effect_surface);
    SDL_SetTextureBlendMode(dark_effect_surface.texture, SDL_BLENDMODE_MOD);

    st_position player_center_pos = GameManager::get_instance()->get_player_relative_center_position();

    ImageView::get_instance()->blend_images(alpha_mask_dark_effect, dark_effect_surface, 0, 0);
    ImageView::get_instance()->blend_images(dark_effect_light_source_mask, dark_effect_surface, player_center_pos.x-512, player_center_pos.y-512);
    ImageView::get_instance()->blend_images(dark_effect_light_source_mask, dark_effect_surface, player_center_pos.x-dark_effect_light_source_mask.surface->w/2, player_center_pos.y-dark_effect_light_source_mask.surface->h/2);

    int limit_flame_effect = 20;
    if (flame_light_timer < TimerView::get_instance()->getTimer()) {
        if (flame_light_expanding) {
            flame_light_state += 2;
            if (flame_light_state > limit_flame_effect) {
                flame_light_state = limit_flame_effect;
                flame_light_expanding = !flame_light_expanding;
            }
        } else {
            flame_light_state -= 2;
            if (flame_light_state < 0) {
                flame_light_state = 0;
                flame_light_expanding = !flame_light_expanding;
            }
        }
        flame_light_timer = TimerView::get_instance()->getTimer() + 10;
    }

    ImageView::get_instance()->blend_images(yellow_light_mask, dark_effect_surface, -100+flame_light_state/2, -100+flame_light_state/2, yellow_light_mask.surface->w-flame_light_state, yellow_light_mask.surface->h-flame_light_state);

void ImageView::blend_images(st_imageData &source, st_imageData &dest, int x, int y)
{
    blend_images(source, dest, x, y, source.surface->w, source.surface->h);
}

void ImageView::blend_images(st_imageData &source, st_imageData &dest, int x, int y, int w, int h)
{

    SDL_SetRenderTarget(gRenderer, dest.texture);

    SDL_SetTextureBlendMode(source.texture, SDL_BLENDMODE_ADD);
    SDL_Rect origin = {0, 0, source.surface->w, source.surface->h};
    SDL_Rect destiny  = {x, y, w, h};
    SDL_RenderCopy(gRenderer, source.texture, &origin, &destiny);

    restore_render_target();
}

OK, That upped the look of things a lot. Very nice effect.
The character might have to float when she does major magic though. (Looking at the window title, that’s probably in the works already). :slight_smile:

1 Like

@obeah: I am very interested in your answer. I am trying to understand how you created a circle using the texture with SDL_CreateTexture( ). Can you post a complete code of what you did (see figures with different background color) in the figures you posted ?
My email: giorgio_guglielmone@yahoo.it