Reputation: 333
I wish to create an unlit shader for a particle system that emits cube meshes, such that each emitted mesh has a hard black outline around it.
Here is the pass for the outline (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; }
(And after this is a simple pass to render the unlit geometry of the mesh itself...)
As you can see, we are simply stretching the vertices outward... but from what?
For a single cube mesh, the shader works perfectly:
However, when applied to a particle system emitting cube meshes, the shader breaks down:
My suspicion is that the line v.vertex *= ( 1 + _Outline);
stretches the vertices outward from the object center, not the mesh center.
Does anyone have a replacement shader or insight on how to fix this problem?
Thanks, rbjacob
Upvotes: 2
Views: 2951
Reputation: 333
It turns out that I misconstrued the problem. When accessing the POSITION
semantic of the vertices, you are getting the vertices of the emitted particles in world space; therefore, stretching the vertices by multiplying is actually just scaling them away from the world center.
To access the vertices relative to each particle, we must be able to access each particle's mesh center from within the shader. To do this, we enable "Custom Vertex Streams" inside the Renderer module of the particle system and press the + button to add the Center stream.
Now we can access TEXCOORD0
(or whatever is specified to the right of the Center stream in the particle renderer GUI) from the shader to get the mesh center in world space. Then we subtract the mesh center from each vertices position, scale outward, and add the mesh center back. And voila, each particle has an outline.
Here are the final vert and frag snippets for the outline pass:
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: Enable vertex streams, add a stream for the particle center, and access this value in the shader to scale individual vertices outward.
Upvotes: 1
Reputation: 15941
My suspicion is that the line
v.vertex *= ( 1 + _Outline);
stretches the vertices outward from the object center, not the mesh center.
That would be correct. Or mostly correct (particle systems combine all the particles into one runtime mesh and that's what your shader is applied to, not the underlying individual particle mesh, which isn't obvious). Try your outline shader on a non-convex mesh (that is also not a particle): You'll find that the concave part won't have the desired outline, confirming your suspicion.
I wrote this shader a couple of years back because the only free shaders I could find that generated outlines were either (a) not free or (b) of the "just scale it bigger" variety. It still has problems (such as getting jagged and weird at large thickness values), but I was never able to resolve them satisfactorily. It uses a geometry pass to turn the source mesh's edges into camera-facing quads, then stencil magic to render only the outline portion.
However I am unsure if that shader will function when applied to particles. I doubt it will without modification, but you're free to give it a shot.
Upvotes: 0