實現說明:
本文將使用geometry shader實現一類網格體的網格顯示。mesh的組成單元爲三角片,但實際應用中有時需要實現不顯示網格的第三邊的矩形網格,這時可以用c#來修改貼圖實現這種效果,也可以直接對GPU進行編程,讓其繪製指定的區域。本文編寫的shader源碼是基於AssetStore的免費shader:UCLA Wireframe Shader製作而來,感謝原作者
效果預覽:
(在Editor模式的Wireframe模式下:)
(在本文編寫的shader模式下:)
實現步驟詳解:
一、利用uv特性獲取三角型直角點
二、對geometry Shader處理後的每個像素點賦值
三、完整shader腳本
一、利用uv特性獲取三角型直角點
之所以要獲取三角型的直角點,是因爲如果得到geometry 參數插值後的對應到兩個直角邊的距離,就可以判斷這一點是保留還是捨去
// Geometry Shader
[maxvertexcount(3)]
void UCLAGL_geom(triangle UCLAGL_v2g p[3], inout TriangleStream<UCLAGL_g2f> triStream)
{
UCLAGL_v2g np[3];
bool find = false;
for (int i = 0; i < 3 && !find; i++)
{
for (int j = i + 1; j < 3 && !find; j++)
{
if (abs(p[i].uv.x - p[j].uv.x)>0.01f && abs(p[i].uv.y - p[j].uv.y)>0.01f)
{
np[0] = p[3 - i - j];//設置np[0]爲直角點
np[1] = p[i];
np[2] = p[j];
find = true;
}
}
}
//points in screen space
float2 p0 = _ScreenParams.xy * np[0].pos.xy / np[0].pos.w;
float2 p1 = _ScreenParams.xy * np[1].pos.xy / np[1].pos.w;
float2 p2 = _ScreenParams.xy * np[2].pos.xy / np[2].pos.w;
//edge vectors
float2 v0 = p2 - p1;
float2 v1 = p2 - p0;
float2 v2 = p1 - p0;
//area of the triangle
float area = abs(v1.x*v2.y - v1.y * v2.x);
//values based on distance to the edges
float dist0 = area / length(v0);
float dist1 = area / length(v1);
float dist2 = area / length(v2);
UCLAGL_g2f pIn;
//add the first point
pIn.pos = np[0].pos;
pIn.uv = np[0].uv;
pIn.dist = float3(dist0, 0, 0);
triStream.Append(pIn);
//add the second point
pIn.pos = np[1].pos;
pIn.uv = np[1].uv;
pIn.dist = float3(0, dist1, 0);
triStream.Append(pIn);
//add the third point
pIn.pos = np[2].pos;
pIn.uv = np[2].uv;
pIn.dist = float3(0, 0, dist2);
triStream.Append(pIn);
}
二、對Fragment Shader的每個像素點賦值
在geometry Shader處理後的每個點的信息中,保存當前點到三角形每個邊的距離,利用這些信息進行如下方法:
// Fragment Shader
float4 UCLAGL_frag(UCLAGL_g2f input) : COLOR
{
//blend between the lines and the negative space to give illusion of anti aliasing
float4 targetColor = _Color * tex2D(_MainTex, input.uv);
float4 transCol = float4(0, 0, 0, 0);
return (_Thickness > input.dist.y || _Thickness > input.dist.z) ? targetColor : transCol;
}
可以得到透明的區域和線條區域
三、完整shader腳本
(1,cg腳本:)//Algorithms and shaders based on code from this journal
//http://cgg-journal.com/2008-2/06/index.html
#ifndef UCLA_GAMELAB_WIREFRAME
#define UCLA_GAMELAB_WIREFRAME
#include "UnityCG.cginc"
// DATA STRUCTURES //
// Vertex to Geometry
struct UCLAGL_v2g
{
float4 pos : POSITION; // vertex position
float2 uv : TEXCOORD0; // vertex uv coordinate
};
// Geometry to UCLAGL_fragment
struct UCLAGL_g2f
{
float4 pos : POSITION; // fragment position
float2 uv : TEXCOORD0; // fragment uv coordinate
float3 dist : TEXCOORD1; // distance to each edge of the triangle
};
// PARAMETERS //
//float4 _Texture_ST; // For the Main Tex UV transform
float _Thickness = 1; // Thickness of the wireframe line rendering
float4 _Color = { 1,1,1,1 }; // Color of the line
float4 _MainTex_ST; // For the Main Tex UV transform
sampler2D _MainTex; // Texture used for the line
// SHADER PROGRAMS //
// Vertex Shader
UCLAGL_v2g UCLAGL_vert(appdata_base v)
{
UCLAGL_v2g output;
output.pos = mul(UNITY_MATRIX_MVP, v.vertex);
output.uv = TRANSFORM_TEX(v.texcoord, _MainTex);//v.texcoord;
return output;
}
// Geometry Shader
[maxvertexcount(3)]
void UCLAGL_geom(triangle UCLAGL_v2g p[3], inout TriangleStream<UCLAGL_g2f> triStream)
{
UCLAGL_v2g np[3];
bool find = false;
for (int i = 0; i < 3 && !find; i++)
{
for (int j = i + 1; j < 3 && !find; j++)
{
if (abs(p[i].uv.x - p[j].uv.x)>0.01f && abs(p[i].uv.y - p[j].uv.y)>0.01f)
{
np[0] = p[3 - i - j];//設置np[0]爲直角點
np[1] = p[i];
np[2] = p[j];
find = true;
}
}
}
//points in screen space
float2 p0 = _ScreenParams.xy * np[0].pos.xy / np[0].pos.w;
float2 p1 = _ScreenParams.xy * np[1].pos.xy / np[1].pos.w;
float2 p2 = _ScreenParams.xy * np[2].pos.xy / np[2].pos.w;
//edge vectors
float2 v0 = p2 - p1;
float2 v1 = p2 - p0;
float2 v2 = p1 - p0;
//area of the triangle
float area = abs(v1.x*v2.y - v1.y * v2.x);
//values based on distance to the edges
float dist0 = area / length(v0);
float dist1 = area / length(v1);
float dist2 = area / length(v2);
UCLAGL_g2f pIn;
//add the first point
pIn.pos = np[0].pos;
pIn.uv = np[0].uv;
pIn.dist = float3(dist0, 0, 0);
triStream.Append(pIn);
//add the second point
pIn.pos = np[1].pos;
pIn.uv = np[1].uv;
pIn.dist = float3(0, dist1, 0);
triStream.Append(pIn);
//add the third point
pIn.pos = np[2].pos;
pIn.uv = np[2].uv;
pIn.dist = float3(0, 0, dist2);
triStream.Append(pIn);
}
// Fragment Shader
float4 UCLAGL_frag(UCLAGL_g2f input) : COLOR
{
//blend between the lines and the negative space to give illusion of anti aliasing
float4 targetColor = _Color * tex2D(_MainTex, input.uv);
float4 transCol = float4(0, 0, 0, 0);
return (_Thickness > input.dist.y || _Thickness > input.dist.z) ? targetColor : transCol;
}
#endif
(2,shader腳本:)Shader "UCLA Game Lab/Wireframe/Double-Sided Cutout"
{
Properties
{
_Color ("Line Color", Color) = (1,1,1,1)
_MainTex ("Main Texture", 2D) = "white" {}
_Thickness ("Thickness", Float) = 1
}
SubShader
{
Pass
{
Tags { "RenderType"="Opaque" "Queue"="Geometry" }
Blend SrcAlpha OneMinusSrcAlpha
Cull Off
LOD 200
CGPROGRAM
#pragma target 5.0
#include "UnityCG.cginc"
#include "UCLA GameLab Wireframe Functions.cginc"
#pragma vertex vert
#pragma fragment frag
#pragma geometry geom
// Vertex Shader
UCLAGL_v2g vert(appdata_base v)
{
return UCLAGL_vert(v);
}
// Geometry Shader
[maxvertexcount(3)]
void geom(triangle UCLAGL_v2g p[3], inout TriangleStream<UCLAGL_g2f> triStream)
{
UCLAGL_geom(p, triStream);
}
// Fragment Shader
float4 frag(UCLAGL_g2f input) : COLOR
{
float4 col = UCLAGL_frag(input);
if(col.a < 0.5f) discard;
else col.a = 1.0f;
return col;
}
ENDCG
}
}
}
後續說明:
1.判斷直角的地方可能會出現uv的x或y不直接相等的問題,所以改成了約等於的方式