前言

在前面的文章中,我们已经了解了怎样使用Unity Shader来绘制简单的点和线,本文将延续上次的话题,讲述一下如何在场景中使用Unity Shader绘制自由多边形。

本文所述的程序,支持在地图中用鼠标点击,确定多边形顶点,并且绘制多边形的边,在内部填充半透明的颜色。先展示一下最终效果。完整工程下载地址在本文末尾处。

【Unity Shader学习笔记】(五)使用鼠标绘制自由多边形(附完整工程源码)

1 开发工具介绍

Windows 10(64位)

Unity 5.4.1(64位)

 

2 建立工程

首先建立一个新工程,命名为Polygon,并创建一个Scene。在场景中新建一个Plane,该Plane是默认带有碰撞体的,这个碰撞体必须有,因为我们在后边使用鼠标选取位置的时候,涉及到碰撞检测。给该Plane加上贴图。

【Unity Shader学习笔记】(五)使用鼠标绘制自由多边形(附完整工程源码)

3 核心代码实现

3.1  Polygon.cs脚本中实现的是鼠标点击和向shader传递信息的功能

(1)为了实现鼠标点选场景中的3D位置,需要使用射线

Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);  
RaycastHit hit;  
if (Physics.Raycast(ray, out hit, 100))  
{  
	Debug.DrawLine(ray.origin, hit.point);  					 
}  

(2)向shader传递顶点的位置和数量

(3)将鼠标点击的位置转化为屏幕坐标

worldPos[currentpointNum-1] = hit.point;
Vector3 v3 = Camera.main.WorldToScreenPoint (worldPos [currentpointNum-1]);
screenPos[currentpointNum-1] = new Vector4(v3.x,Screen.height-v3.y,v3.z,0);

3.2 Polygon.shader中实现多边形的绘制功能

(1)计算两点之间的距离函数

float Dis(float4 v1,float4 v2)
{
	return sqrt(pow((v1.x-v2.x),2)+pow((v1.y-v2.y),2));
}  

(2)绘制线段的函数

bool DrawLineSegment(float4 p1, float4 p2, float lineWidth,v2f i)
{
    float4 center = float4((p1.x+p2.x)/2,(p1.y+p2.y)/2,0,0);
    //计算点到直线的距离  
     float d = abs((p2.y-p1.y)*i.vertex.x + (p1.x - p2.x)*i.vertex.y +p2.x*p1.y -p2.y*p1.x )/sqrt(pow(p2.y-p1.y,2) + pow(p1.x-p2.x,2));  
    //小于或者等于线宽的一半时,属于直线范围  
    float lineLength = sqrt(pow(p1.x-p2.x,2)+pow(p1.y-p2.y,2));
    if(d<=lineWidth/2 && Dis(i.vertex,center)<lineLength/2)  
    {  
        return true;  
    }  
    return false;
}

(3)绘制多边形的函数

参考:https://www.ecse.rpi.edu/Homepages/wrf/Research/Short_Notes/pnpoly.html

bool pnpoly(int nvert, float4 vert[6], float testx, float testy)
{
    
    int i, j;
    bool c=false;
    float vertx[6];
    float verty[6];

    for(int n=0;n<nvert;n++)
    {
        vertx[n] = vert[n].x;
        verty[n] = vert[n].y;
    }
    for (i = 0, j = nvert-1; i < nvert; j = i++) {
    if ( ((verty[i]>testy) != (verty[j]>testy)) && (testx < (vertx[j]-vertx[i]) * (testy-verty[i]) / (verty[j]-verty[i]) + vertx[i]) )
       c = !c;
    }
    return c;
}

4 完整的C#脚本和Shader代码

Ploygon.cs

using UnityEngine;
using System.Collections;
[ExecuteInEditMode] 
public class Polygon : MonoBehaviour {

	public Material mat; //绑定材质
	Vector3[] worldPos; //存储获取的3D坐标
	Vector4[] screenPos; //存储待绘制的多边形顶点屏幕坐标
	int maxPointNum=6;  //多边形顶点总数
	int currentpointNum =0; //当前已经获得的顶点数
	int pointNum2Shader =0; //传递顶点数量给shader
	bool InSelection=true; //是否处于顶点获取过程

	void Start () {
		worldPos = new Vector3[maxPointNum];
		screenPos = new Vector4[maxPointNum];
	}
	

	void Update () {
		mat.SetVectorArray("Value",screenPos); //传递顶点屏幕位置信息给shader
		mat.SetInt ("PointNum",pointNum2Shader); //传递顶点数量给shader

		//使用摄像机发射一条射线,以获取要选择的3D位置
		Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);  
		RaycastHit hit;  
		if (Physics.Raycast(ray, out hit, 100))  
		{  
			Debug.DrawLine(ray.origin, hit.point);  					 
		}  

		//利用鼠标点击来获取位置信息
		if (Input.GetMouseButtonDown (0)&& InSelection) 
		{
			if (currentpointNum < maxPointNum) 
			{
				currentpointNum++;
				pointNum2Shader++;
				worldPos[currentpointNum-1] = hit.point;
				Vector3 v3 = Camera.main.WorldToScreenPoint (worldPos [currentpointNum-1]);
				screenPos[currentpointNum-1] = new Vector4(v3.x,Screen.height-v3.y,v3.z,0);
			} 
			else 
			{
				InSelection = false;
			}
		}

		//实时更新已选择的3D点的屏幕位置
		for (int i = 0; i < maxPointNum; i++) 
		{
			Vector3 v3 = Camera.main.WorldToScreenPoint (worldPos[i]);
			screenPos[i] = new Vector4(v3.x,Screen.height-v3.y,v3.z,0);

		}

		//检测是否有3D点移动到了摄像机后面,如果有,则停止绘制
		for (int i = 0; i < currentpointNum; i++) 
		{
			if (Vector3.Dot(worldPos[i]- Camera.main.transform.position,Camera.main.transform.forward)<=0) 
			{
				pointNum2Shader=0;
				break;
			}
			pointNum2Shader= currentpointNum;
		}
	}

	//抓取当前的渲染图像进行处理
	void OnRenderImage(RenderTexture src, RenderTexture dest) {  
		Graphics.Blit(src, dest, mat);  
	}  		
}


Polygon.shader


5 运行效果

【Unity Shader学习笔记】(五)使用鼠标绘制自由多边形(附完整工程源码)

【Unity Shader学习笔记】(五)使用鼠标绘制自由多边形(附完整工程源码)

【Unity Shader学习笔记】(五)使用鼠标绘制自由多边形(附完整工程源码)

【Unity Shader学习笔记】(五)使用鼠标绘制自由多边形(附完整工程源码)

小结

本文介绍的是关于Unity Shader的一种基本应用。使用了简单的绘制技术,完成了在场景中进行自由多边形区域的选择功能。目前,还只是一种简单的实现,仅仅展示了绘制一个多边形。读者可以根据自己的需要,扩展其相关功能。


---------------------------------------------------------------------------------------------------------------------------------

工程文件打包下载:

链接: https://pan.baidu.com/s/1nvRSweH 密码: jr2g





相关文章: