Open JNechaevsky opened 5 years ago
Let's get the facts sorted: We have two types of translucency, alpha blending and additive blending. The latter is used for full-bright sprites (including the laser spot), the former for anything else. We agree that the current translucency value of blend_alpha = 0xa8
(which is 66% of 0xff
) doesn't provide for optimal visibility of alpha-blended sprites. We should try to make them a bit more opaque by using values such as 0xc0
(75% of 0xff
) or 0xcc
(80% of 0xff
).
This all applies to alpha blending. Now comes the tricky part: We don't even use an alpha value for additive blending! RGB values are simply added and capped at 0xff
. So, how do we improve this algorithm?
One thing to keep in mind: Visibility through translucent objects is an issue, but at most a secondary one. Vanilla Doom had no translucency at all, so all objects were fully opaque and thus obscured the player's sight. So, any little impression of the game world behind a translucent sprite is already a net tactical improvement.
So, any little impression
You've got me absolutely right, thanks! Exactly a little, to keep original shape and volume of the objects. Additionally, though, there might be two possible levels, like:
Probably this will make everyone happy.
We don't even use an alpha value for additive blending!
What? How? :anguished: I wasn't know that, honestly. I have no any technical ideas about improvement, so... Maybe we just can do not use additive blending, considering only standard alpha blending for everything. This also will probably fits well with "one logic" approach, and will be safe for non-standard sprites, because we can't predict everything. And of course, this will make sprites looking good on white or bright backgrounds. And also, this way there will be much less work to do.
I'll probably again will be busy in the evening, but anyways, I definitely wants to play around with blend_alpha
.
P.S. If you will have a free minute before deep evening (~23:00), could you please send a screenshot or two of blend_alpha
80%
and 85%
, with BFG ball fired on MAP13 (on the white floor texture)? I really wants to see this ASAP, but can't do even simple change in the code because of lack of time.
Sorry, I don't think I can offer this today.
@fabiangreffrath I'm changing blend_alpha
from 0x00
to 0xff
and don't see any difference, even with && !(thing->flags & MF_TRANSLUCENT))
addition. Am I doing something wrong? Or something not ready yet in the code?
Also, there is a small visual glitch with changing gamma level while paused state, probably caused by reducing amount of red pain effect while paused state. Nothing critical, just attracted my attention. Note how rocket explosion "flashing" for one tic after changing gamma level:
even with
&& !(thing->flags & MF_TRANSLUCENT))
addition.
Please don't do that! It only prevents translucent sprites from being rendered full-bright, but this didn't even remotely bring the desired effect. Please do not use this anymore, ever.
Am I doing something wrong? Or something not ready yet in the code?
You'll need to get rid of additive blending (which doesn't use this alpha value at all) entirely, e.g.
--- a/src/doom/r_things.c
+++ b/src/doom/r_things.c
@@ -802,7 +802,7 @@ void R_ProjectSprite (mobj_t* thing)
// [crispy] translucent sprites
if (thing->flags & MF_TRANSLUCENT)
{
- vis->blendfunc = (thing->frame & FF_FULLBRIGHT) ? I_BlendAdd : I_BlendOver;
+ vis->blendfunc = I_BlendOver;
}
#endif
}
@@ -897,7 +897,7 @@ static void R_DrawLSprite (void)
vis->translation = R_LaserspotColor();
#ifdef CRISPY_TRUECOLOR
vis->mobjflags |= MF_TRANSLUCENT;
- vis->blendfunc = I_BlendAdd;
+ vis->blendfunc = I_BlendOver;
#endif
vis->xiscale = FixedDiv (FRACUNIT, xscale);
vis->texturemid = laserspot->z - viewz;
Also, there is a small visual glitch with changing gamma level while paused state, probably caused by reducing amount of red pain effect while paused state. Nothing critical, just attracted my attention. Note how rocket explosion "flashing" for one tic after changing gamma level:
Hm, yes, needless to say that palette and gamma changing works quite a bit different in truecolor mode. Let's keep this in mind, but there are more important glitches to fix. :wink:
With the change I posted above, I think translucency is already looking pretty fine!
Uh-oh, 0xcc
(80%) is way too solid, it is barely possible to see anything through this. Unpredictable. I was expecting a bit different result, not so solid, but it is obvious - transparency looking different in 256 (remembered! it's a "two hundred fifty six"!) and in true color.
Even 0xc0
(75%) is looking a bit solid, but -- it seems to be good enough for "conservative" mode.
Initial 0xa8
is fine for "modern" mode. There is only one thing which makes me sad: in contrast of brightmaps, transparent sprites with this level is looking too dimmed (screenshot).
There have to be a way to lit transparent sprites w/o an additive blending... But I can't imagine it for now. Is it possible to draw sprite with primary alpha blending, and "lit up it just a little bit more" somehow?
Edit: Or maybe I was playing in 256 color mode too much, so my eyes are too used to see a more familiar transparency. I need some more time, first impression is not always correct. But anyways, true color is an awesome thing!
Duh, I can fall asleep, but it is too late for today to try this - Photoshop is offering various blending modes (screenshot). Some of them are, let's just say, making a layer brighter, some of them darker, some are mixing colors in different ways. I need to try to do this:
Maybe that will be the Key we are seeking for. And even if we'll find that key, another question is - how it should be represented in the code. But at least, it's worth to try.
how it should be represented in the code
https://developer.nvidia.com/content/transparency-or-translucency-rendering
Naive implementation:
--- a/src/i_video.c
+++ b/src/i_video.c
@@ -95,6 +95,7 @@ static SDL_Texture *yelpane = NULL;
static SDL_Texture *grnpane = NULL;
static int pane_alpha;
static unsigned int rmask, gmask, bmask, amask; // [crispy] moved up here
+static uint8_t rshift, gshift, bshift, ashift;
static const uint8_t blend_alpha = 0xa8;
extern pixel_t* colormaps; // [crispy] evil hack to get FPS dots working as in Vanilla
#else
@@ -1475,6 +1476,11 @@ static void SetVideoMode(void)
SDL_FillRect(argbbuffer, NULL, I_MapRGB(0x0, 0xff, 0x0));
grnpane = SDL_CreateTextureFromSurface(renderer, argbbuffer);
SDL_SetTextureBlendMode(grnpane, SDL_BLENDMODE_BLEND);
+
+ ashift = argbbuffer->format->Ashift;
+ rshift = argbbuffer->format->Rshift;
+ gshift = argbbuffer->format->Gshift;
+ bshift = argbbuffer->format->Bshift;
#endif
SDL_FillRect(argbbuffer, NULL, 0);
}
@@ -1866,11 +1872,15 @@ const pixel_t I_BlendAdd (const pixel_t bg, const pixel_t fg)
{
uint32_t r, g, b;
- if ((r = (fg & rmask) + (bg & rmask)) > rmask) r = rmask;
- if ((g = (fg & gmask) + (bg & gmask)) > gmask) g = gmask;
- if ((b = (fg & bmask) + (bg & bmask)) > bmask) b = bmask;
+ r = ((fg & rmask) >> rshift) + (((0xff - blend_alpha) * ((bg & rmask) >> rshift)) >> 8);
+ g = ((fg & gmask) >> gshift) + (((0xff - blend_alpha) * ((bg & gmask) >> gshift)) >> 8);
+ b = ((fg & bmask) >> bshift) + (((0xff - blend_alpha) * ((bg & bmask) >> bshift)) >> 8);
- return amask | r | g | b;
+ if (r > 0xff) r = 0xff;
+ if (g > 0xff) g = 0xff;
+ if (b > 0xff) b = 0xff;
+
+ return amask | (r << rshift) | (g << gshift) | (b << bshift);
}
// [crispy] http://stereopsis.com/doubleblend.html
Naive implementation:
It looks... A bit better, like sprites becomes more opaque and a bit more bright.
From my side, I think I'm on right track. There are two blending modes, one is standard alpha (66% opacity), second is called "Color dodge" (100% opacity, yay!). BFG ball sprite is not touched, i.e. no corrections were made. But I need more backgrounds to test, like midtones and colored ones... Here it is: screenshot.
Tested with different light levels and background colors: screenshot (1.5 Mb, kinda huge, but I need to see all the pixels). Double-blending approach is same in all areas.
Is it we are seeking for? What's your opinion?
Edit: Try to open this mockup with Gimp, it will explain pretty much everything! PSD file is friendly with Gimp 2.10.8: DOOM0014.zip
Looks good! Though, not everything that looks good on static images does so in moving frames. Let's see if I can ever get this implemented...
One more question, I am not sure I get this right: Now we have two layers, one with alpha blending and one with color dodge. But, how are these two layers combined?
Actually... I can make a video and place BFG ball with these blending modes there. :grinning: In the mockup, layers with BFG ball are simply placed in same coords, one layer on top of the other. In the code, though, it should be different:
But all these calculations should be done with one sprite. The order is critically important, otherwise the sprite will turn into something awfully bright.
Oops, I wasn't clear enough in my answer.
If I'll combine all three layers into one (background + dodge + alpha), everything will be okay because all pixel data is all set. If I'll combine just two (dodge + alpha), transparency will gone, because blending mode will be different in fact.
In the engine everything should be calculated dynamically and simultaneously, all routines in one tic. I think so.
Maybe this will prove helpful: https://photoblogstop.com/photoshop/photoshop-blend-modes-explained
According to this article, color dodge won't work with integer R/G/B values because of the way math works. Consider this:
1.0 / 0.5 = 2.0
255 / 128 = 2
0xff / 0x80 = 0x02
Same calculation with the little difference that 2.0
means "over the top bright" and 0x02
means "dark close to black". :weary:
Wha-? :frowning: But it must be okay. Have a look, if I'll remove alpha blending sprites from top of dodge'ed, here's what will happen: http://priscree.ru/img/2dabfb7703eb18.png The trick is using two blending modes, not just one.
Ir I got something wrong? Is a katastrofa and we had to find another aproach?
Na, this is just the reason why the code I drafted didn't immediately work. Basically it's just fixed-point math with FRACBITS = 8
and FRACUNIT = 256
. I just have to wrap my head around it...
There are couple of things, which makes me worry in the transparency. But first of all, we need a map, not even a simple testing map, we need a galley with all transparent items (1), placed in three different light levels & backgrounds: light-gray-dark (2). Here it is: gallery.zip
1. About the items.
I'm using this approach:
Have a look at this invul sphere (screenshot). It's too transparent, just like a … “soap bubble”. Or here, Supercharge is barely notable in the dark (screenshot). This should not happen.
With a additive approach some things becomes better, but not always, they are not looking so good in the bright areas (screenshot)
Suggested approach: reduce amount of transparency, let items be more opaque.
2. About the projectiles
Mostly same logic as above, but there is a difference. Have a look at non-additive fireball (screenshot)
Yes, it is transparent, but in fact, it’s not looking proper, no longer bright, as it should be. I’m have to agree, that additive transparency may looking to vargant, but must admit, that it’s a proper way to represent fire and plasma transparency by more realistic way.
Suggested approach: left additive approach for projectiles, but also reduce amount of transparency.
There is one more important vice-versa aspect here: exploded projectiles must not be too solid (but neither too transparent). Ultimately notable example - exploded Imp fireball above player face. If player is using transparency, he is expecting to see through this flame.
3. About the muzzle flashes.
Same problem. Muzzle flash must not looks like this (screenshot), ideally, it should be something like this (screenshot - bright, and just a very little transparent).
Suggested approach: maybe an additive transparency should be used, again, with reduced amount of transparency?
To summarize, most of all written above comes to me in RD, where I was trying to make two things:
This is just my vision, your decision should remain only your of course. Anyways, which of described above we should take care about first?
Suggestion №108