开启辅助访问
 找回密码
 注册帐号

扫一扫,访问微社区

开发者专栏

关注:1761

当前位置:游戏蛮牛 技术专区 开发者专栏

__________________________________________________________________________________
开发者干货区版块规则:

  1、文章必须是图文形式。(至少2幅图)
      2、文章字数必须保持在1500字节以上。(编辑器右下角有字数检查)
      3、本版块只支持在游戏蛮牛原创首发,不支持转载。
      4、本版块回复不得无意义,如:顶、呵呵、不错......【真的会扣分的哦】
      5、......
__________________________________________________________________________________
查看: 3361|回复: 130

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

  [复制链接]  [移动端链接]
排名
3538
昨日变化
4

11

主题

41

帖子

715

积分

Rank: 9Rank: 9Rank: 9

UID
139121
好友
7
蛮牛币
1403
威望
0
注册时间
2016-3-11
在线时间
231 小时
最后登录
2017-7-13

专栏作家

发表于 2017-1-3 10:53:15 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有帐号?注册帐号

x
本帖最后由 Moscibroda 于 2017-1-3 10:59 编辑

前言
在前面的文章中,我们已经了解了怎样使用Unity Shader来绘制简单的点和线,本文将延续上次的话题,讲述一下如何在场景中使用Unity Shader绘制自由多边形。
本文所述的程序,支持在地图中用鼠标点击,确定多边形顶点,并且绘制多边形的边,在内部填充半透明的颜色。先展示一下最终效果,完整的工程在文末的附件中。
5.gif

1 开发工具介绍
Windows 10(64位)
Unity 5.4.1(64位)

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


3 核心代码实现
3.1  Polygon.cs脚本中实现的是鼠标点击和向shader传递信息的功能
(1)为了实现鼠标点选场景中的3D位置,需要使用射线
[C#] 纯文本查看 复制代码
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);    
RaycastHit hit;    
if (Physics.Raycast(ray, out hit, 100))    
{    
    Debug.DrawLine(ray.origin, hit.point);                         
} 

(2)向shader传递顶点的位置和数量
[C#] 纯文本查看 复制代码
mat.SetVectorArray("Value",screenPos); //传递顶点屏幕位置信息给shader  
mat.SetInt ("PointNum",pointNum2Shader); //传递顶点数量给shader  

(3)将鼠标点击的位置转化为屏幕坐标
[C#] 纯文本查看 复制代码
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)计算两点之间的距离函数  
[C#] 纯文本查看 复制代码
float Dis(float4 v1,float4 v2)  
{  
    return sqrt(pow((v1.x-v2.x),2)+pow((v1.y-v2.y),2));  
}    

(2)绘制线段的函数
[C#] 纯文本查看 复制代码
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
[C#] 纯文本查看 复制代码
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
[C#] 纯文本查看 复制代码
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
[C#] 纯文本查看 复制代码
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[6];   
            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[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;  
            }  
                              
            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    
        }    
    }    
}  



5 运行效果








小结
本文介绍的是关于Unity Shader的一种基本应用。使用了简单的绘制技术,完成了在场景中进行自由多边形区域的选择功能。目前,还只是一种简单的实现,仅仅展示了绘制一个多边形。读者可以根据自己的需要,扩展其相关功能。
============================================================
感兴趣可以关注我的CSDN:http://blog.csdn.net/zzlyw/article/details/53992048
============================================================

游客,如果您要查看本帖隐藏内容请回复

评分

参与人数 5蛮牛币 +8 鲜花 +14 收起 理由
sdjkxhb + 2 学习了
弱智的小毛驴 + 5 很给力!
荏苒的光影0 + 2 很给力!
海峡同城哥 + 1 请问SetVectorArray编译不通过怎么回事?求.
z7232124 + 8 + 4 很给力!

查看全部评分


回复

使用道具 举报

3偶尔光临
215/300
排名
7351
昨日变化
7

0

主题

36

帖子

215

积分

Rank: 3Rank: 3Rank: 3

UID
148755
好友
0
蛮牛币
719
威望
0
注册时间
2016-5-16
在线时间
61 小时
最后登录
2017-1-23
QQ
发表于 2017-1-3 11:22:19 | 显示全部楼层
谢谢分享,收藏学习了

回复 支持 反对

使用道具 举报

7日久生情
3891/5000
排名
50
昨日变化
12

0

主题

257

帖子

3891

积分

Rank: 7Rank: 7Rank: 7Rank: 7

UID
14133
好友
0
蛮牛币
5285
威望
0
注册时间
2014-2-12
在线时间
1480 小时
最后登录
2017-7-21
发表于 2017-1-3 11:32:52 | 显示全部楼层
好东西,谢谢分享。

回复

使用道具 举报

5熟悉之中
749/1000
排名
4476
昨日变化
26

2

主题

95

帖子

749

积分

Rank: 5Rank: 5

UID
167937
好友
0
蛮牛币
296
威望
0
注册时间
2016-9-10
在线时间
422 小时
最后登录
2017-7-20
发表于 2017-1-3 11:36:30 | 显示全部楼层
感谢分享

回复

使用道具 举报

6蛮牛粉丝
1341/1500
排名
2396
昨日变化
10

0

主题

388

帖子

1341

积分

Rank: 6Rank: 6Rank: 6

UID
155931
好友
0
蛮牛币
82
威望
0
注册时间
2016-7-8
在线时间
509 小时
最后登录
2017-7-21
发表于 2017-1-3 11:38:31 | 显示全部楼层
抗锯齿两边都是借口比vdsjbvjdskbvj出具的萨看出VB都是科技传播萨克雷315avcs32a1c32sa1csa321cas321csa

回复 支持 反对

使用道具 举报

6蛮牛粉丝
1245/1500
排名
4923
昨日变化
6

2

主题

776

帖子

1245

积分

Rank: 6Rank: 6Rank: 6

UID
92518
好友
1
蛮牛币
2466
威望
0
注册时间
2015-4-15
在线时间
277 小时
最后登录
2017-4-6
发表于 2017-1-3 11:56:41 | 显示全部楼层

感谢分享

回复

使用道具 举报

3偶尔光临
284/300
排名
6978
昨日变化
6

0

主题

80

帖子

284

积分

Rank: 3Rank: 3Rank: 3

UID
186272
好友
0
蛮牛币
329
威望
0
注册时间
2016-11-23
在线时间
78 小时
最后登录
2017-7-11
发表于 2017-1-3 11:58:02 | 显示全部楼层
厉害了  我的歌

回复 支持 反对

使用道具 举报

排名
330
昨日变化

43

主题

355

帖子

4131

积分

Rank: 9Rank: 9Rank: 9

UID
42814
好友
32
蛮牛币
6248
威望
0
注册时间
2014-8-31
在线时间
1283 小时
最后登录
2017-7-12

专栏作家

QQ
发表于 2017-1-3 12:02:00 | 显示全部楼层
这个吊炸天

回复

使用道具 举报

7日久生情
2523/5000
排名
2270
昨日变化
12

2

主题

1633

帖子

2523

积分

Rank: 7Rank: 7Rank: 7Rank: 7

UID
182740
好友
0
蛮牛币
3943
威望
0
注册时间
2016-11-10
在线时间
420 小时
最后登录
2017-7-21
发表于 2017-1-3 12:21:45 | 显示全部楼层
谢谢楼主分享

回复

使用道具 举报

5熟悉之中
824/1000
排名
2146
昨日变化
5

0

主题

132

帖子

824

积分

Rank: 5Rank: 5

UID
168193
好友
0
蛮牛币
727
威望
0
注册时间
2016-9-12
在线时间
202 小时
最后登录
2017-7-21
发表于 2017-1-3 12:25:25 | 显示全部楼层

谢谢分享,收藏学习了

回复 支持 反对

使用道具 举报

4四处流浪
446/500
排名
3595
昨日变化
25

2

主题

31

帖子

446

积分

Rank: 4

UID
163094
好友
6
蛮牛币
812
威望
0
注册时间
2016-8-19
在线时间
117 小时
最后登录
2017-7-21
发表于 2017-1-3 12:38:09 | 显示全部楼层
功能炫酷,点个赞

回复

使用道具 举报

4四处流浪
333/500
排名
10577
昨日变化
12

0

主题

226

帖子

333

积分

Rank: 4

UID
181594
好友
0
蛮牛币
135
威望
0
注册时间
2016-11-7
在线时间
45 小时
最后登录
2017-5-30
发表于 2017-1-3 12:47:32 | 显示全部楼层
感谢分享

回复

使用道具 举报

6蛮牛粉丝
1285/1500
排名
835
昨日变化

1

主题

44

帖子

1285

积分

Rank: 6Rank: 6Rank: 6

UID
100193
好友
1
蛮牛币
3027
威望
0
注册时间
2015-5-14
在线时间
317 小时
最后登录
2017-6-24
发表于 2017-1-3 12:57:01 | 显示全部楼层
这个shader没看懂,但是计算两点的距离不是Shader有内置的函数?distance
[发帖际遇]: 一个袋子砸在了 skeleton 头上,skeleton 赚了 1 蛮牛币. 幸运榜 / 衰神榜

回复 支持 反对

使用道具 举报

6蛮牛粉丝
1285/1500
排名
835
昨日变化

1

主题

44

帖子

1285

积分

Rank: 6Rank: 6Rank: 6

UID
100193
好友
1
蛮牛币
3027
威望
0
注册时间
2015-5-14
在线时间
317 小时
最后登录
2017-6-24
发表于 2017-1-3 13:02:57 | 显示全部楼层
PNPoly这个是主要内容

回复 支持 反对

使用道具 举报

7日久生情
2662/5000
排名
1318
昨日变化
7

122

主题

732

帖子

2662

积分

Rank: 7Rank: 7Rank: 7Rank: 7

UID
141908
好友
2
蛮牛币
68464
威望
0
注册时间
2016-3-18
在线时间
982 小时
最后登录
2017-7-21

活力之星七夕浪漫情人

发表于 2017-1-3 13:04:44 | 显示全部楼层
,如果您要查看本帖隐

本人分享所有资源,仅供学习,请勿商用,谢谢合作!
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册帐号

本版积分规则

快速回复 返回顶部 返回列表