Updating/recompiling shaders from chunks on runtime

Hey guys! I’m working on a project that has a ‘curved world’ view (like this Curved World? need to replicate that perspective fx). I’ve written a custom transformVS shader chunk that does the job. The problem is that I’d like to switch between my custom and standart transformVS chunk sometimes (for example, I don’t need to display curvature in main menu location). Right now I modify the pc.shaderChunks.transformVS directly so that affects all the materials and I don’t need to loop over each and update it manually, cause I have lots of materials there.
The question is following: when I modify any of pc.shaderChunks’ chunks, how could I apply the changes? AFAIK I have to recompile all the shaders at runtime to re-build them using updated chunks. How do I do that?

1 Like

Hi @Igor,

Instead of updating the primitive chunk that is getting consumed by the pc.StandardMaterial class, you can update the transformVS on a material base.

Loop through your active materials and replace it like this:

for (var i = 0; i < this.materials.length; i++) {
   this.materials[i].chunks.transformVS = `your custom shader code`;
   this.materials[i].update();
}

That way at any point you can easily revert the materials to the standard chunk code.

Thanks @Leonidas, it definitely works! I did it before decided to try modifying pc.shaderChunks :slight_smile: ! I’m just looking for the most simple way to update all the materials at once instead of iterating over each material in the library and updating it manually.

1 Like

So, I’ve done this another way in the past when doing something similar to what you are doing here (messing with the pc.shaderChunks strings).

  • Replace the original code in the pc.shaderChunks property you modified
  • Then run the following:
// Force an app-wide recompilation of shaders
// each shader removed from the program library will be recompiled when requested 
// (usually that would be the next frame)
const library = app.graphicsDevice.getProgramLibrary();

let i, len;
for (i = 0, len = app.graphicsDevice.shaders.length; i < len; i++) {
  const shader = app.graphicsDevice.shaders[i];
  const vshader = shader.definition.vshader;

  // you can either remove all shaders or add a condition to find only shaders affected by your change
  // usually I've been doing this by searching for a comment or special uniform name
  if (vshader.indexOf("curve") > -1) {
    library.removeFromCache(shader);
  }
}

2 Likes

For anyone finding this thread there is an additional command that needs to run for the shaders to recompile now, updating the code snippet:

// Force an app-wide recompilation of shaders
// each shader removed from the program library will be recompiled when requested 
// (usually that would be the next frame)
const library = app.graphicsDevice.getProgramLibrary();

let i, len;
for (i = 0, len = app.graphicsDevice.shaders.length; i < len; i++) {
  const shader = app.graphicsDevice.shaders[i];
  const vshader = shader.definition.vshader;

  // you can either remove all shaders or add a condition to find only shaders affected by your change
  // usually I've been doing this by searching for a comment or special uniform name
  if (vshader.indexOf("curve") > -1) {
    library.removeFromCache(shader);
  }
}

app.scene.updateShaders = true;
4 Likes

Thank you for sharing, @Leonidas! Is that some sort of setting that you need to set once anywhere, or is it like a setter that you need to call every time you do the cache update?

Yes, the latter, you set it after the cache update and the shaders will recompile when the next frame is rendered.

1 Like