How to position and size a decal to 1 side of a mesh?

Is there a way to display the decal on only 1 side of the mesh? As you can see from my PG, it displays on both sides, and also wraps around the edge of the mesh. Which is not ideal because I’d like it to only display on the bottom. Also, the image seems flipped.

I would assume it would have to do with the normals and sizing, but I have tried Vector3.Up/Down, etc… Not sure if this is more complex than it seems.

Playground example:
https://playground.babylonjs.com/#2NITT6#9

How’s this for texture mapping? :smiley: How to put text on one side of cube only? - #2 by JohnK

1 Like

Thanks for your suggestion. I don’t think this is useful because my mesh is imported and has a ton of faces. Is there not an example of applying a decal to a specific part of a mesh?

I really appreciate the answers.

I would also like to know if it’s possible to apply the decal to specific faces that have normals that point in a specific direction? I thought that would be the { normal: Vector3.Up } property.

Normal example (shows the image is flipped):

Gonna ping @PatrickRyan if he knows a thing or 2 about texture wrapping/mapping :slight_smile:

2 Likes

@brandon, what you are looking for is UV unwrapping your model. The UVs are texture coordinates that you use to wrap color/normal/AO/emissive/etc around your mesh. You will want to separate each face of the deck from each other and from the edge of the deck and place them in UV space as islands that don’t overlap. You will then set up your base color texture to have your decal cover the portion of the base color texture where the UVs of your label side sits in UV space. If you have wood or deck tape on the other side, you will place that texture under the appropriate UVs. Same on the edge. Your unwrap will look something like below:

This debug UV texture shows you how the texture maps to the surface through your UV layout. The UV coordinates of each vertex are the easiest way to do this as you don’t need to do any calculation in the shader for face normal as the UV coordinates map specific texels to each part of the mesh. Every standard material in Babylon does this for you already, so nothing special is needed other than a UV unwrap of your mesh.

What you are seeing right now is a planar projection from one side of your deck so that the top and bottom surfaces of the deck share the same UV coordinates. You see stretching on your edges due to that projection as well. And since the projection came from one side of your mesh, your top and bottom surfaces will be a mirror of one another as the faces on the rear surface are projected into UV space in reverse which accounts for your texture being reversed.

I hope this helps unblock you, but if you have more questions, I am happy to help.

3 Likes

Thanks, I really appreciate your response. This helped me clean up the uv map in blender.

So I think I might have a couple options here…

The problem with the UV map is that I will have to create a new image for the UV to display the bottom graphic every time it changes.

The most efficient way I thought of was, using a decal and somehow relating that to the bottom UV island I have in Blender. Is this possible or theoretically possible?

Or will I just have to go with changing the uv image file to display the bottom graphic?

@brandon, now I understand more about what you are trying to achieve… I wasn’t aware you wanted to swap out the decals before. So there are a few ways you can approach this, one being splitting the mesh into two and assigning different materials containing the textures you need for each surface. That works, but isn’t a very elegant solution as you carry extra draw calls for the split meshes.

So I would opt for a custom shader (like I normally do) which will require a little bit of planning on the part of your UV layout. Let’s make these assumptions for the sake of argument:

  • Each side of the deck is UVed in a vertical orientation, side by side
  • The decal side of your deck can be contained between 0 and 0.3 in U space, leaving the rest over 0.3 in U for the other side of the deck and your edge thickness
  • Your decal textures are all the same size and are 30% of the width of the texture you are using for the base board.
  • You want to show the deck without any decal at some point, so you would just have bare wood as the default in your main texture between 0 and 0.3 in U

The we just need to mix the two textures, the base texture and the decal texture, in the shader. I am just going to show the part of the shader that will mix the two textures for you as you can see the rest of the PBR node setup in (8193) PBR Nodes in Node Materials Part 1 - YouTube. You will want to do this for each of the PBR inputs that need to change. For example, if you need custom roughness or normal textures for your decal, you will need to do this method for each of those inputs. I would always double check the reasoning for adding more channels like normal or roughness to this method just so that my number of texture loads does not get out of hand.

For the two images, we have a UV debug map used as the base texture:

And a Babylon.js logo on a background for the decal. Note that the decal is exactly 30% of the width of the base texture and 100% of the height. Setting up your textures like this will streamline things for you down the line and prevent visual bugs in your asset:

In the shader we are loading the two textures into texture blocks (you will assign your decal through code as you need to change it). The first thing I do is to divide the U coordinates by 0.3 for our decal texture to get it into the correct proportion. Next is to use the U coordinate of the mesh UVs to set up a step node with a break of 0.3. This is then used as the gradient in the lerp between the two textures. The resulting texture is:

We retain the proportion of the images correctly, but have now mixed them to correspond to our UV layout in the mesh. Again, this would then become the input for the base color of the PBRMetallicRoughness block and you would also do this for other inputs that need a switch on them. Another thing you may want to add to the shader is the ability to bypass the decal all together (for a deck with no decal) and to do that, you would just change the break point to 0.0 instead of 0.3. Or you could do something like multiplying the break float by a bool which can be set in code which would translate to multiplying it by 0 if false and 1 if true for your decal. This would prevent you from having to update a lot of code if your UV breaks change in the future.

I hope this helps, but if you have more questions, please feel free to ping me.

3 Likes

Thank you so much for this amazing help! You are truly a hero. I really could not have done it without you, and thank you @msDestiny14 for pinging @PatrickRyan!

A+++ to you sir!

Cheers! :beers:

EDIT:
Actually one more thing, everything works great, the only problem now is that decals with transparency/alpha shows as a black background. I have tried quite a few solutions but since it isn’t a diffuseTexture it doesn’t seem to be working.

Example:
image

Playground with transparent decal:
https://playground.babylonjs.com/#2NITT6#15

@brandon, I’m happy to help. If you have an image with alpha, you only need to mix in the alpha with the mask coming out of the step node. We can easily do this by taking the oneMinus of the decal alpha and then using a max node with the step to create the combined lerp gradient. You can see it here:

I also updated your PG with the extra nodes. I must say it’s been a very long time since I manually configured a node material in code. I tend to prefer using the node graph and either parsing from a snippet or loading and parsing the json from my github.io.

This should get you up and running, but feel free to ping me with other questions.

2 Likes

This is great! I think I am questioned out now…

Thanks a lot! The information is extremely helpful.

1 Like

@PatrickRyan Hey man!

So this has been working excellent. Now I’m at the point where I need additional customizations to my mesh.

I am trying to add another decal texture to the top of the skateboard. You did mention about how I should have my textures to 30% of the width of the uv map. I tried to mess with the node material editor and set the top texture, texture break, to 0.6 (Example uv below). Maybe I am misunderstanding how it’s positioned, because it’s displaying on both sides of the mesh. Just some guidance on this will do me good.

Node: https://nodematerial-editor.babylonjs.com/#SJ37LA#1

Hi @brandon, glad to hear that you are making progress. I can certainly help you with adding decals to the top as well. The first thing I would do, however, is to slighly alter your base texture to reduce on some unneeded texels which will bloat your textures for download. Instead of UVing your model into a square texture, if you create your textures like this:

you won’t be wasting any texture space. What that will mean is aligning your UVs in Blender close together like this and then scaling all of the UVs out to fill the width of UV space. For example, in the UV image earlier in this thread, you can see my UV layout with what look like ellipses in the islands. These are actually circular, but since I wanted to use a texture that is twice as wide as tall, I needed to scale my UVs by 50% in width to use UV space. Now when I pass a texture that is 2048 x 1024 I will get the correct mapping for my mesh. You will just need to figure out based on the percentage of scaling up you do with the UVs what your final texture size will be which will determine the values we pipe into the shader for position and scale.

So for example with the image above, the width is 378 pixels and one half of your deck is 177 pixels so one side sits between 0 and 0.468 and the other sits between 0.468 and 0.937 in U. You just need to do this math to figure out what your actual U values will be.

I did simplify the original shader so there is only one value to push into the shader which will be the width of your deck assuming the bottom and top are unwrapped to take up the same area in UV space. There was one thing that was actually wrong in your version of the shader, and the rest was just simply adjusting the mask to apply only to the top surface of your deck. The incorrect thing is that you can only have one fragmentOut in your shader. The second fragmentOut was replacing the first and you weren’t getting anything from the path connected to the first. So the shader looks like this:

Which yields a final output that looks like this:

What you are seeing is that the first face of the deck displays from 0 to deckWidth in U space. We then take that mask and multiply with OneMinus the mask created by deckWidth * 2. That will give us a white strip aligned with the second deck face. I then take the max of this mask and OneMinus the texture alpha of the second texture. This means that you have your wood grain texture piped into the texture block that currently contains the UV test texture, and the decals for each side into their appropriate texture block. This will get you what you are looking for, and you just need to change the deckWidth to whatever your final UV width turns out to be.

The updated shader can be found here.

2 Likes

You’re literally the best, always exceeding my expectations.

This is absolutely perfect! I really appreciate your support.

I will update my uv map and yeah that should definitely speed things up. Everything is clear and makes total sense.

Thank you.

1 Like

I keep saying it as well :smiley:

3 Likes

This is awesome! Took me some time but this helped me manage to figure out how to also get another decal under the graphic decal!


image

1 Like

Love the fact that it is to create a board editor !!!

3 Likes