利用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不直接相等的问题,所以改成了约等于的方式

  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
要使用Shader绘制网格,需要在Unity中创建一个材质(Material)并将其分配给网格对象(Mesh)。材质中包含着Shader的代码,它会告诉Unity如何绘制网格。 以下是一个简单的示例,演示如何使用Shader绘制一个简单的网格: 1. 创建一个新的Shader 在Unity中,选择"Create" -> "Shader"创建一个新的Shader。选择一个合适的命名,例如"MyShader"。 2. 编写Shader代码 打开新创建的Shader,你会看到一个空白的代码文件。在这里,你可以自由编写你的Shader代码。以下是一个简单的示例,它会绘制一个灰色的网格: ``` Shader "Custom/MyShader" { Properties { _MainTex ("Texture", 2D) = "white" {} } SubShader { Tags {"Queue"="Transparent" "RenderType"="Opaque"} Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag struct appdata { float4 vertex : POSITION; float2 uv : TEXCOORD0; }; struct v2f { float2 uv : TEXCOORD0; float4 vertex : SV_POSITION; }; sampler2D _MainTex; v2f vert (appdata v) { v2f o; o.vertex = UnityObjectToClipPos(v.vertex); o.uv = v.uv; return o; } fixed4 frag (v2f i) : SV_Target { return tex2D(_MainTex, i.uv) * 0.5; } ENDCG } } } ``` 这个Shader中包含一个名为"_MainTex"的属性,它是一个2D纹理。在Pass块中,Shader使用这个纹理对网格进行着色,并将颜色乘以0.5,以便将其变为灰色。 3. 创建一个材质 现在,你需要创建一个材质(Material),并将Shader分配给它。在Unity中,选择"Create" -> "Material"创建一个新的材质。给它一个合适的名称,例如"MyMaterial"。 选择新创建的材质,将Shader分配给它。在Inspector窗口中,将Shader字段设置为新创建的Shader。 4. 将材质分配给网格对象 最后一步是将材质分配给网格对象。在Unity中,选择你想要绘制网格对象,并将新创建的材质分配给它。 现在,当你在场景中查看网格对象时,它应该使用你编写的Shader进行着色了。如果需要,你可以进一步调整你的Shader代码,以实现更复杂的效果。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值