Live-reloading of shaderMaterial?

Hello community,

I am currently setting up a dev environment for Babylon.JS using Parcel.js hot module replacement capabilities a bit in the fashion of thlorenz:


Basically, everytime you change the code and hit save, the renderCanvas is diposed and a new canvas is initialized, thereby updating the scene without having to refresh the browser.

This is quite neat but in practice it means that all of the game objects and the camera go back to their initial states. It also means that Parcel has to recompile everything which takes more time than needed, especially when using the ES6 import syntax with the alpha v4 of Babylon as I am.

My question is:
Is there a way to update the shaderMaterial applied on a mesh?
There is a way of being specific about the changes Parcel.js has to do during HMR using:

if (module.hot) {
module.hot.accept(function accept() {
// update shaders here
})
}

Coming from three.js this could be achieved using the function:

material.needsUpdate = true;

I have looked into the documentation about the Effect and ShaderMaterial classes but could not find a way to make it work. Does somebody has an idea of how this could be achieved?

Hello!
what do you mean by update the ShaderMaterial? What do you want to update? (If I understand correctly, the ShaderMaterial will be rebuild anyway by your HMR right?)

Hi Delta.

As it is now, the entire game is being rebuilt everytime I hit save, so yes, the ShaderMaterial gets updated in the process along with everything else which makes the build time long without need + it resets the objects and camera positions which is not the best development experience.

What I’m really trying to achieve is to edit my vertex and fragment shaders in my editor and see the changes appear in the browser instantly without affecting anything else but the ShaderMaterial I’m editing.

At of now, Parcel detects when I am making a change to the shader files without issues, the problem is that I don’t know how to tell BabylonJS to recompute the ShaderMaterial with the new values.

Here is my code, notice the module.hot.accept() function at the end:

import { ShaderMaterial } from “@babylonjs/core/Materials/shaderMaterial”
import { Effect } from “@babylonjs/core/Materials/effect”;

import vertexShader from “…/…/Shaders/sky.vert”;
import fragmentShader from “…/…/Shaders/sky.frag”

export default class TestShaderMaterial {

constructor(mesh) {

    this.vertexShader = Effect.ShadersStore['skyVertexShader'] = vertexShader;

    this.fragmentShader = Effect.ShadersStore['skyFragmentShader'] = fragmentShader;

    this.shaderMaterial = buildShaderMaterial(mesh, 'sky');


    function buildShaderMaterial(mesh, shaderName) {

        const shaderMaterial = new ShaderMaterial("skyShaderMaterial", mesh._scene, {
            vertex: shaderName,
            fragment: shaderName
        }, {
                attributes: ["position"],
                uniforms: ["worldViewProjection", "time", 'cameraOffset']
            })

        return shaderMaterial
    }

    mesh.material = this.shaderMaterial

}

}

if (module.hot) {

module.hot.accept(function accept() {

console.log(' * * * Change in shader detected!');

})
}

Is there a function in BabylonJS that can force the ShaderMaterial to be updated?

1 Like

Also interested in hot-module concept…

:eagle: vanilla js link:

ok I understand!

you can call scene.markAllMaterialsAsDirty(BABYLON.Constants.MATERIAL_AllDirtyFlag)

2 Likes

I tried adding your function both in the module.hot.accept callback function as well as inside my setup function where I call all the scene’s parameters before init without any luck in both cases.

What is the exact purpose of this function? Is is supposed to trigger the reloading of all the materials in the scene when it’s called?

Thank you

yes it will recompile them. But maybe you need to recreate them?