【发布时间】:2019-03-04 15:39:50
【问题描述】:
【问题讨论】:
-
不使用标准贴花系统。
-
你有想要的效果的图片,以便我们更好的理解吗?
-
你达到预期的效果了吗?
【问题讨论】:
不是在运行时,使用投影贴花,没有。
你需要的是一个程序纹身图。将其视为另一种纹理,例如光照贴图。您可能需要自定义着色器,但可以使用标准着色器的辅助反照率通道来完成。
棘手的部分是写入该纹理。我将概述基本算法,但由您来实现:
您需要做的第一件事是在代码中解开网格的三角形。您需要确定 UV 贴图上哪些边是连续的,哪些是分开的。接下来,您需要一种方法来识别纹身和初始变换。首先,您需要在纹身源纹理上定义一个原点,它将围绕该原点旋转。然后,您需要定义一个引用源纹理的结构,以及将其应用于目标纹理的 UV 位置 (Vector2)/旋转 (float)/缩放 (float)。
一旦您以该格式存储了纹身,您就可以开始为皮肤构建纹身蒙版纹理。如果您的皮肤 uv 具有一致的像素密度,这会容易得多,因为您可以主要在 uv 空间中工作,但如果没有,您需要重新投影以获得每个 tri 的比例。但是,基本上,您从包含原点的主体三角形开始,然后正常绘制到该三角形上。从那里,您知道该三角形的每个顶点和边缘位于纹身源纹理上的位置。所以,遍历每个相邻的三角形(我推荐广度优先递归方法)并从你已经知道的边缘继续它。如果所有三个顶点都落在源纹理的矩形之外,您可以停在那里。否则,继续下一个三角形的邻居。确保在计算邻居时使用 3D 网格,以免卡在接缝处。
当纹身完全缠绕并重叠自身时,该算法将有一个边缘情况需要处理,但有几种不同的方法可以处理。
将所有纹身写入纹身纹理后,只需将其应用于皮肤材料,瞧!这不仅会使所有计算脱离实时渲染,而且可以让您完全控制纹身的应用方式。
【讨论】:
您可以使用 Unity 的官方预览工具Render Pipelines - High Definition. 使用贴花投影仪
以下是我使用它在桶上投射“纹身”的方法。当然,您可以将其应用于您的模型。 (儿童贴花投影仪,使纹身跟随模型)
导入渲染管线-高清包的最佳方式是到use Unity Hub to create a new project,选择它作为模板。如果它是一个现有项目,这个official blog 可能会对您有所帮助。
成功设置包后,关注this tutorial,您就可以将纹身投射到您想要的任何地方。
【讨论】:
我用自定义着色器做了类似的事情。我认为它会做你想要的。 Mine 基于 iPad 游戏原型的单位等级和类型动态渲染标志。你将如何做这取决于你如何在你的项目中设置东西,但这是我的样子 - 第一个图像是显示网格的线框,第二个是打开着色器并显示它们添加颜色和基于等级和单位的徽章。我刚刚添加了顶部标志的着色器,因为它添加了单位标志,类似于您希望纹身的样子:
请注意,您可以将多个着色器附加到特定网格。
而标志只是添加到着色器并在着色器中作为纹理引用的具有透明度的图像:
您可以看到我们还有一张带有阴影纹理的图片,用作横幅的背景。
这是我的第一个着色器,是不久前编写的,所以我确信它在各种方面都不是最佳的,但它应该足以让你开始(它仍然适用于 Unity 2018.3.x ,虽然我不得不修改一些修改才能编译):
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
}}
}
着色器学习如何做有点让人抓狂,但一旦让它们工作起来就很有趣 - 就像大多数编程一样 :)
在我的例子中,覆盖纹理的大小/形状与标志相同,这使它更容易一些。我认为您需要向着色器添加一些参数,以指示您希望叠加层相对于网格绘制的位置,并且对于纹身边界之外的顶点/片段不做任何事情,就像第一个想法一样。
【讨论】: