Using a mask to render 2nd material

Hello,

I am looking into rendering a model with two materials, and using a mask to specify which material should render where, basically like a soccer ball with a black & white hexagonal pattern etc, where I want to be able to change the colour tint of only one of the materials (for example, the white) in runtime, but I have no idea where to start or how to go about this.

I have previously done some texture swapping on a model with multiple mesh instances before; simply meshInstances[this.meshNumber].material = this.textureActive.resource;, but I’m not sure if this is similarly applicable, as I want to be able to control the tint of the material and not make all the possible textures by hand.

To give a bit more insight on the kind of project I’m working on, it’s essentially a customization tool where I want the user to be able to customize footballs / soccer balls by selecting base shapes, and changing colour tints of these shapes, but in this case leaving the black hexagonal pattern in-tact.

There’s unfortunately not a project or much material where I can show what I would like to achieve.

In summary, is it possible to have 1 model, 2 materials, specify which material to render where via mask, and change colour tint on 1 of 2 materials.

Edit1:
I don’t want to apply different material id’s on polygons, but I want to be able to paint specific patterns on a per-model basis

Hi @IHVD,

Yes that’s possible using the detail texture channel available on the pc.StandardMaterial. It’s not exposed in editor yet (I think it’s coming very soon), so you will have to enable it in code.

Check the docs on which material channels provide a second detail map channel for use:

https://developer.playcanvas.com/en/api/pc.StandardMaterial.html#diffuseDetailMap

For a more complex scenario or for finer control you can use custom shaders and shader chunks:

https://developer.playcanvas.com/en/tutorials/?tags=shaders

2 Likes

Cheers Leonidas for the incredibly fast reply.

I’ll have a go at this and report back whether I was successful or not, thanks!

1 Like

Seems I was wrong in specifically having two different materials.
I currently have the mask setup as a diffuseDetail;
I have 2 separate Textures, the Diffuse and the “mask” that I want to use as a detailmap;
image
only now the trick is to be able to change the colour of the diffuse without affecting the colour of the areas in the diffuseDetailMap.
Since I have basically no shader knowledge, I’ve been looking a bit through the Planet Earth tutorial on Shader Chunks as you mentioned, but I don’t really know where to actually start.

Would my shader have to be something like this, in very simple terms;

uniform sampler2D texture_diffuseMap;

vec3 getDiffuse() {
    return "a product resulting from diffuseMap - diffuseDetailMap"
}

and then change the colour of the diffuse in code, rather than in editor?

So, for something like that you would override the diffuse (albedo) shader chunk. You could pass these custom uniforms to the material:

  • texture_mask
  • texture_diffuseSecondMap

And then here is some code that I think can work in your case, not tested but you can work from this I think:

uniform vec3 material_diffuse;
uniform sampler2D texture_diffuseMap;

// --- custom uniforms
uniform sampler2D texture_mask;
uniform sampler2D texture_diffuseSecondMap;

void getAlbedo() {
  vec4 mask = texture2D(texture_mask, $UV);
  vec3 texel0 = texture2D(texture_diffuseMap, vUv0).rgb;
  vec3 texel1 = texture2D(texture_diffuseSecondMap, vUv0).rgb;

  // --- we use the mask channels to find the final color for this pixel
  // --- in this example the RED channel of the mask is used to map the diffuseMap texture + the diffuse color as assigned in editor
  // --- the GREEN channel is used to paint the custom texture uniform diffuseSecondMap that isn't affected by the diffuse color
  dAlbedo = gammaCorrectInput(
      addAlbedoDetail(mask.r * texel0 * material_diffuse.rgb + mask.g * texel1)
  );
}
1 Like

Hmm, I’ve added the shaderchunk but I don’t seem to be getting the desired result.
Is it okay if I add you to the private project? if I invite you on that project, it shouldn’t add you to the organization, right?

Yes, I think I won’t be added to the org, my PC username: leonidas . I may be slow to take a look at it today though.

Note for the shader chunk to work you need to add the custom uniforms to the material in your Javascript code:

material.setParameter('texture_mask', maskAsset.resource);
material.setParameter('texture_diffuseSecondMap', secondTextureAsset.resource);
1 Like

I’ve added you to the project.

I think I might’ve been a bit unclear with what I meant when I had 2 textures.
image
Here’s a layout of the file structure.

I have 1 diffuse and 1 mask, and I’m currently setting the Colour on the material directly; image
In the end-product, a separate application where we use playcanvas as the engine, I won’t have access to the editor itself and would have to load these scripts in separately.

Ok thanks, I will take a look at it later today and get back to you.

1 Like

Very much appreciated, thank you so much!

So, if I understood correctly you want to use the diffuse texture for the whole surface of model, and paint red (apply tint) only to the masked parts.

Here is the simplified shader, it has been already implemented in your project:

uniform vec3 material_diffuse;
uniform sampler2D texture_diffuseMap;

// --- custom uniforms
uniform sampler2D texture_mask;

void getAlbedo() {
  vec4 mask = texture2D(texture_mask, $UV);
  vec3 texel0 = texture2D(texture_diffuseMap, vUv0).rgb;

  dAlbedo = gammaCorrectInput(
      addAlbedoDetail(texel0 + (mask.r * material_diffuse.rgb))
  );
}

image

image

2 Likes

Amazing! Thank you very much :smiley:
Now to actually implement this to our actual application, but I’m pretty sure that shouldn’t be too difficult.

Thanks once again, you’re a life saver!

-IHVD

1 Like