乘以顶点数据会导致轮廓不均匀(shaderlab unity3d)

乘以顶点数据会导致轮廓不均匀(shaderlab unity3d),unity3d,shader,hlsl,cg,shaderlab,Unity3d,Shader,Hlsl,Cg,Shaderlab,我在Unity3D的shader lab中创建了一个简单的轮廓着色器,它有两个过程:通过沿顶点法线乘以顶点信息来放大对象,通过两个过程绘制对象的常规(基本过程)版本。问题出在大纲通行证的代码中: Pass{ 名称“大纲” 注销 混合SrcAlpha ONEMUSSRCALPHA//正常 CGP程序 #pragma顶点顶点 #布拉格碎片碎片 #包括“UnityCG.cginc” 结构appdata{ 浮动4顶点:位置; 3正常:正常; }; 结构v2f{ 浮动位置4:位置; 3正常:正常; };

我在Unity3D的shader lab中创建了一个简单的轮廓着色器,它有两个过程:通过沿顶点法线乘以顶点信息来放大对象,通过两个过程绘制对象的常规(基本过程)版本。问题出在大纲通行证的代码中:

Pass{
名称“大纲”
注销
混合SrcAlpha ONEMUSSRCALPHA//正常
CGP程序
#pragma顶点顶点
#布拉格碎片碎片
#包括“UnityCG.cginc”
结构appdata{
浮动4顶点:位置;
3正常:正常;
};
结构v2f{
浮动位置4:位置;
3正常:正常;
};
均匀浮点数_/newidth;
均匀浮动4_轮廓颜色;
v2f垂直(appdata v){
v、 vertex.xyz*=_OutlineWidth;
v2fo;
o、 位置=UnityObjectToClipPos(v顶点);
返回o;
}
半4框架(v2f i):颜色{
返回_OutlineColor;
}
ENDCG
}
\u OutlineWidth
\u OutlineColor
分别属于范围和颜色类型。我已经将该着色器应用于两个通过编程创建的“类似tetromino”的网格。结果是(点击链接):

如您所见,创建了轮廓,但轮廓沿对象的外边缘的宽度不一致。沿其中一个面,轮廓更大--沿距离形状中心最远的面,轮廓更大。对于非凸形状,问题被放大;轮廓甚至可能根本不包含形状:


我理解这是由于顶点位置相对于形状中心,而线
v.vertex.xyz*=\u OutlineWidth
仅将该位置乘以常量(使其远离对象中心)。如何修改代码,使轮廓像素的计算独立于形状的中心,并与对象的真实轮廓保持一致?

正如@Gnietschow所说,您需要使用这些顶点的“平滑法线”来了解扩展轮廓的方向:

资料来源:FrostSoft

一种方法是计算C#中的软法线,然后将其指定为顶点数据。此示例使用
uv2
uv3
通道来保持平滑法线分量:

Mesh-Mesh=GetComponent().Mesh;
Vector3[]网格顶点=网格顶点;
//将顶点位置映射到该位置所有顶点的ID
字典顶点合并=新字典();
对于(int i=0;i
资料来源:

然后,使用TEXCOORD2和TEXCOORD3在
vert
中重建平滑法线。使用这些移动相应的顶点位置,同时在任何照明目的下保持顶点法线与硬法线相同:

Pass {
    Name "OUTLINE"

    ZWrite Off
    Blend SrcAlpha OneMinusSrcAlpha // Normal

    CGPROGRAM
    #pragma vertex vert
    #pragma fragment frag
    #include "UnityCG.cginc"

    struct appdata {
        float4 vertex : POSITION;
        float3 normal : NORMAL;
        float4 texcoord2 : TEXCOORD2;
        float4 texcoord3 : TEXCOORD3;
    };

    struct v2f {
        float4 position : POSITION;
        float3 normal : NORMAL;
    };

    uniform float _OutlineWidth;
    uniform float4 _OutlineColor;

    v2f vert(appdata v) {
        // add the outline width in the direction of the shared normal
        float3 sharedNormal = float3(v.texcoord2.xy, v.texcoord3.x);

        v.vertex.xyz +=  _OutlineWidth * sharedNormal;  

        v2f o;
        o.position = UnityObjectToClipPos(v.vertex);
        return o;
    }

    half4 frag(v2f i) : COLOR {
        return _OutlineColor;
    }

    ENDCG
}

如前所述,您应该沿着顶点法线推动顶点
,而不是直接乘以它们的位置,因为这会从对象中心缩放它们。因此,您应该考虑法线
v.vertex.xyz+=v.normal*\u OutlineWidth对于同一位置的所有顶点,应平均法线,这一点很重要,否则这些面将彼此断开。我如何平均相同位置的所有vérties的法线@GnietschowI从未在着色器实验室与您合作过,因此我只能给您一些提示。您需要在创建模型时计算它们。当我需要边界着色器时,我在加载时为所有网格添加了第二条法线。因此,我聚集了所有具有相同位置的顶点,平均它们的法线,并将其存储为每个顶点中的第二个法线,稍后我可以在着色器中使用该法线作为输入。很抱歉,等待了很长时间,我花了一段时间来实现这一点。您给出的代码效果很好,除了当有六条法线都从一个点正交向外指向时会出现一些瑕疵,但修复起来很简单。谢谢