Hi everybody,
Warning: this gets a bit technical
Originally the outlines in VizionEck were a separate mesh made by hand. I'd finish my level maps, and then edge by edge I'd extrude a rectangle until the full thing was covered.
(The large space between them is just illustrative)
The result looked great but as you'd imagine it took a lot of time. To fix that, I switched to a different method that calculates the outlines in real time.
The solution is a custom shader. Shaders for those of you that don't know, are responsible for drawing polygons to the screen. They handle everything from basic textures to complex bump maps. I determined it'd be much easier for my shader to make the outlines as the result of three scaled faces verse what I had been doing. Rendering the same geometry three times with slight variations is much quicker than generating completely new geometry and rendering it just once.
This looks exactly the same as outlines once all three layers are packed together.
So all the shader needs to do is render each face three times, each slightly smaller than before. To do this the shader moves each vertex along its tangent. If these tangents point towards the center of the face, then it scales the face. Pretty simple.
"//" means a comment. These lines are ignored by the system and are only there to make reading the code more user friendly.
Although the shader is finished, this is the result it makes.
Kinda cool, but not correct. This is because the tangent values for the cube are not pointing towards the center of each face. To change this I have a custom import function that calculates tangents the way I need.
See, now it's perfect.
Let me know if you have any questions or need extra clarification.
-Michael Armbrust
Warning: this gets a bit technical
Originally the outlines in VizionEck were a separate mesh made by hand. I'd finish my level maps, and then edge by edge I'd extrude a rectangle until the full thing was covered.
(The large space between them is just illustrative)
The result looked great but as you'd imagine it took a lot of time. To fix that, I switched to a different method that calculates the outlines in real time.
The solution is a custom shader. Shaders for those of you that don't know, are responsible for drawing polygons to the screen. They handle everything from basic textures to complex bump maps. I determined it'd be much easier for my shader to make the outlines as the result of three scaled faces verse what I had been doing. Rendering the same geometry three times with slight variations is much quicker than generating completely new geometry and rendering it just once.
This looks exactly the same as outlines once all three layers are packed together.
So all the shader needs to do is render each face three times, each slightly smaller than before. To do this the shader moves each vertex along its tangent. If these tangents point towards the center of the face, then it scales the face. Pretty simple.
"//" means a comment. These lines are ignored by the system and are only there to make reading the code more user friendly.
Code: [Select]
Shader "Custom/Outlines" {
Properties {
//These four properties set up variables for materials using the shader. Colors are shown as four numbers representing red, blue, green, and alpha. Alpha is the transparency of the color. "float" is a non integer number, being shorthand for "floating point."
_Color ("Outline Color", Color) = (1.0,1.0,1.0,1.0)
_BColor("Background Color", Color) = (0.0,0.0,0.0,1.0)
_OutlineD ("Outline Distance From Edge", float)=.3
_OutlineW ("Outline Width", float)=.1
}
SubShader {
Tags {}
//A pass is what renders the polygon. Since I need to render it three times, I have three passes.
Pass{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
//Here the color variable is defined for this pass
fixed4 _BColor;
//structs
struct vertexInput {
float4 vertex : POSITION;
};
struct vertexOutput {
float4 pos : SV_POSITION;
};
//vertex function
vertexOutput vert(vertexInput v){
vertexOutput o;
//this converts the vertex positions from world space to screen space
o.pos= mul(UNITY_MATRIX_MVP, v.vertex);
return o;
}
//fragment function
float4 frag(vertexOutput i) : COLOR
{
//sets the pixel to the color
return _BColor;
}
ENDCG
}
//second pass
Pass{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
//material defined variables
float _OutlineD;
float _OutlineW;
fixed4 _Color;
//structs
struct vertexInput {
float4 vertex : POSITION;
float3 tangent : TANGENT;
};
struct vertexOutput {
float4 pos : SV_POSITION;
float3 tangentDir : TEXCOORD0;
};
//vertex function
vertexOutput vert(vertexInput v){
vertexOutput o;
//offsets the vertices to make each polygon smaller, then converts to screen space, then decreases world space depth. This makes this pass render above the previous pass
o.pos= mul(UNITY_MATRIX_MVP, v.vertex+(_OutlineD-_OutlineW)*float4(v.tangent, 0))-float4(0,0,.0001,0);
return o;
}
//fragment function
float4 frag(vertexOutput i) : COLOR
{
return _Color;
}
ENDCG
}
Pass{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
float _OutlineD;
float _OutlineW;
fixed4 _BColor;
//structs
struct vertexInput {
float4 vertex : POSITION;
float3 tangent : TANGENT;
};
struct vertexOutput {
float4 pos : SV_POSITION;
float3 tangentDir : TEXCOORD0;
};
//vertex function
vertexOutput vert(vertexInput v){
vertexOutput o;
o.pos= mul(UNITY_MATRIX_MVP, v.vertex+(_OutlineD+_OutlineW)*float4(v.tangent, 0))-float4(0,0,.0002,0);
return o;
}
//fragment function
float4 frag(vertexOutput i) : COLOR
{
return _BColor;
}
ENDCG
}
}
}
Although the shader is finished, this is the result it makes.
Kinda cool, but not correct. This is because the tangent values for the cube are not pointing towards the center of each face. To change this I have a custom import function that calculates tangents the way I need.
See, now it's perfect.
Let me know if you have any questions or need extra clarification.
-Michael Armbrust