Setting Gradient in a NodeMaterial

I’ve done a lot of searching and trying things but I can’t figure this out.

I have a nodematerial create in NME. It has a gradient. (its a noise texture)

In my code I want to change the gradient in my code based on user input. I change float values which change the look of the noise already and that works fine - but I can’t figure out how to change the gradient.

in the below code blk is returned correctly as a gradient block, and I set the colorSteps on it - there is no error/crash but the noise gradient does not change.

let blk = this.Material.getBlockByName(block_name) as BABYLON.GradientBlock;
if(blk!=null) {
blk.colorSteps.length = 0;
blk.colorSteps.push(new BABYLON.GradientBlockColorStep(0, new BABYLON.Color3(0, 0, 0)));
blk.colorSteps.push(new BABYLON.GradientBlockColorStep(20, new BABYLON.Color3(1, 0.14901960784313725, 0)));
}

ok - so it seems once I change the colerSteps I have to rebuild the nodematerial.

But there doesn’t seem to be a way to wait until the build has completed - I really need a completed material. I am looking for something like;

await build()

But I can’t find it - anyone have any solution to this?

The build operation is synchronous, which other steps you want to wait for ? cause you could check the scene executeWhenReady once built ?

1 Like

Yeah thats what I thought. So when I just wait a bit (using a setTimeout) it seems to work.

This whole thing comes from trying to change the gradient in a NodeMaterial imported from NME and it not changing the material instantly like it does when I setFloat. (I mean using getBlockByName(‘…’).value = x )

So again the real question is - what is the right way to change a GradientBlock’s colorsteps from my javascript code in an imported NodeMaterial without having to rebuild the material? Is this just not possible?

This question is more for @Deltakosh :slight_smile:

The gradient data is used to generate the shader code (so it is super fast and does not need additional uniforms),

But to do so you have then to rebuild the material. You can then use material.onBuildObservable to know when your material is ready :slight_smile:

thanks, I’ll take a look at that.

You have to call nodeMaterial.build() after you change the values of the Gradient

Try to comment out:
// nodeMaterial.build()

Thanks - yes I eventually got there. The problem is I only do the render once - I’m creating an image from the shader texture. so I need to build, wait till its built, then render.

I’ll have a look what Deltakosh put in soon.

Oh, sorry :slight_smile: As @deltakosh wrote the observable will do the job.

1 Like

ok - I am back on this, but it is still not working.

So I;

  1. await ParseFromFileAsync to get the material
  2. I createProceduralTexture from this material (it is a procedural texture from NME)
  3. I then change the material gradient with getBlockByName
  4. I listen to the onBuildObservable
  5. I build() the material
  6. when onBuildObservable returns, I do texture.refresh()

The first time I do this it still has the old gradient.
If next time the texture refreshes it has the new gradient.
If I wait (with a settimeout) for at least 200 ms after onbuildobservable returns, then I do a texture.refresh() it works with the new gradient.

Why do I have to wait??? am I missing a step? How can I tell the material is built and ready with the new gradient?

It’s working for me w/o the observables as well.

Change the step to sett the changes:

image

I have to set the refreshRate to 0 - which breaks your code. This seems to be the key.
Is there a reason why this breaks everything?

So as stated in the doc: RefreshRate will ask the texture to render only once. So whatever changes after that will not redraw the texture.

You can use procTexture.resetRefreshCounter() to reset that state

I already tried it and it doesn’t work.

This PG should display red, green and a blue plane.

  1. Red is set at scene init - works.
  2. After 2s green color is set, nm is rebuilt, texture counter is reset but doesn’t change the texture, so no color change on the plane.
  3. After 4s blue is set, same actions taken, color changes to green.

RED - ALL OK

setting gradient - red
VM1052:47 rebuilding nm
proceduralTexture.ts:417 shouldRender (_currentRefreshId === -1) true -1 -1
VM1052:31 beforegen tex
VM1052:34 gen tex Uint8Array(4194304) [255, 0, 0, 255, 255, 0, 0, 255, 255, 0, 0, 255, 255, 0, 0, 255, 255, 0, 0, 255, 255, 0, 0, 255, 255, 0, 0, 255, 255, 0, 0, 255, 255, 0, 0, 255, 255, 0, 0, 255, 255, 0, 0, 255, 255, 0, 0, 255, 255, 0, 0, 255, 255, 0, 0, 255, 255, 0, 0, 255, 255, 0, 0, 255, 255, 0, 0, 255, 255, 0, 0, 255, 255, 0, 0, 255, 255, 0, 0, 255, 255, 0, 0, 255, 255, 0, 0, 255, 255, 0, 0, 255, 255, 0, 0, 255, 255, 0, 0, 255, …]

GREEN - get content still returns 255, 0, 0, 255

setting gradient - green
VM1052:47 rebuilding nm
VM1052:39 nm onbuild
proceduralTexture.ts:417 shouldRender (_currentRefreshId === -1) true -1 0
VM1052:31 beforegen tex
VM1052:34 gen tex Uint8Array(4194304) [255, 0, 0, 255, 255, 0, 0, 255, 255, 0, 0, 255, 255, 0, 0, 255, 255, 0, 0, 255, 255, 0, 0, 255, 255, 0, 0, 255, 255, 0, 0, 255, 255, 0, 0, 255, 255, 0, 0, 255, 255, 0, 0, 255, 255, 0, 0, 255, 255, 0, 0, 255, 255, 0, 0, 255, 255, 0, 0, 255, 255, 0, 0, 255, 255, 0, 0, 255, 255, 0, 0, 255, 255, 0, 0, 255, 255, 0, 0, 255, 255, 0, 0, 255, 255, 0, 0, 255, 255, 0, 0, 255, 255, 0, 0, 255, 255, 0, 0, 255, …]

BLUE - get content returns[0, 255, 0, 255 - so the previous one

VM1052:47 rebuilding nm
VM1052:39 nm onbuild
proceduralTexture.ts:417 shouldRender (_currentRefreshId === -1) true -1 1
VM1052:31 beforegen tex
VM1052:34 gen tex Uint8Array(4194304) [0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, …]
proceduralTexture.ts:433 

Now the funny thing about this is that it sometimes generates all three colors. I believe it should behave always the same. Any ideas?

Checking :wink:

1 Like

Ok this PG should work but there was a bug:
Fix procedural refresh issue by deltakosh · Pull Request #15366 · BabylonJS/Babylon.js (github.com)

1 Like

Cool!

Thanks!

EDIT:
Wait :slight_smile: Wrong PG. This one doesn’t work. It relies solely on the resetRefreshCounter function:

The first one I posted uses a little bit of hacking with various observables and setting the refreshRate instead calling the resetRefreshCounter function.

However your fix removed this issue and the result is always the same.

1 Like

Sorry, my bad, it works :slight_smile: The problem was in my PG.

So here is a working PG using the resetRefreshCounter:

Ok, so I’m gonna look like an idiot but I tried the ‘working’ PG posted here one more time

…and guess, it failed to draw the blue texture :smiley:

@deltakosh isn’t this related to the global shader cache of the browser your were writing about not so long ago?