Shader blends misteriously

Holzchopf

Well-known member
3rd Party Module Dev
Tutorial Author
Joined
Jul 31, 2017
Messages
498
I want to write a shader in mojo2 that draws a fragment when it's alpha value is above a certain threshold. In BlitzMax, I've done something similar once with the offset coming from the vertex alpha... Anyhow, I've come to a point where I have to admit that I have a frustrating problem:

When I overwrite the fragment's alpha in b3d_FragColor it seems to blend... anything but alphablending. When I have a pixel that is rgba(1.0, 1.0, 1.0, 0.1) and try to render it with alpha overwritten to 1.0, it becomes rgba(0.1, 0.1, 0.1, 1.0). Just as if the fragment color was mixed with black. I can hardly understand what's going on, but have a look at the minimalist example I've come up with - it demonstrates the effect.

Code:
Strict

Import mojo2


Function Main:Int()
    New MyApp
    Return 0
End

Class MyApp Extends App
    Field canvas:Canvas
    Field imgBackground:Image
    Field imgSprite:Image
  
    Field mx:Int, my:Int
  
    Method OnCreate:Int()
        canvas = New Canvas
  
        Local shader := New ClipShader
  
        imgBackground = Image.Load("background.jpg", 0, 0)
        imgSprite = Image.Load("sprite.png", , , , shader)
      
        SetUpdateRate(30)
      
        Return 0
    End
  
    Method OnUpdate:Int()
        mx = MouseX()
        my = MouseY()
        Return 0
    End
  
    Method OnRender:Int()
        canvas.Clear()

        canvas.SetBlendMode(BlendMode.Alpha)
        canvas.DrawImage(imgBackground, 0, 0)
        canvas.DrawImage(imgSprite, mx, my)
      
        canvas.Flush()
      
        Return 0
    End
End

' clip shader
Class ClipShader Extends Shader
    Method New()
        Build( LoadString( "cerberus://data/clipshader.glsl" ) )
    End
  
    ' init shader with default (empty) material
    Method OnInitMaterial:Void(pMaterial:Material)
        pMaterial.SetTexture("ColorTexture", Texture.White())
    End
End

Code:
// clipshader.glsl

uniform sampler2D ColorTexture;

void shader(){
    // source color
    vec4 frag = texture2D( ColorTexture, b3d_Texcoord0 ).rgba;
  
    // write output
    //b3d_FragColor = vec4( frag.rgb, frag.a );  // does what it should
    //b3d_FragColor = vec4( frag.rgb, 1.0 ); // half transparent pixel become black?
    b3d_FragColor = vec4( frag.rgb, 0.5 ); // blends partly additive?!
}

Can anyone pleas help? Or has anyone a functioning clipping shader for mojo2?
 

Attachments

  • background.jpg
    background.jpg
    62.4 KB · Views: 287
  • sprite.png
    sprite.png
    2.1 KB · Views: 285

Holzchopf

Well-known member
3rd Party Module Dev
Tutorial Author
Joined
Jul 31, 2017
Messages
498
Ok it took me the whole afternoon but I found the cause:

For some reason the default in HTML5 is to pre-multiply alpha; multiplying all colour components with the alpha value (that's why white with alpha ends up grey). This allows to "alphablend" by half-lightblending, meaning instead of
Code:
glBlendFunc GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA
you can have
Code:
glBlendFunc GL_ONE,GL_ONE_MINUS_SRC_ALPHA
which (supposedly) is faster. And I guess since mojo2 should behave equal on all targets, the same behaviour (pre-multiplying) was implemented on GLFW. I haven't tested how it's for Android...

I can undo the pre-multiplying in my shader by dividing all colour components with the alpha value:
Code:
b3d_FragColor = vec4( frag.rgb / frag.a, 1.0 );

To get the blending right, I have to change mojo2 DrawList.Render() so it uses
Code:
glBlendFunc GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA
when BlendMode.Alpha is set.

With both of those measures I got it working the way I wanted.
 
Top Bottom