Solarus Forum

Solarus => Bugs & Feature requests => Topic started by: Satoh on July 06, 2016, 08:49:04 pm

Title: Feature Request: Change a surface's blend mode
Post by: Satoh on July 06, 2016, 08:49:04 pm
SDL has four blend modes, SDL_BLENDMODE_NONE, SDL_BLENDMODE_BLEND, SDL_BLENDMODE_ADD, and SDL_BLENDMODE_MOD.
SDL_BLENDMODE_NONE isn't terribly useful for Solarus so we can ignore it.

Solarus source code appears to always use BLEND already, which makes sense.
My desire is to be able to have some surfaces specify the ADD and MOD modes.
ADD is useful for creating brightly shining effects which lighten and color things under them, without washing them out or making them look hazy. Using a semitransparent sprite or surface by itself would cause the sprite underneath to lose some of its clarity.
If you wanted a particularly bright special effect, it would destroy the clarity of the scene beneath it, whereas an ADD blended surface would allow more of the detail to show through.
It would look better particularly when a dark and light colored object are beneath it, as the dark object would would be lit proportional to its original color.
A simple BLEND effect would cause the darker object to seem to be lit more, due to the BLEND surface partly replacing the scene, rather than adding to it.

Similarly MOD can be used to darken a scene, and add really rich vibrant color to it, without degrading image quality. This is fairly easy to demonstrate, using a screenshot from another SNES game.
(http://i.imgur.com/fvPRzqH.png)

At the top we have a BLEND mode style layering, with a blue color, at 75% opacity. It... doesn't look so great, and I wasn't able to make it much darker than that without the image turning more black than blue. Below that is a MOD style layer, which darkens the image with a rich blue color, without making the scene hazy, and it looks like a nice nighttime effect.

With the MOD blendmode, one could make an active day and night cycle by rendering a surface with different colors for different times of day, or shadows which darken sprites under them, without making them look smoky (like what happens with a black or gray semitransparent BLEND shadow)

As far as I could locate, the git src only sets the blendmode twice, so it seems like adding a blendmode property to the solarus drawable class and passing that as an argument, should work... But that's only as far as I could find. I'm sure Christopho knows the source better than I.

Anyway, that's my argument for why it would be a decent feature to have.
Title: Re: Feature Request: Change a surface's blend mode
Post by: Christopho on July 06, 2016, 09:04:36 pm
I am not an expert with blend modes, but your example is very convincing.
From the Lua API point of view, maybe the blend mode could be an optional additional parameter to drawable:draw(), or maybe we could make drawable:get/set_blend_mode(). The second option has my preference because drawable:draw() already has optional parameters, let's don't mix everything.
Title: Re: Feature Request: Change a surface's blend mode
Post by: Satoh on July 06, 2016, 10:53:05 pm
I was indeed imagining something like a drawable:set_blend_mode(mode) myself.
Sprites inherit from drawable as I understand it, so it should in theory work on manually defined surfaces as well as sprites right?

EDIT:
Link to SDL documentation (https://wiki.libsdl.org/SDL_SetRenderDrawBlendMode) on what the blend modes do. Pretty sure only the function needs to be called.
There are some other SDL_Set____BlendMode type functions, but the one in the link is the only one I found directly when I looked in the git.
Title: Re: Feature Request: Change a surface's blend mode
Post by: wrightmat on July 07, 2016, 04:07:33 am
This would be amazing, and it sounds like it would be easy to implement! Maybe we could even have it (at least experimentally) for 1.5?
Title: Re: Feature Request: Change a surface's blend mode
Post by: Christopho on July 07, 2016, 12:39:32 pm
If you insist :)
Title: Re: Feature Request: Change a surface's blend mode
Post by: wrightmat on July 08, 2016, 06:05:39 am
Haha, you're the best! It'll be really cool to see what we can do with it :)
Title: Re: Feature Request: Change a surface's blend mode
Post by: Neovyse on July 08, 2016, 09:20:50 am
Nice idea !
Title: Re: Feature Request: Change a surface's blend mode
Post by: Christopho on July 11, 2016, 06:19:57 pm
Done in 1.5! http://www.solarus-games.org/doc/1.5/lua_api_drawable.html#lua_api_drawable_set_blend_mode
Note that it also solves the problem of http://forum.solarus-games.org/index.php/topic,686.msg3500.html#msg3500 :D
Title: Re: Feature Request: Change a surface's blend mode
Post by: Satoh on July 11, 2016, 07:29:35 pm
Done in 1.5! http://www.solarus-games.org/doc/1.5/lua_api_drawable.html#lua_api_drawable_set_blend_mode
Note that it also solves the problem of http://forum.solarus-games.org/index.php/topic,686.msg3500.html#msg3500 :D
I'm excited to test it out! Thanks for the heads-up
Title: Re: Feature Request: Change a surface's blend mode
Post by: Zefk on July 12, 2016, 12:35:01 am
This is magnificent.  Does that mean the PDF version of the documentation will be updated?
http://www.solarus-games.org/doc/solarus-doc-v1.5.pdf
Title: Re: Feature Request: Change a surface's blend mode
Post by: MetalZelda on July 12, 2016, 01:45:38 pm
I already have a fully functionnal Day/Night system for my Zelda project, I should give a look at blending modes.

Thus, if the result is correct, I'll maybe release it.

Is it available in a released screenshot ?
Title: Re: Feature Request: Change a surface's blend mode
Post by: Christopho on July 12, 2016, 03:40:24 pm
Here is a new snapshot with the feature! http://www.solarus-games.org/downloads/solarus/win32/solarus-1.5.0-snapshot-20160712-win32.zip
Title: Re: Feature Request: Change a surface's blend mode
Post by: MetalZelda on July 12, 2016, 04:53:29 pm
I do have to guess that to be better, the day/night tone surface needs to be in "color_modulate" blending ?

But, strange thing is, if a surface's color is at 0, 0, 0, 0, it is really dark (while it's alpha is at 0), I don't understand much about surface blendings but if someone has any clues I'll gladly take it.

I'll let a video about the different blendings mode available, with target colors unchanged, and let the OP decide about what could be the best option for this kind of system, thus, I'll change tones

Edit: here

https://www.youtube.com/watch?v=tvZ0mJAmQy8&feature=youtu.be
Title: Re: Feature Request: Change a surface's blend mode
Post by: YoshiMario2000 on July 12, 2016, 05:43:08 pm
...if a surface's color is at 0, 0, 0, 0, it is really dark (while it's alpha is at 0)...
I think what is going on in the video is that there is no alpha, rather than there being alpha.
Try changing the alpha value around, That might fix the darkness you see in the video.
Title: Re: Feature Request: Change a surface's blend mode
Post by: MetalZelda on July 12, 2016, 06:26:45 pm
...if a surface's color is at 0, 0, 0, 0, it is really dark (while it's alpha is at 0)...
I think what is going on in the video is that there is no alpha, rather than there being alpha.
Try changing the alpha value around, That might fix the darkness you see in the video.

It didn't

The surface at daytime is completely clear (0, 0, 0, 0), from 7AM to 4PM and it give a pitch black surface with color moduling blending.
There is not so much documentation about this blending, so I don't know how this works, but it is very dark afaik

Edit: putting everything at 255 makes everything normal
Oh, I see ...
Title: Re: Feature Request: Change a surface's blend mode
Post by: Christopho on July 12, 2016, 07:42:19 pm
The doc gives the exact formulas: http://www.solarus-games.org/doc/1.5/lua_api_drawable.html#lua_api_drawable_set_blend_mode
If you set RGB to all zero and use color modulate, you obtain black because it is a multiplication.
Title: Re: Feature Request: Change a surface's blend mode
Post by: Satoh on July 12, 2016, 11:15:40 pm
a lot of neat effects can be gained by drawing surfaces with different blend modes onto each other, before finally rendering them on the screen. This is exactly what I hoped for.

For those who don't fully comprehend blend modes, here's a quick rule of thumb:

Treat the 0-255 values as though they are 0-100% or 0.0-1.0
(You still have to type them in as 0-255, but the math they do is more equivalent to 0.0 to 1.0)

Multiplication works this way. If you have 0.5 * 0.5 (which would be 128*128 RGB) what you end up with is 0.25 (which turns into 64 in RGB equivalent.)

Additive works the same basic way. If you add 0.75 (or 192) to 0.75 (192) you end up with 1.5 (technically 384, but it gets capped at 255)

Keeping that in mind, you just have to imagine whether you want to make something darker (by making the numbers smaller, by multiplying their percentages) or brighter (by adding them together).


If you set up 2 surfaces, lets say
Code: [Select]
local shadow = sol.surface.create()
local light_mask = sol.surface.create()
--You can then set them to two different blend modes
shadow:set_blend_mode("color_modulate")
light_mask:set_blend_mode("additive_blending")
--now, lets pretend each entity in the map will draw a 'glow sprite' onto the light_mask surface
--having drawn those entities' sprites in light_mask, we can use it to cut away selective dark areas in shadow
--by simply drawing it into the shadow surface
light_mask:draw(shadow)
--and finally we can draw it in our scene like so

local scene_surface = sol.surface.create()

map:on_draw(scene_surface)
  shadow:draw(scene_surface)
end
and our map will be darkened everywhere there is not an entity drawing a light mask sprite

*Note that trying to change the blend mode on [map:on_draw(scene_surface)] directly, it doesn't seem to have an effect.
Title: Re: Feature Request: Change a surface's blend mode
Post by: MetalZelda on July 12, 2016, 11:38:10 pm
Okay, I get it, I finally added the blending effect and changed the values, etc.

But, except for being darker, I don't notice that much difference, except colors are more vibrant in sunset / sunrise and more natural at night time.
Looks fine to me, but again I'm a noob at these things, and OP seems to know these so I need some feedbacks

Before & After (Don't pay attention to the clouds, I'll look at them later)

(http://i.imgur.com/q6LsDIT.png)(http://i.imgur.com/yKbFYWY.png)

Am I doing it wrong ?
Title: Re: Feature Request: Change a surface's blend mode
Post by: Satoh on July 13, 2016, 12:34:31 am
It depends. What effect are you trying to achieve?

If you want the daytime to be bright, set the surface to pure white, and it will not be darkened at all.
If you want the daytime to be yellow tinted, set the color to fully bright yellow. (255,255,0,255)
if you want the scene to turn blue, set the color to (0,0,255,255).
If you want the scene to be slightly darker, with a little bit of a blue tint, set it to something like (192,192,255,255)
If you want it really dark and slightly blue, set it to (64,64,128,255)

The reason to use modulate blending is to darken the scene without making it look smokey and desaturated, like what happens when you just use a regular semitransparent solid color on top of it.

That is, if you used a dark color, with blend mode Blend, at 50% transparent, the blacks would lighten and the lights would darken.
With color_modulate the darks stay dark, but the lights get darker too.

With additive, the lights stay light, and the darks get lighter, but colors stay bright, instead of being washed out, like the same that happens in the previous example.

Color choice is really important  with blend modes.

Additive blend is really good for things like fire and light beams and mirages, modulate is better for things like shadows and color tinting.

Light colored mods are best for slight effects, while dark colored mods will have more severe effects. Pure white mods will be invisible. (multiplying 1 * whatever)

Dark colored adds will be better for slight effects, like a dim glow from a candle, while brighter adds will more sharply glow (until they reach pure white). Pure black adds are completely invisible (adding 0 + whatever)
Title: Re: Feature Request: Change a surface's blend mode
Post by: MetalZelda on July 13, 2016, 12:43:22 am
I wanted to make something similar to your second screen , but I'm satisfied with the current result, sunrise, sunset and night feels right, and much vibrant than before
Title: Re: Feature Request: Change a surface's blend mode
Post by: Satoh on July 13, 2016, 01:09:51 am
I wanted to make something similar to your second screen , but I'm satisfied with the current result, sunrise, sunset and night feels right, and much vibrant than before
Well, I don't remember the exact values I used in the original screenshot, but
Code: [Select]
shadow_surface:fill_color({32,64,240,255})
shadow_surface:set_blend_mode("color_modulate")
Gets really close to it.

And yeah, your sunrise/set look pretty rich and nice.
Title: Re: Feature Request: Change a surface's blend mode
Post by: Christopho on July 13, 2016, 08:35:03 am
Thanks for the feedback!
Satoh: since you are clearly an expert, can you tell me if the names I chose are appropriate blending mode names: "none", "alpha_blending", "additive_blending" and "color_modulate"?
Instead of "color_modulate", would "modulate_blending" be better?
Title: Re: Feature Request: Change a surface's blend mode
Post by: Diarandor on July 13, 2016, 10:50:32 am
Hi, I could be wrong but I think something is wrong with the definition of alpha blending.
I wrote the explanations here:
https://github.com/christopho/solarus/issues/931

EDIT: I added another issue to request custom blendings. That may be very useful:
https://github.com/christopho/solarus/issues/932
Title: Re: Feature Request: Change a surface's blend mode
Post by: Satoh on July 13, 2016, 11:02:27 pm
Well if its just naming convention, most engines simplify it to 'None', 'Alpha'/'Blend', 'Add', 'Multiply'
Some engines also have a variant of additive that is subtractive ( (-1*source) +destination )** which is sort of similar in effect to multiply, but applied differently and with different side effects... (still used usually for the same goal, darkening).

But if its preference for naming... none, alpha, add, multiply are the most directly intuitive (not only because that's what many engines call them, but also because that's the operation they do mathematically)

@Diarandor
the four modes Christopho added were predefined by the SDL library. It would likely require a much deeper amount of digging into that to supply a custom blend mode. Not to say it wouldn't be a neat feature, but it would likely be more difficult to implement.

**There are also 'power' /'exponent' and 'divide' modes... none of which I've ever actually seen used outside of purely mathematic calculations.


Incidentally, it seems like certain blend modes don't work well on sprites. I may just be testing it wrong, but switching a sprite to additive_blending or color_modulate seems to make them not draw at all on a surface. (It might be that I'm misunderstanding them though. I'm using sprites like any other surface, with a simple sprite:set_blend_mode("additiv_blending") sprite:draw(dst_surface,x,y) )

That might be something to test and look into.. I'm not sure. (Sprites inherit from drawable, but they also have a lot more code, so I don't know)
Title: Re: Feature Request: Change a surface's blend mode
Post by: Christopho on July 13, 2016, 11:43:31 pm
It is probably a bug if sprites don't work with new blending modes. You can open an issue on github.
Ok I think I will rename the blending modes before the release, thanks for the info.
Title: Re: Feature Request: Change a surface's blend mode
Post by: Diarandor on July 14, 2016, 09:38:25 am
Thanks for the info, Satoh.

I thought the alpha blending had a unique definition, but I was wrong. The definition of the Wikipedia is probably the most standard definition as I have read (and it has very nice properties, like associativity). The definition given in the wiki of the SDL library seems different and non-equivalent (it almost coincides with the formula for pre-multiplied alpha, but there appears one factor that should not be there in the formula, as you can check with the Wikipedia: https://en.wikipedia.org/wiki/Alpha_compositing#Alpha_blending); maybe their code is fine and it's just that their documentation has a mistake in the formula, who knows. But it is ok anyway, even if the alpha blending is a different ones, since the results are similar in most of the cases. Well, I might be wrong in all of this, so it does not matter at all.

At first I thought all of this had been progammed by Chris, that's why I opened the issues, but I was wrong, this all comes from some SDL library and we cannot change it. So forget about this. I will close both issues. Sorry for the annoyance.
Title: Re: Feature Request: Change a surface's blend mode
Post by: wrightmat on July 15, 2016, 12:33:32 am
Satoh, using your example code, what kind of image would you want to use to create the brightened area? I'm currently trying with a black image where a white circle represents the light area and it doesn't seem to be working. I'm trying to attack a glow to some statues that I already have in a map, but while dimming the entire room works, I'm not getting any glow (I'm attempting to create the glow sprite at run time and draw everything to the shadow surface, which I am drawing in map:on_draw).

Code: (lua) [Select]
    shadow:fill_color({32,32,255,255})
    shadow:set_blend_mode("color_modulate")
    light_mask:set_blend_mode("additive_blending")
    for entity in map:get_entities("statue") do
      local x, y, z = entity:get_position()
      local sprite = sol.sprite.create("entities/torch_light")
      sprite:set_xy(x, y)
      sprite:draw(light_mask)
    end
    light_mask:draw(shadow)
Title: Re: Feature Request: Change a surface's blend mode
Post by: Satoh on July 15, 2016, 02:12:55 am
Well... I'm not sure exactly why you're not getting the effect you need, but here's how I did mine (note that I used all hard coded values for stuff in this specific map, because its just testing things.)

Code: (lua) [Select]
local map = ...
s = sol.surface.create(512,256)--s is my shadow
s2 = sol.surface.create(512,256)--s2 is my light
s:set_blend_mode("color_modulate")
s2:set_blend_mode("additive_blending")
local ss = sol.surface.create()--ss is my final surface for drawing the screen
function map:on_draw(ss)
  local x,y = map:get_camera():get_position()
  local w,h = map:get_camera():get_size()
  s:clear()
  s:fill_color({032,064,128,255})
  s2:clear()
  for e in map:get_entities("lit_") do
    local xx,yy = e:get_position()
    local sp = sol.sprite.create(e.sprite or "glow")
    sp:set_blend_mode("alpha_blending")
    sp:draw(s2,xx,yy)
  end
  s2:draw_region(x,y,w,h,s,x,y)
  s:draw_region(x,y,w,h,ss)
end

in your code I think
Code: (lua) [Select]
local mapsurface = sol.surface.create()
function map:on_draw(mapsurface)
-------------------------------------------------
    shadow:fill_color({32,32,255,255})
    shadow:set_blend_mode("color_modulate")
    light_mask:set_blend_mode("additive_blending")
    for entity in map:get_entities("statue") do
      local x, y, z = entity:get_position()
      local sprite = sol.sprite.create("entities/torch_light")
      sprite:set_xy(x, y)
      sprite:draw(light_mask)
    end
    light_mask:draw(shadow)
    ------------------------------------------
    shadow:draw_region()--...stuff for camera here, my code has an example of how to get the portion of the surface to draw
end

You need three surfaces at least, as far as I can manage.
You draw your lights onto the additive surface
you draw the additive onto the modulate
and you draw the modulate onto the map's on_draw surface

I got the coords and size of the camera, so I could draw a portion of the map that was only as big as the screen, but my shadow and light surfaces were the full size of the map. (I only drew portions of them at a time though)

Setting sprites to additive directly, doesn't seem to work correctly right now. But try looking at my code (sorry its so undescriptive with its names...) to see where yours differs. Mine works for me.

EDIT:
I'd like to point out I'm no expert on the workings of surfaces in Solarus. I only just started messing with them when the blendmode snapshot came out.
I just kept trying things until I found what worked. I have some experience with blend modes and lua previous to this, but otherwise its all just something I'm learning as I go. Just a disclaimer in case there's a question I can't answer.
Title: Re: Feature Request: Change a surface's blend mode
Post by: wrightmat on July 15, 2016, 03:06:05 am
Alright - got it to work! Thanks!

I think something else on the map was throwing things off, so I tested on a fresh map and then moved the correct code. This actually cleaned up my code by a lot and created an infinitely better effect!
Title: Re: Feature Request: Change a surface's blend mode
Post by: Christopho on July 15, 2016, 07:40:41 pm
I renamed the blend modes to "none", "blend" (default), "add" and "multiply".
Don't be surprised with the next snapshost or the release.