Unity粒子轮廓着色器

Unity particle outline shader

我想为发射立方体网格的粒子系统创建一个不发光的着色器,这样每个发射的网格周围都有一个硬黑色轮廓。

这里是大纲的pass(in Cg):

struct appdata {
    float4 vertex : POSITION;
    float3 normal : NORMAL;
};

struct v2f {
    float4 pos : POSITION;
    float4 color : COLOR;
};

uniform float _Outline;
uniform float4 _OutlineColor;

v2f vert(appdata v) {
    v2f o;

    v.vertex *= ( 1 + _Outline);

    o.pos = UnityObjectToClipPos(v.vertex);

    o.color = _OutlineColor;
    return o;
}

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

(在此之后是渲染网格本身未点亮几何体的简单传递...)

如您所见,我们只是将顶点向外拉伸......但是从哪里开始呢?

对于单个立方体网格,着色器完美运行:

但是,当应用于发射立方体网格的粒子系统时,着色器出现故障:

我怀疑线 v.vertex *= ( 1 + _Outline); 对象 中心向外拉伸顶点,而不是网格中心。

是否有人有替代着色器或关于如何解决此问题的见解?

谢谢, 雅各布

My suspicion is that the line v.vertex *= ( 1 + _Outline); stretches the vertices outward from the object center, not the mesh center.

没错。或者大部分是正确的(粒子系统将所有粒子组合到一个运行时网格中,这就是您的着色器所应用的对象,而不是底层的单个粒子网格,这并不明显)。在非凸面网格(也不是粒子)上尝试轮廓着色器:您会发现凹面部分没有所需的轮廓,证实了您的怀疑。

wrote this shader 几年前,因为我能找到的唯一免费着色器生成的轮廓要么 (a) 不是免费的,要么 (b) "just scale it bigger" 种类。它仍然存在问题(例如在大厚度值时变得锯齿状和怪异),但我一直无法令人满意地解决它们。它使用几何通道将源网格的边缘变成面向相机的四边形,然后使用模板魔术仅渲染轮廓部分。

但是我不确定该着色器在应用于粒子时是否会起作用。我怀疑它会在没有修改的情况下出现,但您可以随意试一试。

原来是我理解错了问题。当访问顶点的 POSITION 语义时,您将获得 world space 中发射粒子的顶点;因此,通过相乘来拉伸顶点实际上只是将它们从世界中心缩放。

要访问与每个粒子相关的顶点,我们必须能够从着色器中访问每个粒子的网格中心。为此,我们在粒子系统的渲染器模块中启用 "Custom Vertex Streams",然后按 + 按钮添加中心流。

现在我们可以从着色器访问 TEXCOORD0(或粒子渲染器 GUI 中中心流右侧指定的任何内容)以获取世界 space 中的网格中心。然后我们从每个顶点位置减去网格中心,向外缩放,然后将网格中心加回去。瞧,每个粒子都有一个轮廓。

以下是轮廓通道的最终 vert 和 frag 片段:

        struct appdata {
            float3 vertex : POSITION;
            float4 color : COLOR;
            float3 center : TEXCOORD0;
        };

        struct v2f {
            float4 pos : POSITION;
            float4 color : COLOR;
            float3 center : TEXCOORD0;

        };

        uniform float _Outline;
        uniform float4 _OutlineColor;

        v2f vert(appdata v) {
            v2f o;
            o.center = v.center;
            float3 vert = v.vertex - v.center;
            vert *= ( 1 + _Outline);
            v.vertex = vert + v.center;
            o.pos = UnityObjectToClipPos(v.vertex);

            o.color = _OutlineColor;
            return o;
        }

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

TLDR:启用顶点流,为粒子中心添加流,并在着色器中访问该值以向外缩放单个顶点。