Shader Chunk Update

I was trying to update shader chunk on the script.
As seen the below code, I’ve changed the shader chunk with a custom shader and also called app-wide shader update.

I referenced the application level shader update from Updating/recompiling shaders from chunks on runtime

However, this seemed not working.

    let fs = this.shader.resource;
    for (let i = 0; i < this.materials.length; i++) {
        var material = this.materials[i].resource;
        
        material.chunks.lightSpecularBlinnPS = fs;
        material.update();
    }

    const library = this.app.graphicsDevice.getProgramLibrary();
    let i, len;
    for (i = 0, len = this.app.graphicsDevice.shaders.length; i < len; i++) {
        const shader = this.app.graphicsDevice.shaders[i];
        library.removeFromCache(shader);
    }
    
    this.app.scene.updateShaders = true;

Is there anything I missed?

Hi @sooyong_Kim,

That was a hacky way targeting an older PlayCanvas engine build, not sure if that still works and/or is recommended.

@Elliott @mvaligursky what’s the proposed way in doing that right now?

Are you trying to update a shader chunk globally in the engine/app?

That’s easy if you do it before the initial material compiles, by overriding the global chunks:

pc.shaderChunks.diffusePS = `// etc`;

But recompiling already compiled materials to pickup the new chunk app wide, I’m not sure how.

Yeah, last time I had to do this, I made a script asset that is set the Loading Type to ‘After the Engine’ file and overrode the chunk in that so it is effectively a patch

1 Like

So, it should be done before PlayCanvas asset loading? sounds like I need to replace the shader chunk in loading script?

Basically it needs to be done before any material using that chunk compiles its shader. That will happen as soon as any model using that material will start rendering.

A good place is After Engine as Yaustar shared above. Or another way we do often is add a script in the scene root entity (we don’t put render components usually there).

1 Like

Then, i seem to be not possible to change it at runtime…

Can you post an example project of what you are trying to do?

I’ve tried working with the clearVariants() pc.Material method and also clearShaders() on the referenced mesh instances, but that didn’t trigger any update on the material side.

It seems the logic in the engine has changed and the program library is used differently now?

I mean you could use a dirty old trick and temporary change a material property (e.g. reference a channel that you don’t use normally, e.g. add an emissive map). Just for a frame call update, afterwards remove it and call again.

Maybe @mvaligursky has an idea on how to properly do this (force recompile a material shader)?

After I attached the script to the root entity, I called the shader chunk update function initially.
As @Leonidas says, the shader of the material is updated.

Interesting part is once the shader chunk exist on the material, I can update it in realtime.
@yaustar Is this expected behavior??

There is no expected behaviour here, as none of this is a public API. We could find some way to do this, but could you please provide the repro project with what you are trying to do? The implementation of this code in the engine is pretty complex, and I’m not sure we can provide suggestions without testing those to see what might work.

2 Likes

https://playcanvas.com/editor/scene/1692776

This is my test project.

pc.app.fire(“SPECULAR_UPDATE”, “NO_SPECULAR”)
pc.app.fire(“SPECULAR_UPDATE”, “ORIGINAL_SPECULAR”)

I’ve attached it to the root entity and make the script loaded at the first.

I’m not sure what the problem to look at here? The events seem to be working fine with the material.