Could need some advice on adapting the SHADER BURN example

Or should it be more like this:

const renders = ship.findComponents("render");
for (let i = 0; i < renders.length; ++i) {
	const meshInstances = renders[i].meshInstances;
	for (let j = 0; j < meshInstances.length; j++) {
		const material = meshInstances[j].material;
		if (material) {
			material.chunks.APIVersion = pc.CHUNKAPI_1_56;
			material.chunks.opacityPS =
				assets.opacityShader.resource;
			material.setParameter(
				"uHeightMap",
				assets.clouds.resource
			);
			material.update();
		}
	}
}

Because my model has several submeshes and materials.

The last version of the code gives a shader compile error (which is a good sign that it tries to compile the correct shader :slight_smile: )

================================================================
Failed to compile fragment shader:

ERROR: 0:226: ‘vUv0’ : undeclared identifier
ERROR: 0:226: ‘texture’ : no matching overloaded function found
ERROR: 0:226: ‘r’ : field selection requires structure, vector, or interface block on left hand side

221:
222: void getOpacity() {
223: dAlpha = 1.0;
224:
225: float t = uTime;
226: float height = texture2D(uHeightMap, vUv0).r;
227: if (height < t) {
228: dAlpha = 0.0;
229: }
230: dAlpha *= material_opacity;
231: }

===============================================================

I copied the shader code from the example.

As a test I changed the shader to this code (I copied this from the opacity shader in the shader lib on Github mentioned above by @yaustar):

#ifdef MAPFLOAT
uniform float material_opacity;
#endif
void getOpacity() {
    dAlpha = 1.0;
    #ifdef MAPFLOAT
    dAlpha *= material_opacity;
    #endif
    #ifdef MAPTEXTURE
    dAlpha *= texture2DBias($SAMPLER, $UV, textureBias).$CH;
    #endif
    #ifdef MAPVERTEX
    dAlpha *= clamp(vVertexColor.$VC, 0.0, 1.0);
    #endif
}

No shader compiler error this time. But when setting dAlpha to 0.4 before returning the function doesn’t make a difference in rendered model. So I doubt if this shader is actually running.

You can capture a frame with JS Spector (I used Chome plugin) and inspect the shaders / uniforms.

Here’s an editor project that uses ‘engine-only’ code to demonstrate the effect: https://playcanvas.com/project/1031602/overview/f-shader-dissolve

The important part is here:

            const materials = asset.resource.materials;
            for (const materialAsset of materials) {
                /** @type {pc.StandardMaterial} */
                const material = materialAsset.resource;
                // Important to include the opacity chunk
                material.blendType = pc.BLEND_NORMAL;
                material.chunks.APIVersion = pc.CHUNKAPI_1_56;
                material.chunks.opacityPS = assets.opacityShader.resource;
                material.setParameter("uHeightMap", assets.clouds.resource);
                material.update();
            }

We have to make sure that the material includes the opacity chunk so that it compiles with our overridden shader code.

Hence changing the blend type from none to ‘normal’ which means the material has to include the opacity chunk.

We have a model with different sub objects. So is the double loop as shown below needed/correct?
(not yet addapted to change the blend type)

You can do it that way or just resource.materials like I did. You are changing the same materials either way.

I prefer to do it via my method as multiple render components and meshinstances can have the same material which means you are looping more than necessary

But what if our model has multiple materials?

Not sure what it changes? In my code, I’m applying the shader to all of them

I think I do not understand your code.
Do you assume I load a material using the asset loader? Because you use this line:

const materials = asset.resource.materials;

My models are loaded using the glTF files, which already include the materials.Like this:

const ship = assets.ship.resource.instantiateRenderEntity();

I do not see how your code combines with this.

No, this is the list of materials in the GLB container. engine/glb-container-resource.js at main · playcanvas/engine · GitHub

It’s not yet public API as it was subject to change when we first implemented this. But it hasn’t really changed since then

const ship = assets.ship.resource.instantiateRenderEntity();
const materials = assets.ship.resource.materials;

Thanks a lot for the example. I think I understand now.

And I can get a custom shader running. I know because when setting dAlpha to 0.5 my complete ship turns half transparent. The complete shader code in that case:

void getOpacity() {
    dAlpha = 0.5;
}

But when using the shader code of your example:

uniform float material_opacity;

#ifndef CUSTOM_GEM_UNIFORMS
#define CUSTOM_GEM_UNIFORMS
uniform sampler2D uHeightMap;
uniform float uTime;
#endif

void getOpacity() {
    dAlpha = 1.0;
    
    float t = uTime; 
    float height = texture2D(uHeightMap, vUv0).r;
    if (height < t) {
        dAlpha = 0.0;
    }
    dAlpha *= material_opacity;
}

Then model is wrong. Parts are missing and when rotating parts are dissapearing or appear depending on rotation.
I started to comment out lines and this is the shader that shows my complete ship correctly:


#ifndef CUSTOM_GEM_UNIFORMS
#define CUSTOM_GEM_UNIFORMS
uniform sampler2D uHeightMap;
uniform float uTime;
#endif

void getOpacity() {
    dAlpha = 1.0;
    
    float t = uTime; 
    //float height = texture2D(uHeightMap, vUv0).r;
    //if (height < t) {
       // dAlpha = 0.0;
    //}
    //dAlpha *= material_opacity;
}

Off course without dissolving. Just a complete ship.
But when adding the line

float height = texture2D(uHeightMap, vUv0).r;

again, I get this missing parts again.

I may know what is wrong:

Some parts of the ship do not have a material yet. I suspect those parts to fail.
I will investigate further…

Well, the model did have materials for all parts, but some materials did not have an associated texture. In that case the new opacity shader results in a compiler error.

So I removed all parts that did not have a texture and now it works.
There probably is a better way to handle this, like testing if there is a texture. But I do not know how and I do not know if it is a particular texture that is needed to prevent the compiler error.

But unfortunately, there is an other problem:

The object doesn’t really get transparent. When I draw something behind it, that part stays invisible.

See this demo: PlayCanvas 3D HTML5 Game Engine

But this is only for lines. Solid objects works perfect. So maybe it has nothing to do with the code but is it something about line drawing and transparancy.

There are multiple issues you are running into here:

  1. As you saw, some meshes don’t have materials and therefore aren’t affected by the shader

  2. There are materials that don’t use a texture and therefore the shader optimisation process to reduce the size of shader, removes the UV0 sampler as if there is no texture, you don’t need UV coordinates. However, we do need it for the shader. So we have to add a blank texture to the material. (See here for updated project in doing this: PlayCanvas 3D HTML5 Game Engine

  3. Rendering transparent meshes over each other. See this thread for more details: [SOLVED] Layers and affect of the 'Order' - #7 by yaustar TLDR is that it’s difficult to render complex transparent meshes in the right order to the camera. When the wrong order happens, you get issues where some meshes can’t be seen.

Regarding 3, I don’t know if there is a way not to render to the depth buffer here if a pixel is fully transparent @mvaligursky / @Leonidas ?

Otherwise you are in the realm of forcing the render with render layers (ie change the render component layers of the meshes inside the ship to render first, then the outside)

I do wonder if it’s possible to do this on the diffuse chunk and discard the pixel instead? That way, you aren’t rendering on the transparent layers :thinking:

if the shader discards the pixel, the pixel does not write to depth.
So discard the pixels with alpha being zero.

1 Like

Thanks Martin!

So after updating the shader to use discard instead of setting the transparency to 0, it looks a lot better.

Before:

After:

There is still an issue with some areas because they are already using semi transparent materials (wind screen etc)

This is likely be due to the render order issue mentioned before. A quick-ish fix to this is to have all the transparent parts of the model on a separate mesh instance/mesh in your modelling package before exporting.

That way on load, you can set the render component for that mesh to be renderer last after the rest of the ship.

1 Like

Thanks a lot you all! This is really great support! It is good to notice that you take the time to explain to people who still have to learn a lot.
And I already learned a lot and understand the dissolve demo completely now :slight_smile:

I’m ready for the next step: I want to adapt the dissolve algorithm. Because I do not like it 100% yet.
I would like to make the ship appear and dissapear not this random but from the middle of the ship to the outside.

So I want to calculate for each pixel the distance to the middle point of the ship and take that value into account for the opacity calculation/decision.

But then there is something new (for me) needed: I would need to know the position of the pixel. Is that somewhere available in some variable? Can I somehow get this value?

Hi Yaustar,
I made a mistake. I was not allowed to put the ship model on public available projects. So I changed some projects to private and deleted the ship on public projects such that the demo works with an other model.
But now I see you are using a copy of the model in a public project. Can you please delete that one?

Deleted the model