利用shader繪製矩形網格

實現說明:

本文將使用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不直接相等的問題,所以改成了約等於的方式

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章