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:
// Create image for HUD
global imgHud as integer
imgHud = find free image()
// If shader can work with transparent png
load image "hud_1.png", imgHud, 1
// If shader can work only with bmp, uncomment this
//load image "hud_2.bmp", imgHud, 1
// Create shader for HUD
global fxHud as integer
fxHud = find free effect()
// Loading shader
load effect "hud_coil_shader.fx", fxHud, 0
// Hero health points
global HP_MAX as integer = 150
local HP as integer
HP = HP_MAX
// Max damage points
local DM as integer = 10
// Angles and Radii for erasing in shader (global)
global angleStart as float = 0
global angleEnd as float = 0
// Coil parameters
local rIn as float = 80.0 // Inner radius (+/- 1px)
local rOut as float = 100.0 // Outer radius (+/- 1px)
// Normalize inner radius for shader [0..1]
global radiusInner as float
radiusInner = rIn / image width(imgHud)
// Normalize outer radius for shader [0..1]
global radiusOuter as float
radiusOuter = rOut / image height(imgHud) // Square image, width = height
// Create sprite and use texture (just to see input image)
local sprHud as integer
sprHud = find free sprite()
sprite sprHud, 200, 200, imgHud
// Create object plane for using effects
global objHud as integer
objHud = find free object()
make object plane objHud, 100, 100, 1
position object objHud, 0, 0, 2890 // Change Z-coordinate according your camera settings
set object transparency objHud, 5
// Link effect to object
set object effect objHud, fxHud
texture object objHud, imgHud
// Control health value
set window title "Health = " + str$(HP)
repeat
if inkey$() = " "
HP = hudHealthUpdate(HP, rnd(DM)+1)
endif
if inkey$() = chr$(13)
HP = HP_MAX
endif
set cursor 0,0
print "Health = ", str$(HP)
print "angleStart = ", angleStart
print "angleEnd = ", angleEnd
print "radiusInner = ", radiusInner
print "radiusOuter = ", radiusOuter
sync
UNTIL scancode() <> 207
next
end
// Modifying Health and re-activate shader
function hudHealthUpdate(HP as integer, DM as integer)
local newHP as integer = 0
if HP <= 0 then exitfunction newHP
// Decrease Health by Damage
newHP = HP - DM
if newHP < 0 then newHP = 0
// Shader wants the start angle to be less than the end angle
angleStart = 360.0 * (newHP / (1.0 * HP_MAX))
angleEnd = 360.0 * (HP / (1.0 * HP_MAX))
set effect constant float fxHud, "angleStart", angleStart
set effect constant float fxHud, "angleEnd", angleEnd
set effect constant float fxHud, "radiusInner", radiusInner
set effect constant float fxHud, "radiusOuter", radiusOuter
// Do we need update texture before apply shader?
texture object objHud, imgHud
// Should the shader be activated once when the hero takes damage or every time before synchronization?
set effect technique fxHud, "HudEffect"
endfunction newHP
SHADER (which is not work for me, even if just return source color, without any mathematics)
// VORTEX SHADER ---------------------
float4x4 WorldViewProjection : WORLDVIEWPROJECTION;
struct VS_INPUT
{
float4 Position : POSITION;
};
struct VS_OUTPUT
{
float4 Position : POSITION;
float2 TextureCoordinates : TEXCOORD0;
};
VS_OUTPUT vs_main(VS_INPUT input)
{
VS_OUTPUT output;
output.Position = mul(input.Position, WorldViewProjection);
output.TextureCoordinates = input.Position.xy + float2(0.5, 0.5); // Transform coordinates from the range [-1, 1] to the range [0, 1]
return output;
}
// PIXEL SHADER ---------------------
sampler2D TextureSampler : register(s0);
float radiusInner;
float radiusOuter;
float angleStart;
float angleEnd;
float4 ps_main(float2 texCoord : TEXCOORD0) : COLOR
{
// Convert texture coordinates to polar coordinates
float2 center = float2(0.5, 0.5); // Texture center
float2 direction = texCoord - center;
float distance = length(direction);
float angle = atan2(direction.y, direction.x) / 3.14159265358979323846 + 1.0; // Normalized angle in range [0, 1]
float normalizedAngleStart = angleStart / 3.14159265358979323846 + 1.0; // Normalized angle of start
float normalizedAngleEnd = angleEnd / 3.14159265358979323846 + 1.0; // Normalized angle of end
// Checking if a point is in a ring sector
float insideRing = step(radiusInner, distance) * step(distance, radiusOuter);
float insideAngle = step(normalizedAngleStart, angle) * step(angle, normalizedAngleEnd);
// Smoothing edges
float alpha = smoothstep(radiusInner, radiusInner + 0.001, distance) * smoothstep(radiusOuter - 0.001, radiusOuter, distance) *
smoothstep(normalizedAngleStart, normalizedAngleStart + 0.0005, angle) * smoothstep(normalizedAngleEnd - 0.0005, normalizedAngleEnd, angle);
// Applying alpha channel
float4 color = tex2D(TextureSampler, texCoord);
// Change color alpha if pixel inside sector
if (insideRing && insideAngle) {
color.a *= alpha;
}
return color;
}
// TECHNIQUE ---------------------
technique HudEffect
{
pass P0
{
VertexShader = compile vs_2_0 vs_main();
PixelShader = compile ps_2_0 ps_main();
}
}