How to multiply `material.opacityNode`, not replace it?

I’m trying to multiply a custom opacity with the built-in opacity.

With WebGLRenderer, it could be done with shader patching like this:

threeMat.onBeforeCompile = (params, renderer) => {
	params.fragmentShader = params.fragmentShader.replace(
		/*glsl*/ `vec4 diffuseColor = vec4( diffuse, opacity );`,
		/*glsl*/ `vec4 diffuseColor = vec4( diffuse, opacity );
			float alphaFactor = /*...custom multiplier...*/ 0.1234; // using a constant for sake of example

			diffuseColor.a *= alphaFactor;
		`,
	)
}

With WebGPURenderer, I’ve no luck achieving the same yet, although I bet I’m missing something simple. Here’s the TSL code, but it has no effect on opacity:

material.opacityNode = Fn(() => {
    const alphaFactor = /*...custom multiplier...*/ Const(0.1234); // using a constant for sake of example

    return diffuseColor.a.mul(alphaFactor) // this is not working
})()

Here’s a live JSFiddle example using TSL. In the example, the end result is not multiplied:

    const material = new THREE.MeshPhongNodeMaterial( {
        map,
        transparent: true,
        opacity: 0.000001,
    } );

    material.opacityNode = Fn(() => {
        const alphaFactor = Const(0.5)

        return diffuseColor.a.mul(alphaFactor) // not working, opacity shows up as 0.5, not 0.5 * 0.000001
    })()

I’m expecting the same result as in this fiddle where I explicitly set the opacity value to the expected result of 0.000001 * 0.5:

    const material = new THREE.MeshPhongNodeMaterial( {
        map,
        transparent: true,
        opacity: 0.000001 * 0.5, // this works (but it is not dynamic TSL)
    } );

    // material.opacityNode = Fn(() => {
    //     const alphaFactor = Const(0.5)
    //     return diffuseColor.a.mul(alphaFactor) // not working, opacity shows up as 0.5, not 0.5 * 0.000001
    // })()

The opacity property of the material is materialOpacity:

import {..., materialOpacity} from 'three/tsl';
:
material.opacityNode = Fn(() => {
    const alphaFactor = Const(0.5)
    return materialOpacity.mul(alphaFactor);
})()
4 Likes

Hey @PavelBoytchev, I can’t seem to get that working. Here’s a new jsfiddle:

    material.opacityNode = Fn(() => {
        const alphaFactor = float(0.2)
        materialOpacity.mul(alphaFactor)
        return materialOpacity
    })()

EDIT: ah, so this doesn’t work either:

    material.opacityNode = Fn(() => {
        const alphaFactor = float(0.2)
        // this causes a runtime error
        materialOpacity.assign(materialOpacity.mul(alphaFactor))
        return materialOpacity
    })()

But this works!

    material.opacityNode = Fn(() => {
        const alphaFactor = float(0.2)
        const newOpacity = Const(materialOpacity.mul(alphaFactor))
        return newOpacity
    })()

EDIT: Ah, the Const is not needed there:

    material.opacityNode = Fn(() => {
        const alphaFactor = float(0.2)
        const newOpacity = materialOpacity.mul(alphaFactor)
        return newOpacity
    })()
1 Like

One step forward and you will get exactly what I have proposed:

return materialOpacity.mul(alphaFactor)
4 Likes

Thank you! Now I’m stuck in this one, where I believe the opacity handling is correct based on the above, but perhaps I’m not able to port vMapUv correctly:

@muzammil12345 yeah true, but the above is a simple example. I need to port the code in this thread,

that uses vMapUv with WebGLRenderer to TSL with WebGPURenderer to run logic per fragment/pixel.