Order-independent transparency + custom shader + needAlphaBlending = broken shader

Hi All,
First of all: Congratulations for the new 7.0 release! It looks awesome! Can’t wait to play with Gaussian Splatting.

I’m back with a question / bug report about the combination of

  • order-independent transparency
  • using a custom shader
  • which uses needAlphaBlending.

This breaks the shader (GL_INVALID_OPERATION: Active draw buffers with missing fragment shader outputs.) I looked into the OIT fragment shader files and was able to fix the GL_INVALID_OPERATION error by setting the missing outputs (depth, frontColor, backColor), but since I don’t fully understand the details if this OIT implementation, I could not get it to render correctly.

Here’s a PG to play with.

In the top two lines you can turn OIT and needAlphaBlending on and off. Turning on both breaks the shader.

Possible workaround: Maybe needAlphaBlending is not necessary as I want to set a constant alpha value for the entire plane and not per fragment.

Thanks a lot in advance for looking into this issue!
Tom

@sebavan or @Evgeni_Popov maybe can help?

1 Like

It’s a bit complicated because you will have to implement the necessary code in your custom shader to support OIT. You should take example on the standard material and look for the ORDER_INDEPENDENT_TRANSPARENCY define. You will also have to add #include<oitDeclaration> and #include<oitFragment> in your shader. Again, look how it is done in the default material (shaders/default.vertex.fx and shaders/default.fragment.fx).

1 Like

Thanks for the pointers, @Evgeni_Popov. I’ll give it a try in the next few days and report back.

Should or must I use the precompiler directives (e.g. #ifdef ORDER_INDEPENDENT_TRANSPARENCY) in my custom shader?

Yes, except if you’re sure you will never use your shader without OIT enabled.

Hi 7om. I have the exact same configuration. Were you able to solve it? And if so, could you please post the shadercode? Thank you!!

my progress so far: https://playground.babylonjs.com/#FHIIKI#8

Hi @8thdwarf,
yes, I was able to make it work by using a CustomMaterial. Here’s a code snippet:

const mat = new BABYLON.CustomMaterial("custom", scene)

mat.AddAttribute("uv")
mat.Vertex_Definitions(`in vec2 uv; varying vec2 vUv;`)
mat.Vertex_MainEnd(`vUv = uv;`)

mat.AddUniform("slice", "sampler2D")
mat.AddUniform("windowing", "vec2")
mat.AddUniform("tint", "vec3")
mat.Fragment_Definitions(`varying vec2 vUv;`)
mat.Fragment_Before_FragColor(`
     float v = saturate(windowing.y * (texture(slice, vUv).r - windowing.x));
     color.rgb = tint.rgb * v;
`)

mat.onBindObservable.add(mesh => {
    const m = mesh.material.getEffect()

    m.setTexture("slice", this.texture)
    m.setVector3("tint", V(...(TYPE_COLOR.get(this.type) ?? TYPE_COLOR.get("unknown"))))
    m.setVector2("windowing", V(
        (this.window_center - 0.5 * this.window_width) / MAX_SLICE_VALUE,
        MAX_SLICE_VALUE / this.window_width
    ))
})

I create a new CustomMaterial, and then I inject some stuff into the default shader.

Check out the vertex code and search for “CUSTOM_VERTEX_”. Then you will find all possible insertion points.
The same applies to fragment shader, but search for (you guessed it) “CUSTOM_FRAGMENT_” instead.

After that I add an observer to the material’s onBindObservable which sets the according bindings right before rendering the object.

Dear Babylon developers, please chime in and let me know if anything can be improved or clarified.

Thanks so much for helping out. For certain reasons, I have to stick to the shaderMaterial.
Please have a look at https://playground.babylonjs.com/#FHIIKI#9.
My goal is to create a simple shadermaterial WITH OIT support.
What I’m missing here is how to setup oitDepthSampler oitFrontColorSampler.
@Evgeni_Popov - can you help? This might be interesting for many users.

You can reuse the existing OIT shader code by using the same #includes than the ones used in the PBR / standard materials. Also, you have to declare the oitDepthSampler and oitFrontColorSampler samplers when you create your material (line 72). To bind the samplers, simply call scene.depthPeelingRenderer.bind(shaderMaterial.getEffect()) (line 80):

2 Likes

This is brilliant, thanks so much!