4

I'm working on tattoo simulator program, i need to know if there's a way for the decal (tattoo) to wrap arond the target mesh, like having a tattoo that goes from one side to the other side of lets say leg, or event behind it.

enter image description here

chakmeshma
  • 246
  • 8
  • 27

3 Answers3

4

Not at runtime, using a projected decal, no.

What you need here instead is a procedural tattoo map. Think of it as another texture, like a lightmap. You may need a custom shader, but it could possibly be done with the secondary albedo channel of the standard shader.

The tricky part is writing to that texture. I'll outline the basic algorithm, but leave it up to you to implement:

The first thing you need to be able to do is unwrap the mesh's triangles in code. You need to identify which edges are contiguous on the UV map, and which are separate. Next, you need a way to identify the tattoo and the initial transform. First, you'll want to define an origin on the tattoo source texture that it will rotate around. Then you'll want to define a structure that references the source texture, and the UV position (Vector2) / rotation (float) / scale (float) to apply it to in the destination texture.

Once you have the tattoos stored in that format, then you can start building the tattoo mask texture for the skin. If your skin uvs have a consistent pixel density, this is a lot easier because you can work primarily in uv-space, but if not, you'll need to re-project to get the scale for each tri. But, basically, you start with the body triangle that contains the origin, and draw onto that triangle normally. From there, you know where each vertex and edge of that triangle lies on the tattoo source texture. So, loop through each neighboring triangle (I recommend a breadth-first recursive method) and continue it from the edge you already know. If all three verts fall outside the source texture's rect, you can stop there. Otherwise, continue with the next triangle's neighbors. Make sure you're using the 3D mesh when calculating neighbors so you don't get stuck at seams.

That algorithm is going to have an edge case you'll need to deal with for when the tattoo wraps all the way around and overlaps itself, but there are a couple different ways you can deal with that.

Once you've written all tattoos to the tattoo texture, just apply it to the skin material and voila! Not only will this move all the calculations out of real-time rendering, but it will let you fully control how your tattoos can be applied.

Louis Ingenthron
  • 1,267
  • 8
  • 21
1

You can use a decal projector using Unity's official preview tool Render Pipelines - High Definition.

Here's how I used it to project a "tatoo" onto a bucket. You can apply it to your model of course. (Child the decal projector so that the tatoo follows the model)

enter image description here

The best way to import Render Pipelines - High Definition package is to use Unity Hub to create a new project, choosing it as a template. If it's an existing project, this official blog might help you.

Once you succefully set up the package, follow this tutorial and you'll be able to project tatoos onto your models anywhere you want.

  • I need the decal to wrap around an object, e.g. if the decal is large enough, portions of decal stretches to behind the object... – chakmeshma Mar 14 '19 at 12:28
  • Once you nested the projector, it's like wrapped. You can move and scale the projector to stretch. – ClarHandsome Mar 15 '19 at 18:55
1

I've done something similar with a custom shader. I think it would do what you want. Mine is dynamically rendering flags based on rank and type of a unit for an iPad game prototype. Exactly how you'll do it depends a bit on how you have things setup in your project, but here's what mine looks like - first image is the wireframe showing the mesh and second is with the shaders turned on and shows them adding the colors and emblem based on rank and unit. I've just included the shader for the top flag since that has the unit emblem added added similar to how you want your tattoo to be:

enter image description here enter image description here

Note that you can attach multiple shaders to a particular mesh.

And the emblem is just an image with transparency that is added to the shader and referenced as a texture within the shader:

shader parameter setup - emblem

You can see we also have a picture that has some shadow texture that's used as the background for the banner.

This is my first shader and was written a while ago, so I'm sure it's sub-optimal in all kinds of ways, but it should hopefully be enough to get you started (and it still works in Unity 2018.3.x, though I had to hack in some changes to get it to compile):

Shader "Custom/TroopFlagEmblemShader" {

Properties {
    _BackColor ("Background Color", Color) = (0.78, 0.2, 0.2)       // scarlet  
    _MainTex ("Background (RGBA)", 2D) = "" {}
    _EmblemTex("Emblem (RGBA)", 2D) = "" {}
    _Rank ( "Rank (1-9)", Float ) = 3.0
}

SubShader {


Pass {

CGPROGRAM
#pragma exclude_renderers xbox360 ps3 flash
#pragma target 3.0
#pragma vertex vert
#pragma fragment frag

#include "UnityCG.cginc"

struct appdata {
    float4 vertex: POSITION;
    float4 texcoord: TEXCOORD0;
};

struct v2f {
    float4 pos: SV_POSITION;
    float2 uv: TEXCOORD0;
};


uniform sampler2D _MainTex;
uniform sampler2D _EmblemTex;
uniform float3 _BackColor;
uniform float _Rank;

v2f vert( appdata v ) 
{
    v2f o;

    o.pos = UnityObjectToClipPos( v.vertex );
    o.uv = v.texcoord.xy;

    return o;
}


float4 frag( v2f IN ) : COLOR
{
    float4 outColor;
    float4 backTextureColor = tex2D( _MainTex, IN.uv.xy );
    float4 emblemTextureColor = tex2D( _EmblemTex, IN.uv.xy );

        // not drawing the square at all above rank 5
    if ( _Rank >= 6.0 )
        discard;

    if ( _Rank < 5 )                                    // 4 and below 
    {           
        outColor = float4( (emblemTextureColor.rgb * emblemTextureColor.a) + 
                            (((1.0 - emblemTextureColor.a) * backTextureColor.rgb) * _BackColor.rgb) , 1 );

        // float4(_BackColor.rgb, 1 ));
    }
    else if ( _Rank >= 5.0 ) // but excluded from 6 above
    {
        // 5 is just solid backcolor combined with background texture
        outColor = float4( backTextureColor.rgb * _BackColor.rgb, 1 );
    }

    return outColor;
}
ENDCG
}}
}

Shaders are a bit maddening to learn how to do, but pretty fun once you get them working - like most programming :)

In my case the overlay texture was the same size/shape as the flag which makes it a bit easier. I'm thinking you'll need to add some parameters to the shader that indicate where you want the overlay to be drawn relative to the mesh and do nothing for vertexes/fragments outside your tattoo bounds, just as a first thought.

Dad
  • 6,388
  • 2
  • 28
  • 34
  • it occurs to me that you're also going to need to take scale into account; likely you'll have these tattoo images at a certain scale but the user of your software will want to be able to adjust that. So you'll need to include that scale adjustment in your shader calculations along with position. And that, of course, means you'll also need to take into account rotation because they can rotate the image as well. So definitely the above is just a starting point. – Dad Mar 16 '19 at 15:45