Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Custom blending states reset between rendered frames #9564

Closed
3 of 12 tasks
Rantanen opened this issue Aug 23, 2016 · 3 comments · Fixed by #9839
Closed
3 of 12 tasks

Custom blending states reset between rendered frames #9564

Rantanen opened this issue Aug 23, 2016 · 3 comments · Fixed by #9839

Comments

@Rantanen
Copy link

Rantanen commented Aug 23, 2016

Description of the problem

I've got a ShaderMaterial with CustomBlending. Drawing this on screen on the first frame works as expected. However on subsequent frames the blending resets back to something similar to NormalBlending.

The issue seems to be in setBlending(...) of WebGLState.js.

Looking at the source code of the function it seems like it won't handle CustomBlending-NoBlending-CustomBlending sequence correctly.

Below is a crude outline of the function with the faulty logic highlighted in the comments:

this.setBlending = function ( blending, blendEquation, blendSrc, blendDst, blendEquationAlpha, blendSrcAlpha, blendDstAlpha, premultipliedAlpha ) {

    // First check whether blending is enabled or not.
    if ( blending !== THREE.NoBlending ) {
        this.enable( gl.BLEND );
    } else {
        this.disable( gl.BLEND );

        // Resets currentBlending AND RETURNS.
        currentBlending = blending;
        return;
    }

    // Since we didn't RETURN yet, blending is something else than NoBlending.
    // Handle the predefined blendings first.
    if ( blending !== currentBlending || premultipliedAlpha !== currentPremultipledAlpha ) {

        if ( blending === THREE.AdditiveBlending ) {
            // ...
        } else if ( blending === THREE.SubtractiveBlending ) {
            // ...
        } else if ( blending === THREE.MultiplyBlending ) {
            // ...
        } else {

            // This else-branch is executed for NormalBlending as probably planned.
            // BUT ALSO for CustomBlending.
            gl.blendEquationSeparate( gl.FUNC_ADD, gl.FUNC_ADD );
            gl.blendFuncSeparate( gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA );

            // The equation, equationAlpha, blendSrc, blendDst, etc. have practically
            // been reset, BUT the "currentXXX", variables that track these states
            // still contain the old values.

            // In case blending === NormalBlending, these variables will be reset
            // in the else-branch at the bottom of the function - however for CustomBlending
            // that else-branch is not executed.
        }

        currentBlending = blending;
        currentPremultipledAlpha = premultipliedAlpha;
    }

    // Now check for custom blending options.
    if ( blending === THREE.CustomBlending ) {

        // If the only state between the current CustomBlending state and the
        // previous CustomBlendingState was NoBlending, then the currentBlendEquation,
        // currentBlendEquationAlpha and all the other custom currentXXX variables
        // contain the old values from the previous CustomBlending state.
        //
        // This despite the fact that the (supposed) NormalBlending-branch above
        // has been executed, which has reset the gl.blendEquation and gl.blendFunc
        // states.
        if ( blendEquation !== currentBlendEquation || blendEquationAlpha !== currentBlendEquationAlpha ) {
            // ... change state
        }

        if ( blendSrc !== currentBlendSrc || blendDst !== currentBlendDst || blendSrcAlpha !== currentBlendSrcAlpha || blendDstAlpha !== currentBlendDstAlpha ) {
            // ... change state
        }

    } else {

        // This else-branch is never executed when setting NoBlending.
        currentBlendEquation = null;
        currentBlendSrc = null;
        currentBlendDst = null;
        currentBlendEquationAlpha = null;
        currentBlendSrcAlpha = null;
        currentBlendDstAlpha = null;
    }
};

I was unable to get the master branch working in my environment most likely due to #9489. (JSPM imports three.js as undefined with master branch).

However I did check the source code and the only changes that affect the above function seem to be related to the module refactoring so I'm fairly confident this issue affects r80 as well.

I switched my code to AdditiveBlending as a workaround which works just fine for my use case.

Three.js version
  • Dev
  • r79
  • ...
Browser
  • All of them
  • Chrome
  • Firefox
  • Internet Explorer
OS
  • All of them
  • Windows
  • Linux
  • Android
  • IOS
Hardware Requirements (graphics card, VR Device, ...)
@mrdoob
Copy link
Owner

mrdoob commented Aug 23, 2016

Do you mind creating a jsfiddle we can work with?

@Rantanen
Copy link
Author

Rantanen commented Aug 24, 2016

@fallenoak
Copy link
Contributor

fallenoak commented Oct 10, 2016

Have just bumped into this problem myself.

I've taken a stab at a fix by adding the state reset logic to the conditional at the start of the setBlending function.

if ( blending !== NoBlending ) {

    enable( gl.BLEND );

} else {

    disable( gl.BLEND );

    currentBlending = blending; // no blending, that is
    currentBlendEquation = null;
    currentBlendSrc = null;
    currentBlendDst = null;
    currentBlendEquationAlpha = null;
    currentBlendSrcAlpha = null;
    currentBlendDstAlpha = null;

    return;

}

This has resolved the issue for me, although I'm unclear if this may have had unintended side effects (haven't come across any, knock on wood).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants