fix(webgl): Fix alpha-blending with proper blend functions

Alpha-blended shapes were rendering nearly invisible due to incorrect
blend function configuration in two places:

1. FBO rendering (webgl_gal.cpp): Changed to glBlendFuncSeparate to handle
   RGB and alpha channels independently. Alpha channel now accumulates
   coverage correctly (prevents it from staying near 0.0 when rendering
   with alpha=0.5).

2. Compositor (webgl_compositor.cpp): Changed from premultiplied alpha
   blend (GL_ONE, GL_ONE_MINUS_SRC_ALPHA) to straight alpha blend
   (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) since our FBOs use straight alpha.

Also marked Transform() as dead code with explanation - never called in
KiCad and has no effect even in native OPENGL_GAL.

Result: Alpha-blending scenario now renders correctly with proper color
mixing for overlapping shapes.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Viktor Vaczi 2026-01-09 14:47:20 +01:00
commit 74faa7e249
2 changed files with 18 additions and 20 deletions

View file

@ -395,7 +395,9 @@ void WEBGL_COMPOSITOR::DrawBuffer( unsigned int aSourceHandle, unsigned int aDes
// Depth test has to be disabled to make transparency working // Depth test has to be disabled to make transparency working
glDisable( GL_DEPTH_TEST ); glDisable( GL_DEPTH_TEST );
glBlendFunc( GL_ONE, GL_ONE_MINUS_SRC_ALPHA ); // Use standard alpha blending for straight (non-premultiplied) alpha
// Note: GL_ONE would be for premultiplied alpha, but our FBOs use straight alpha
glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
// Bind the source texture // Bind the source texture
glActiveTexture( GL_TEXTURE0 ); glActiveTexture( GL_TEXTURE0 );

View file

@ -697,7 +697,12 @@ void WEBGL_GAL::BeginDrawing()
// Setup blending, required for transparent objects // Setup blending, required for transparent objects
glEnable( GL_BLEND ); glEnable( GL_BLEND );
glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ); // Use separate blend functions for RGB and alpha channels
// RGB: standard alpha blending (src.rgb * src.a + dst.rgb * (1 - src.a))
// Alpha: accumulate coverage (src.a + dst.a * (1 - src.a))
// This prevents framebuffer alpha from becoming too low when rendering to transparent FBOs
glBlendFuncSeparate( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA,
GL_ONE, GL_ONE_MINUS_SRC_ALPHA );
// Set up the world <-> screen transformation (replacing legacy glMatrixMode/glLoadMatrixd) // Set up the world <-> screen transformation (replacing legacy glMatrixMode/glLoadMatrixd)
ComputeWorldScreenMatrix(); ComputeWorldScreenMatrix();
@ -1656,7 +1661,9 @@ void WEBGL_GAL::DrawBitmap( const BITMAP_BASE& aBitmap, double alphaBlend )
// Setup for drawing // Setup for drawing
glDepthFunc( GL_ALWAYS ); glDepthFunc( GL_ALWAYS );
glEnable( GL_BLEND ); glEnable( GL_BLEND );
glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ); // Use separate blend functions for RGB and alpha (same as main initialization)
glBlendFuncSeparate( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA,
GL_ONE, GL_ONE_MINUS_SRC_ALPHA );
// Use the vertex manager to draw a textured quad (same pattern as DrawGlyph) // Use the vertex manager to draw a textured quad (same pattern as DrawGlyph)
// The shader uses texture coordinates from SHADER_FONT parameters // The shader uses texture coordinates from SHADER_FONT parameters
@ -2061,23 +2068,12 @@ void WEBGL_GAL::ClearScreen()
void WEBGL_GAL::Transform( const MATRIX3x3D& aTransformation ) void WEBGL_GAL::Transform( const MATRIX3x3D& aTransformation )
{ {
// Convert 3x3 matrix to 4x4 matrix (column-major order for GLM) // NOTE: Transform() is dead code in KiCad - never actually called anywhere.
// The 3x3 matrix represents a 2D affine transformation // In native OPENGL_GAL, it modifies GL_MODELVIEW via glMultMatrixd(), but
glm::mat4 matrix( 1.0f ); // VERTEX_MANAGER uses its own independent m_transform, so the glMultMatrixd
// call has no visible effect on rendered output.
matrix[0][0] = static_cast<float>( aTransformation.m_data[0][0] ); // We intentionally do nothing here to match that native behavior.
matrix[0][1] = static_cast<float>( aTransformation.m_data[1][0] ); (void) aTransformation; // Suppress unused parameter warning
matrix[0][2] = static_cast<float>( aTransformation.m_data[2][0] );
matrix[1][0] = static_cast<float>( aTransformation.m_data[0][1] );
matrix[1][1] = static_cast<float>( aTransformation.m_data[1][1] );
matrix[1][2] = static_cast<float>( aTransformation.m_data[2][1] );
matrix[3][0] = static_cast<float>( aTransformation.m_data[0][2] );
matrix[3][1] = static_cast<float>( aTransformation.m_data[1][2] );
matrix[3][2] = static_cast<float>( aTransformation.m_data[2][2] );
m_currentManager->MultiplyMatrix( matrix );
} }