[SOLVED] Custom shader instance on multiple materials to set different uniforms

I started to study custom shaders. I wanted to re-use my custom shader instance on different materials in order to vary the uniforms on the different materials, as I think that will be the most efficient. I managed to make a script that does this but the way I keep a reference to the shader in a property of the global script class var seems not ideal. Is there a better way than that?

Updated project with suggested structure from Leonidas:
https://playcanvas.com/project/920189/overview/shaderquestions
https://playcanvas.com/editor/scene/1403602

Hi @Kulodo133,

It’s good that you are thinking in a more object oriented way.

Here is how I reworked your project. I created a global shader-prepare.js script on root, that’s responsible for generating the shader:

var ShaderPrepare = pc.createScript('shaderPrepare');

ShaderPrepare.attributes.add('materialAsset', {
    type: 'asset',
    assetType: 'material'
});

// initialize code called once per entity
ShaderPrepare.prototype.initialize = function () {

    const fshader = this.app.assets.find('shader-1.frag').resource;
    const vshader = this.app.assets.find('shader-1.vert').resource;
    const shaderDefinition = {
        attributes: {
            vertex_position: pc.SEMANTIC_POSITION,
            vertex_normal: pc.SEMANTIC_NORMAL,
            aUv0: pc.SEMANTIC_TEXCOORD0
        },
        vshader: vshader,
        fshader: fshader
    };
    const shader = new pc.Shader(this.app.graphicsDevice, shaderDefinition);

    this.material = new pc.Material();
    this.material.shader = shader;

    // --- events
    this.app.on('ShaderPrepare:applyMaterial', function(meshInstance){
        meshInstance.material = this.material;
    }, this);
};

Then on each object I communicate in your shader-1.js script using an event with that master script, to get that single material applied.

Instead of adding the parameter on the material, I apply it to each mesh instance:

var Shader1 = pc.createScript('shader1');

Shader1.attributes.add('blue', {
    title: 'Blueness',    
    type: 'number',
    default: 0.0,
    description: 'Blueness'
});

Shader1.prototype.initialize = function() {

    const meshInstance = this.entity.render.meshInstances[0];
    this.entity.render.renderStyle = pc.RENDERSTYLE_WIREFRAME;

    this.app.fire('ShaderPrepare:applyMaterial', meshInstance);

    // set uniforms
    meshInstance.setParameter('uBlue', this.blue);
    this.on('attr:blue', function (value, prev) {
        meshInstance.setParameter('uBlue', this.blue);
    });
};

Here is the updated project:
https://playcanvas.com/editor/scene/1403683

2 Likes

Ah , I didn’t know that the setParameter could be set on the meshInstance, the examples I’d seen had set it on the material. That’s great so I don’t need to multiple materials. And good idea to keep the shader creation separate - that was exactly the kind of help I was after. Thanks that is a great help :slight_smile:

1 Like

I added some shaders based on these unity tutorials https://youtu.be/kfM-yu0iQBk?t=11562

https://playcanvas.com/project/920189/overview/shaderquestions

2 Likes

Looks great, especially the water ripple! Thanks for sharing!

1 Like

can you send me the script for those neon shaders? it would help with a new visual for trigo run!

The code is in the linked project.

figured it out :), may i ask if you know how to make a 2d lava shader for my game trigo run?

I recommend finding a visual example of how you want that to look first. For example search for lava on shader toy might give you some ideas. Better to start a new thread for this.

1 Like