Sorry your browser is not supported!

You are using an outdated browser that does not support modern web technologies, in order to use this site please update to a new browser.

Browsers supported include Chrome, FireFox, Safari, Opera, Internet Explorer 10+ or Microsoft Edge.

DarkBASIC Professional Discussion / Coil-shaped Health bar HUD based on shader + sprite - is it real?

Author
Message
Umbra Stellar
8
Years of Service
User Offline
Joined: 17th Jun 2016
Location:
Posted: 6th Dec 2024 13:14 Edited at: 6th Dec 2024 16:04
Hello everyone who understands shaders, effects and texturing.

I need help in creation of health indicator in the form of a ring, designed as PNG file with transparency. Shader must transform the ring texture, erasing a sector with the specified parameters - angles and radius.

AI already helped me with shaders (I myself do not know how to write them).

Is it possible to implement an algorithm?

1. Load an image from a PNG file

2. Load an effect with a shader and apply it to the image, passing pre-calculated parameters of angles and radii.

3. Create a sprite and use it as a HUD with a texture, periodically modifying it.

Here is some code and graphics PNG, BMP and screenshot of idea for experiments.

Appreciate any help and advices!



SHADER:

Attachments

Login to view attachments
Umbra Stellar
8
Years of Service
User Offline
Joined: 17th Jun 2016
Location:
Posted: 7th Dec 2024 07:03
Looks like we can't use vertex shaders for sprite textures, we can apply effects onlyon objects, f.e. textured plane (in the case of HUD).

Okay, any Ideas of how to realize this idea through planes?

Or may be we can modify textures on hidden planes and then just re-use such image ID for sprite?
Mage
Valued Member
18
Years of Service
User Offline
Joined: 3rd Feb 2007
Location:
Posted: 7th Dec 2024 10:48
The best way to do this in Dark Basic Professional would be to create the HUD "ring" as a 3D object, even if it was completely flat. You can then arrange the texture mapping, so that you adjust/shift the texture to make the yellow portion appear to move across the ring.

You'd do this by making the texture half red and half yellow. You'd take a 3D strip (a plane with a lot of vertices, to maintain the roundness) and bend that strip into a ring. Map the strip before bending it. The strip would only be mapped to the red side of the texture. So when you shift the texture, the yellow would start to cover portions of the strip. Done properly it would create the effect you wanted. You could even get fancy and animate the texture or use a gradient instead of a solid color.

No need for a shader to do this.

Depending on the program you intend to use this for, you'd probably disable the z sorting on the hud object to keep it above everything.

This probably isn't needed, but getting crazier:
If you needed it to be a live "2D" element like a sprite or image (3D object was just impossible for some reason) there are even ways to do this. I have posted about creating transparent screenshots and transforming 3D objects to a 2D image, maintaining background transparency (which normally is not possible). This included an implementation using imagekit v2 plugin to do this live. Converting to 2D is not recommended as it would even with clever implementation come at a performance penalty.

Mage's Modular Code - Get awesome UI controls and powerful Bitmap Fonts!
Screen Device Recovery - Stop your apps from crashing when minimized/tabbed/screen locked.
CPU Friendly Frame Limiter - Make your app generate less computer heat + noise, and use less battery life!
Umbra Stellar
8
Years of Service
User Offline
Joined: 17th Jun 2016
Location:
Posted: 7th Dec 2024 11:38 Edited at: 8th Dec 2024 09:45
Mage, thanks for the brainstorm! Great idea, but looks too complicated for my project and for me who hasn't worked with real 3D spools yet. I'm not a 3D modeler.

I plan to use this algorythm many times in many different ways on many small 2D GUI elements (even if they are textured 3D planes), and that the "coils" could be not only gradient filled, it can be a snake (ouroboros with non-constant transparent body edges), or a burst of energy in a shape of color, or a dynamically generated coil chart, or a volume/speed indicators.

That is why shader is universal idea for all this cases (without preparing tons of alpha-masks and 3D modelling)
All I need to do is just pass the inner and outer radius of the ring and the sector angles to the shader, and then erase part of the ring image.

The only difficulty for me is to implement the algorithm on shaders and transparent PNG.
But if shader can work only with non-transparent BMP textures this idea failed.

And it will be easier for me to create tons of masks for many kinds of artistic coils (like snakes or energy splashes).

Here is corrected program from IDE without pseudocode and errors.
But shader don't work.
May be I use it wrong way and also need to normalize both of radius in shader.

DBP Program:




SHADER (which is not work for me, even if just return source color, without any mathematics)

Umbra Stellar
8
Years of Service
User Offline
Joined: 17th Jun 2016
Location:
Posted: 7th Dec 2024 12:07 Edited at: 8th Dec 2024 09:48
It looks like I give shader non-normalized radiuses, and already updated my previous DBP program.

Anyway, I see no any changes by shader even when I just return a yellow color (during tests) - I don't see the texture turning completely yellow as expected
Umbra Stellar
8
Years of Service
User Offline
Joined: 17th Jun 2016
Location:
Posted: 8th Dec 2024 09:33 Edited at: 8th Dec 2024 09:51
Well, I just solved this idea without shader (this way pretty fast for small HUD elements, despite the fact that it uses a CPU only when need to modify health bar, instead of GPU, which used shader every time using original texture before every sync).
And most importantly, edited textures can be used with sprites or/and planes.

1. Creating memblock from image.

2. Passing through the whole memblock in a loop, erasing the colors inside the desired sector of the coil with simple mathematics.

3. Restore image from memblock (and the result will be visible automatically on sprites and planes that use a texture with such ID).
________
P.S. Yes, experienced shader-gurus may answer that we also can return the shader work result to the original texture into memory, activating the shader only when we need to change Health, and avoiding the constant GPU load. But How? I would be glad to see such solution. Or may be somebody correct my last code and write why my shader not works at all
Mage
Valued Member
18
Years of Service
User Offline
Joined: 3rd Feb 2007
Location:
Posted: 9th Dec 2024 10:24
The arguments against this approach are that doing this on each frame (or every other) would be very costly. It also would not support aliasing of any kind, so the effect produced (at best) would have hard edges.

Mage's Modular Code - Get awesome UI controls and powerful Bitmap Fonts!
Screen Device Recovery - Stop your apps from crashing when minimized/tabbed/screen locked.
CPU Friendly Frame Limiter - Make your app generate less computer heat + noise, and use less battery life!
Umbra Stellar
8
Years of Service
User Offline
Joined: 17th Jun 2016
Location:
Posted: 10th Dec 2024 13:57
Mage, in case of health - we don't need to constantly postprocess the health bar before each sync during the whole game. We need it only when the hero gets damaged or healed. So it reduces some of the CPU load. In case of using simple horizontal bars, we also often using such method by copying part of the texture with the full progress bar into the empty part.

I implemented antialiasing effect using smoothstep() - look at the shader code. It has a well-known and simple code. Just check the boundaries and calculate the transparency level of the pixels (I also thought it had complex math before learning this method.)

During the test, I pressed the damage key and my non-hi-end processor gave me good performance by randomly cutting the portions of coil without fps() drops. Of course, the texture can't be very big. But even the health bar from my attachment is bigger than I need as a real HUD.

So this method can be used instead shader.
Mage
Valued Member
18
Years of Service
User Offline
Joined: 3rd Feb 2007
Location:
Posted: 10th Dec 2024 15:53
It's true you should only be regenerating the HUD element when it changes, as a performance optimization.
There is no wrong answer here, you just need to decide which implementation suits your situation.

You should find that having a 3D plain (whether it's been coiled or left straight), and shifting the texture U/V to provide the progress effect, will be the best performing option mentioned in DBP. It's just rendering a textured plain, and adjusting it is just changing a single u/v value.

You'll find the shader to be the second best performing (still very good since hardware is highly optimized for shaders), but the hardest to implement because it requires writing a shader or editing a shader for the specific use case.

Thirdly the memblock image conversion method requires a lot of conversion and processing processing for those texture bits. You also need to loop some logic to covert pixels to a new color. Any kind of aliasing will be difficult to handle. So you'll either be left with jagged edges on the texture avoiding aliasing altogether, aliasing artifacts where some pixels were missed, or heavy slow logic needed to process the aliasing. The size of the image will also quickly affect the processing time of this method.

All three methods are perfectly valid ways to do things.

Mage's Modular Code - Get awesome UI controls and powerful Bitmap Fonts!
Screen Device Recovery - Stop your apps from crashing when minimized/tabbed/screen locked.
CPU Friendly Frame Limiter - Make your app generate less computer heat + noise, and use less battery life!
Umbra Stellar
8
Years of Service
User Offline
Joined: 17th Jun 2016
Location:
Posted: 11th Dec 2024 06:15
As I mentioned earlier, there were no problems with the antialiasing effect when processing circles, circle sectors or ring sectors. All this is easily solved by the smoothstep() function for those situations when you need to erase part of the image - another words change color's alpha values. A smooth edge can be drawn or erased both when approaching the edge of a circle or ring (inner or outer) and when approaching the sides of a sector. Although I am not a 3D modeler, I am a graphic designer. And if it were impossible to achieve smooth edges, then for the sake of beauty in the game I would use heaps of prepared masks, even if I had to draw all 100 states for every % of the ring progress bar. But I did not have to do this.

Optimization can be achieved if you provide inner and outer radius a little bigger (+1 or +2 px) then real designed radius, because you already have well-designed or drawn texture of ring (snake / energy burst / swirl etc.) with antialiased edges, and you need just smooth transition on two sides of cutting sectors. So all we need to check angles and calculate new alpha of color using smoothstep(). If you check very thin transition spaces (1-2grades) you'll get antialiased edges. If you will work with more than 3grades, you can get achieve even gradient alpha fading effect for the edges.

I use changing UV coordinates on the plane texture only when animating looped textures to imitate the movement of clouds or dotted lines using the scroll texture object function. How this function would help me with the ring, I do not quite understand yet.

If I could model and texture complex objects, I would probably create a flat object in the form of a ring, consisting of many sectors, in which I could hide fragments of sectors, imitating a progress bar. But alas, I am not a 3D modeler, but only a designer and programmer
Umbra Stellar
8
Years of Service
User Offline
Joined: 17th Jun 2016
Location:
Posted: 11th Dec 2024 07:06
I think I understood what you meant: to have a megatexture with 100 states and depending on the progress status - just change the UV coordinate of the frame that is relevant for the state? Also a good approach for complex art elements that cannot be redrawn programmatically.
Kuper
17
Years of Service
User Offline
Joined: 25th Feb 2008
Playing: Planescape:Torment
Posted: 16th Dec 2024 09:17 Edited at: 16th Dec 2024 09:18
I can make advice: use no texture/sprite sequences at all. Instead use only shader for drawing and animating health circle.
Reasons for that:
1)Its cheap for rendering speed
2)No memory consumption
3)You dont need to create sprites sequence
4)You dont need to re-create all sequence if want to change something
5)All parameters like size, speed, color, effects can be changed in realtime, anytime you want
5)Such kind of shader has not complicated structure, ( not like shadows/lighting shader ), so once you will learn how to do that kind of, you will be able to create any kind of HUD. In fact it is just plane in front of camera with a tiny shader on it.
You can use shader editors like DarkShader to see result instantly ( not save-re launch your .exe for checking results )
Virtual Nomad
Moderator
19
Years of Service
User Offline
Joined: 14th Dec 2005
Location: SF Bay Area, USA
Posted: 18th Dec 2024 03:24 Edited at: 18th Dec 2024 03:29
i don't remember if DBPro has a notion of the SetSpriteScissor() command found in AppGameKit where you can restrict visibility of a sprite to a specified region of the screen but i used a (filled) semicircle sprite, scissored & rotated it for this (low-res) countdown timer and can see similar technique helpful here if it does:
Mage
Valued Member
18
Years of Service
User Offline
Joined: 3rd Feb 2007
Location:
Posted: 18th Dec 2024 10:52
In a limited manner. You can play with sprite U/V data but as I demonstrated in another thread, any time an image is loaded anywhere in the program all existing sprite u/v data will be reset to defaults. It's a bug. It makes serious work with sprites in DBP almost non-viable. You'd have to wrapper all of your sprite and image creation and deletion so you can ensure you put everything back after each image load. You could also play with the underlying image used by a sprite, using memblocks (like adding in a colorkey to hide sections) but it would add a lot of overhead processing.

Mage's Modular Code - Get awesome UI controls and powerful Bitmap Fonts!
Screen Device Recovery - Stop your apps from crashing when minimized/tabbed/screen locked.
CPU Friendly Frame Limiter - Make your app generate less computer heat + noise, and use less battery life!
TinTin
18
Years of Service
User Offline
Joined: 16th May 2006
Location: BORG Drone Ship - Being Assimilated near Roda Beta (28)
Posted: 26th Dec 2024 17:07
Hi
Apologies for being late to the party, but I vaguely remember a few decades ago trying the same thing I eventually ended up just using the SIN, COS commands to draw lines it's a bit math intensive but I'm sure you could figure it out and even adjust the colour of each line as it increases in value, degree = 360.0 * (percent / 100.0) and rgb = 255.0 * (percent / 100.0) , x = radius * sin(degree), y = cos(degree)

best of luck on your progress
Mage
Valued Member
18
Years of Service
User Offline
Joined: 3rd Feb 2007
Location:
Posted: 28th Dec 2024 11:36
If you draw lines or shapes, I highly recommend you use the Advanced2D plugin. The built in 2D draw commands are very slow. In a little demo they will appear to be okay. In a game you'll see a massive fps drop. Even if it's just a pixel.

Mage's Modular Code - Get awesome UI controls and powerful Bitmap Fonts!
Screen Device Recovery - Stop your apps from crashing when minimized/tabbed/screen locked.
CPU Friendly Frame Limiter - Make your app generate less computer heat + noise, and use less battery life!

Login to post a reply

Server time is: 2025-05-15 04:04:51
Your offset time is: 2025-05-15 04:04:51