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

### 如何在Unity的UI界面中创建并显示三维面片 要在Unity的UI界面中创建并显示三维面片,可以利用自定义Shader和Mesh来实现复杂的几何形状渲染。以下是具体的方法: #### 使用UGUI绘制三角形、四边形和圆环 为了在UGUI上绘制这些图形,可以通过编写自定义Shader以及调整顶点数据的方式完成。 1. **准备自定义材质** 需要先下载Unity内置Shaders[^1],并将它们导入项目中作为基础素材。 2. **设置Canvas Renderer** 确保Canvas使用`Screen Space - Overlay`模式或者`World Space`模式以便支持更灵活的空间变换[^4]。 3. **构建网格(Mesh)** 利用脚本来动态生成目标形状所需的顶点坐标及其索引列表。例如对于一个简单的矩形: ```csharp using UnityEngine; public class CustomMesh : MonoBehaviour { void Start() { Mesh mesh = new Mesh(); Vector3[] vertices = { new Vector3(-0.5f, -0.5f, 0), new Vector3(0.5f, -0.5f, 0), new Vector3(-0.5f, 0.5f, 0), new Vector3(0.5f, 0.5f, 0) }; int[] triangles = { 0, 1, 2, 2, 1, 3 }; mesh.vertices = vertices; mesh.triangles = triangles; GetComponent<MeshFilter>().mesh = mesh; } } ``` 4. **应用着色器语义** 自定义Shader应基于特定需求设计输入变量与计算逻辑[^3]。比如通过UV映射控制纹理贴附效果。 #### 注意事项 - 绘制复杂模型时需注意性能开销,过多独立对象可能影响效率。 - 如果希望进一步优化,则考虑合并多个小物件到单一批次内统一提交给GPU处理。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值