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

查看:1030 |回复:9 | 2018-6-22 13:04:38

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

x
实现 : 支持在 Plane 上用鼠标点击,确定多边形顶点,并且绘制多边形的边,在里面填充颜色 ;
Plane 带有碰撞体 , 使用鼠标选取位置的时候涉及到碰撞检测 .
ScriptShader005.cs 脚本实现鼠标点击和向 Shader 传递信息的功能 .
Shader005.shader 实现多边形的绘制功能 .
效果图 :
a53846c3gw1fbkgb7czs6g20bb0c7jyp.jpg
179205b2cc4f579a5c.png

  1. using UnityEngine;
  2. using System.Collections;

  3. [ExecuteInEditMode]
  4. public class ScriptShader005 : MonoBehaviour
  5. {
  6.     // 绑定材质
  7.     public Material mat;

  8.     // 存储获取的 3D 坐标
  9.     Vector3[] worldPos;

  10.     // 存储待绘制的多边形顶点屏幕坐标
  11.     Vector4[] screenPos;

  12.     // 多边形顶点总数
  13.     int maxPointNum = 10;

  14.     // 当前已经获得的顶点数
  15.     int currentPointNum = 0;

  16.     // 传递顶点数量给 Shader
  17.     int pointNum2Shader = 0;

  18.     // 是否处于顶点获取过程
  19.     bool InSelection = true;

  20.     void Start ()
  21.     {
  22.         worldPos = new Vector3[maxPointNum];
  23.         screenPos = new Vector4[maxPointNum];
  24.     }

  25.     void Update ()
  26.     {
  27.         // 传递顶点屏幕位置信息给 shader
  28.         mat.SetVectorArray ("Value", screenPos);

  29.         // 传递顶点数量给 shader
  30.         mat.SetInt ("PointNum", pointNum2Shader);

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

  37.         // 利用鼠标点击来获取位置信息
  38.         if (Input.GetMouseButtonDown (0) && InSelection) {
  39.             if (currentPointNum < maxPointNum) {
  40.                 currentPointNum++;
  41.                 pointNum2Shader++;
  42.                 worldPos [currentPointNum - 1] = hit.point;
  43.                 Vector3 v3 = Camera.main.WorldToScreenPoint (worldPos [currentPointNum - 1]);
  44.                 screenPos [currentPointNum - 1] = new Vector4 (v3.x, v3.y, v3.z, 0);
  45.             } else {
  46.                 // 超过了多边形顶点总数就不能继续获取
  47.                 InSelection = false;
  48.             }
  49.         }

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

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

  63.     }

  64.     // 抓取当前的渲染图像进行处理
  65.     void OnRenderImage (RenderTexture src, RenderTexture dest)
  66.     {
  67.         Graphics.Blit (src, dest, mat);
  68.     }
  69. }
点击此处复制文本

MainCamera 关联的脚本 ScriptShader005.cs :


Shader005.shader :
  1. Shader "Custom/Shader005" {

  2.     Properties {
  3.         // 定义基本属性 , 可以从编辑器中进行设置的变量

  4.     }

  5.     CGINCLUDE

  6.     // 从应用程序传入顶点函数的数据结构定义
  7.     struct a2v {
  8.         float4 vertex : POSITION;
  9.         float2 uv : TEXCOORD0;
  10.     };

  11.     // 从顶点函数传入片元函数的数据结构定义
  12.     struct v2f {
  13.         float2 uv : TEXCOORD0;
  14.         float4 vertex : SV_POSITION;
  15.     };

  16.     // 定义贴图变量
  17.     sampler2D _MainTex;

  18.     // 定义与脚本进行通信的变量 , 10 个顶点
  19.     vector Value[10];

  20.     int PointNum = 0;


  21.     // 计算两点之间的距离的函数
  22.     float Dis(float4 v1, float4 v2) {
  23.         return sqrt(pow(v1.x - v2.x,2) + pow(v1.y - v2.y,2));
  24.     }

  25.     // 绘制线段
  26.     bool DrawLineSegment(float4 p1, float4 p2, float lineWidth, v2f i) {
  27.         float4 center = float4((p1.x + p2.x)/2, (p1.y + p2.y)/2, 0, 0);
  28.         // 计算点到直线的距离
  29.         float d = abs((p2.y - p1.y) * i.vertex.x +
  30.             (p1.x - p2.x) * i.vertex.y +
  31.             p2.x * p1.y - p2.y * p1.x) / sqrt(pow(p2.y - p1.y, 2) + pow(p1.x - p2.x, 2));
  32.         // 小于或者等于线宽的一般的时候就属于直线的范围
  33.         float lineLength = sqrt(pow(p1.x - p2.x, 2) + pow(p1.y - p2.y, 2));
  34.         if(d <= lineWidth/2 && Dis(i.vertex, center) < lineLength/2) {
  35.             return true;
  36.         }
  37.         return false;
  38.     }

  39.     // 绘制多边形
  40.     bool pnpoly(int nvert, float4 vert[10], float testx, float testy) {
  41.         int i, j;
  42.         bool c = false;
  43.         float vertx[10];
  44.         float verty[10];

  45.         for(int n=0; n<nvert; n++) {
  46.             vertx[n] = vert[n].x;
  47.             verty[n] = vert[n].y;
  48.         }

  49.         for(i=0,j=nvert-1; i<nvert; j=i++){
  50.             if(((verty[i] > testy) != (verty[j] > testy)) && (testx < (vertx[j] - vertx[i]) * (testy - verty[i]) / (verty[j] - verty[i]) + vertx[i])) {
  51.                 c = !c;
  52.             }
  53.         }
  54.         return c;
  55.     }

  56.     v2f vert (a2v v) {
  57.         v2f o;
  58.         // 将物体顶点从模型空间转换到摄像机裁剪空间
  59.         // 简写方式 : o.vertex = UnityObjectToClipPos(v.vertex);
  60.         o.vertex = mul(UNITY_MATRIX_MVP, v.vertex);
  61.         // 2D UV 坐标变换 , 简写方式 : o.uv = TRANSFORM_TEX(v.uv, _MainTex);
  62.         // o.uv = v.uv.xy * _MainTex_ST.xy + _MainTex_ST.zw
  63.         return o;
  64.     }

  65.     fixed4 frag(v2f i) : SV_Target {
  66.         // 绘制多边形顶点
  67.         for(int j=0; j< PointNum; j++) {
  68.             if(Dis(i.vertex, Value[j]) < 10/2) {
  69.                 return fixed4(1,0,0,0.5);
  70.             }
  71.         }

  72.         // 绘制多边形的边
  73.         for(int k=0; k<PointNum; k++) {
  74.             if(k == PointNum - 1) {
  75.                 if(DrawLineSegment(Value[k], Value[0], 2,i)) {
  76.                     return fixed4(1, 1, 0, 0.5);
  77.                 }
  78.             } else {
  79.                 if(DrawLineSegment(Value[k], Value[k+1], 2, i)) {
  80.                     return fixed4(1, 1, 0, 0.5);
  81.                 }
  82.             }
  83.         }

  84.         // 填充多边形的里面
  85.         if(pnpoly(PointNum, Value, i.vertex.x, i.vertex.y)) {
  86.             return fixed4(0, 1, 0, 0.3);
  87.         }
  88.         return fixed4(0, 0, 0, 0);
  89.     }

  90.     ENDCG

  91.     SubShader {
  92.         Tags { "RenderType"="Opaque" }
  93.         LOD 200

  94.         Pass {
  95.             // 选取 Alpha 混合方式
  96.             Blend SrcAlpha OneMinusSrcAlpha
  97.             // 在 CGPROGRAM 代码块中写自己的处理过程
  98.             CGPROGRAM
  99.             // 定义顶点函数和片元函数的入口分别为 vert 和 frag
  100.             #pragma vertex vert
  101.             #pragma fragment frag
  102.             #include "UnityCG.cginc"
  103.             ENDCG
  104.         }
  105.     }
  106. }
点击此处复制文本

点到直线距离公式 :


2018-6-22 13:04:38  
 赞 赞 0

使用道具 登录

9个回答,把该问题分享到群,邀请大神一起回答。
2#
感谢大佬分享~~~
回复 收起回复
2018-6-22 15:27:54   回复
 赞 赞 0

使用道具 登录

3#
厉害
回复 收起回复
2018-6-22 18:14:49   回复
 赞 赞 0

使用道具 登录

4#
请指点一下  用途是什么??? 有什么地方可以使用??
回复 收起回复
2018-7-27 21:48:50   回复
 赞 赞 0

使用道具 登录

5#
这个帖子不错,大家快来顶起来!
回复 收起回复
2018-8-7 08:20:05   回复
 赞 赞 0

使用道具 登录

6#
楼主好人啊
回复 收起回复
2018-8-21 16:16:59   回复
 赞 赞 0

使用道具 登录

7#
回复 收起回复
2018-10-30 09:49:39   回复
 赞 赞 0

使用道具 登录

8#
看看资料,好东西!
回复 收起回复
2019-2-13 09:03:09   回复
 赞 赞 0

使用道具 登录

9#
感谢分享
回复 收起回复
2019-2-13 09:40:53   回复
 赞 赞 0

使用道具 登录

10#
好东西
回复 收起回复
2019-2-14 08:48:51   回复
 赞 赞 0

使用道具 登录

CG 游戏行业专业问题

Unity3D技术手机游戏引擎手游引擎
您需要登录后才可以回帖 登录 | 注册

本版积分规则

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