您的位置:首页 > 移动开发 > Unity3D

Unity Shader 使用鼠标绘制自由多边形

2017-01-04 22:47 260 查看
原文链接 : http://www.manew.com/thread-98660-1-1.html

效果:



注意,基于 unity 5.4.1 或以上版本

shader 代码:

Shader "Unlit/polygon"
{
Properties
{
//定义基本属性,可以从编辑器里面进行设置的变量
// _MainTex ("Texture", 2D) = "white" {}
}

CGINCLUDE
//从应用程序传入顶点函数的数据结构定义
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
};
//从顶点函数传入片段函数的数据结构定义
struct v2f
{
float2 uv : TEXCOORD0;
float4 vertex : SV_POSITION;
};
//定义贴图变量
sampler2D _MainTex;
// float4 _MainTex_ST;

//定义与脚本进行通信的变量
vector Value[8];
int PointNum =0;

//计算两点间的距离的函数
float Dis(float4 v1,float4 v2)
{
return sqrt(pow((v1.x-v2.x),2)+pow((v1.y-v2.y),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;
}

//绘制多边形,这里限制了顶点数不超过6。可以自己根据需要更改。
bool pnpoly(int nvert, float4 vert[8], float testx, float testy)
{

int i, j;
bool c=false;
float vertx[6];
float verty[6];

for(int n=0;n<nvert;n++)
{
vertx
= vert
.x;
verty
= vert
.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;
}

v2f vert (appdata v)
{
v2f o;
//将物体顶点从模型空间换到摄像机剪裁空间,也可采用简写方式——o.vertex = UnityObjectToClipPos(v.vertex);
o.vertex = mul(UNITY_MATRIX_MVP,v.vertex);
//2D UV坐标变换,也可以采用简写方式——o.uv = TRANSFORM_TEX(v.uv, _MainTex);
//o.uv = v.uv.xy * _MainTex_ST.xy + _MainTex_ST.zw;
return o;
}
fixed4 frag (v2f i) : SV_Target
{

//绘制多边形顶点
for(int j=0;j<PointNum;j++)
{
if(Dis(i.vertex, Value[j])<3)
{
return fixed4(1,0,0,0.5);
}
}
//绘制多边形的边
for(int k=0;k<PointNum;k++)
{
if(k==PointNum-1)
{
if(DrawLineSegment(Value[k],Value[0],2,i))
{
return fixed4(1,1,0,0.5);
}
}
else
{
if(DrawLineSegment(Value[k],Value[k+1],2,i))
{
return fixed4(1,1,0,0.5);
}
}

}
//填充多边形内部
if(pnpoly(PointNum, Value,i.vertex.x ,i.vertex.y))
{
return fixed4(0,1,0,0.3);
}
return fixed4(0,0,0,0);
//fixed4 col = tex2D(_MainTex, i.uv);
//return col;
}
ENDCG

SubShader
{
Tags { "RenderType"="Opaque" }
LOD 100
Pass
{
//选取Alpha混合方式
Blend  SrcAlpha OneMinusSrcAlpha
//在CGPROGRAM代码块中写自己的处理过程
CGPROGRAM
//定义顶点函数和片段函数的入口分别为vert和frag
#pragma vertex vert
#pragma fragment frag
//包含基本的文件,里面有一些宏定义和基本函数
#include "UnityCG.cginc"

ENDCG
}
}
}


C#绘制代码:

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

public Material mat; //绑定材质
Vector3[] worldPos; //存储获取的3D坐标
Vector4[] screenPos; //存储待绘制的多边形顶点屏幕坐标
int maxPointNum=8;  //多边形顶点总数
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);
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: