我正在研究纹身模拟器程序,我需要知道贴花(纹身)是否有办法包裹在目标网格上,比如纹身从腿的一侧到另一侧,或者说事件在它后面。
3 回答
不是在运行时,使用投影贴花,不。
你需要的是一个程序纹身图。将其视为另一种纹理,例如光照贴图。您可能需要自定义着色器,但可以使用标准着色器的辅助反照率通道来完成。
棘手的部分是写入该纹理。我将概述基本算法,但由您来实现:
您需要做的第一件事是在代码中解开网格的三角形。您需要确定 UV 贴图上哪些边是连续的,哪些是分开的。接下来,您需要一种方法来识别纹身和初始变换。首先,您需要在纹身源纹理上定义一个原点,它将围绕该原点旋转。然后,您需要定义一个引用源纹理的结构,以及将其应用于目标纹理的 UV 位置 (Vector2)/旋转 (float)/缩放 (float)。
以该格式存储纹身后,您就可以开始为皮肤构建纹身蒙版纹理。如果您的皮肤 uv 具有一致的像素密度,这会容易得多,因为您可以主要在 uv 空间中工作,但如果没有,您将需要重新投影以获得每个 tri 的比例。但是,基本上,您从包含原点的主体三角形开始,然后正常绘制到该三角形上。从那里,您知道该三角形的每个顶点和边缘位于纹身源纹理上的位置。所以,遍历每个相邻的三角形(我推荐一种广度优先递归方法)并从你已经知道的边缘继续它。如果所有三个顶点都落在源纹理的矩形之外,您可以停在那里。否则,继续下一个三角形的邻居。确保你'
该算法将有一个边缘情况,你需要处理当纹身一直缠绕并重叠自身时,但有几种不同的方法可以处理这个问题。
将所有纹身写入纹身纹理后,只需将其应用到皮肤材料上,瞧!这不仅会使所有计算脱离实时渲染,而且可以让您完全控制纹身的应用方式。
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:
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:
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.
您可以使用 Unity 的官方预览工具Render Pipelines - High Definition 使用贴花投影仪。
这是我用它来将“纹身”投射到桶上的方法。当然,您可以将其应用于您的模型。(儿童贴花投影仪,使纹身跟随模型)
导入 Render Pipelines - High Definition 包的最佳方法是使用 Unity Hub 创建一个新项目,选择它作为模板。如果它是一个现有的项目,这个官方博客可能会对你有所帮助。
成功设置软件包后,按照本教程进行操作,您就可以将纹身投影到您想要的任何地方。