Unity&Shader基础篇-绘制网格+圆盘

来源:互联网 发布:小众软件安卓 编辑:程序博客网 时间:2024/05/17 01:54

一、前言

尊重原创,转载请注明出处凯尔八阿哥专栏

上一章点击打开链接中已经画出了一个棋盘网格,首先来完善一下这个画网格的Shader,添加属性,属性包括网格的线的宽度,网格的颜色等。代码如下:

Shader "Unlit/Chapter2-2"{Properties{_backgroundColor("面板背景色",Color) = (1.0,1.0,1.0,1.0)_axesColor("坐标轴的颜色",Color) = (0.0,0.0,1.0)_gridColor("网格的颜色",Color) = (0.5,0.5,0.5)_tickWidth("网格的密集程度",Range(0.1,1))=0.1_gridWidth("网格的宽度",Range(0.0001,0.01))=0.008_axesXWidth("x轴的宽度",Range(0.0001,0.01))=0.006_axesYWidth("y轴的宽度",Range(0.0001,0.01))=0.007}SubShader{//去掉遮挡和深度缓冲Cull OffZWrite Off//开启深度测试ZTest AlwaysCGINCLUDE//添加一个计算方法float mod(float a,float b){//floor(x)方法是Cg语言内置的方法,返回小于x的最大的整数return a - b*floor(a / b);}ENDCGPass{CGPROGRAM//敲代码的时候要注意:“CGPROGRAM”和“#pragma...”中的拼写不同,真不知道“pragma”是什么单词#pragma vertex vert#pragma fragment frag#include "UnityCG.cginc"uniform float4 _backgroundColor;uniform float4 _axesColor;uniform float4 _gridColor;uniform float _tickWidth;uniform float _gridWidth;uniform float _axesXWidth;uniform float _axesYWidth;struct appdata{float4 vertex:POSITION;float2 uv:TEXCOORD0;};struct v2f{float2 uv:TEXCOORD0;float4 vertex:SV_POSITION;};v2f vert(appdata v){v2f o;o.vertex = mul(UNITY_MATRIX_MVP,v.vertex);o.uv = v.uv;return o;}fixed4 frag(v2f i) :SV_Target{//将坐标的中心从左下角移动到网格的中心fixed2 r = 2.0*fixed2(i.uv.x - 0.5,i.uv.y - 0.5);fixed3 backgroundColor = _backgroundColor.xyz;fixed3 axesColor = _axesColor.xyz;fixed3 gridColor = _gridColor.xyz;fixed3 pixel = backgroundColor;//定义网格的的宽度const float tickWidth = _tickWidth;if (mod(r.x, tickWidth) < _gridWidth){pixel = gridColor;}if (mod(r.y, tickWidth) < _gridWidth){pixel = gridColor;}//画两个坐标轴if (abs(r.x) < _axesXWidth){pixel = axesColor;}if (abs(r.y) < _axesYWidth){pixel = axesColor;}return fixed4(pixel, 1.0);}ENDCG}}}

通过以下代码将网格的轴心移动到中心位置

fixed2 r = 2.0*fixed2(i.uv.x - 0.5,i.uv.y - 0.5);
效果图如图所示:


二、画一个圆盘

原理:圆的方程为(x-a)^2+(y-b)^2=r^2,根据方程即可以添加如下方法来计算预定圆盘的颜色值信息

//添加第二个计算方法,根据半径,原点、原点的偏移量r和颜色来绘制圆盘fixed3 disk(fixed2 r,fixed2 center,fixed radius,fixed3 color,fixed3 pixel){fixed3 col=pixel;if (length(r - center) < radius){col = color;}return col;}
效果如图所示:

完整的代码如下:
Shader "Unlit/Chapter3-2"{Properties{_backgroundColor("面板背景色",Color) = (1.0,1.0,1.0,1.0)_col1("圆盘1的颜色",Color) = (0.216, 0.471, 0.698) // blue_col2("圆盘2的颜色",Color) = (1.00, 0.329, 0.298) // red_col3("圆盘3的颜色",Color) = (0.867, 0.910, 0.247) // yellow_center2X("第三个圆盘的原点X的位置",Range(0.0,1.0)) = 0.9_center2Y("第三个圆盘的原点Y的位置",Range(0.0,1.0)) = -0.4_radius1("第一个圆盘的半径",Range(0.1,1)) = 0.8_radius2("第二个圆盘的半径", Range(0.1, 1)) = 0.3_radius3("第三个圆盘的半径", Range(0.1, 1)) = 0.6}SubShader{//去掉遮挡和深度缓冲Cull OffZWrite Off//开启深度测试ZTest AlwaysCGINCLUDE//添加一个计算方法float mod(float a,float b){//floor(x)方法是Cg语言内置的方法,返回小于x的最大的整数return a - b*floor(a / b);}//添加第二个计算方法,根据半径,原点、原点的偏移量r和颜色来绘制圆盘fixed3 disk(fixed2 r,fixed2 center,fixed radius,fixed3 color,fixed3 pixel){fixed3 col=pixel;if (length(r - center) < radius){col = color;}return col;}ENDCGPass{CGPROGRAM//敲代码的时候要注意:“CGPROGRAM”和“#pragma...”中的拼写不同,真不知道“pragma”是什么单词#pragma vertex vert#pragma fragment frag#include "UnityCG.cginc"uniform float4 _backgroundColor;uniform float4 _col1;uniform float4 _col2;uniform float4 _col3;uniform float _radius1;uniform float _radius2;uniform float _radius3;uniform float _center2X;uniform float _center2Y;struct appdata{float4 vertex:POSITION;float2 uv:TEXCOORD0;};struct v2f{float2 uv:TEXCOORD0;float4 vertex:SV_POSITION;};v2f vert(appdata v){v2f o;o.vertex = mul(UNITY_MATRIX_MVP,v.vertex);o.uv = v.uv;return o;}fixed4 frag(v2f i) :SV_Target{float2 r = 2.0*(i.uv - 0.5);float aspectRatio = _ScreenParams.x / _ScreenParams.y;r.x *= aspectRatio;fixed3 backgroundColor = _backgroundColor.xyz;fixed3 col1 = _col1.xyz;fixed3 col2 = _col2.xyz;fixed3 col3 = _col3.xyz;fixed3 pixel = _backgroundColor;//画第一个圆盘pixel = disk(r,fixed2(0.1,0.3), _radius1,col3,pixel);//画第二个圆盘pixel= disk(r, fixed2(_center2X, _center2Y), _radius2, col2, pixel);//画第三个圆盘pixel= disk(r, fixed2(-0.8, 0.6), _radius3, col1, pixel);return fixed4(pixel, 1.0);}ENDCG}}}

三、在网格中绘制圆盘

1、根据2中绘制圆盘的原理,只要将a和b通过C#代码传递参数给Shader来控制即可,这个参数就是鼠标点击时候的坐标位置。当然这个位置要经过计算才能应用到网格坐标中,网格坐标的范围为-1~1,通过代码来转换:
void Update () {        if(Input.GetMouseButtonDown(0))        {            vec_mouseBtnPos = Input.mousePosition;            //将鼠标的位置除以屏幕参数得到范围为0~1的坐标范围            vec_mouseBtnPos = new Vector2(vec_mouseBtnPos.x / Screen.width, vec_mouseBtnPos.y / Screen.height);            //设定坐标原点为中点            vec_mouseBtnPos -= new Vector2(0.5f,0.5f);            vec_mouseBtnPos *= 2;            vec_mouseBtnPos.y = -vec_mouseBtnPos.y;}
基本上和Shader代码中的计算方法相同,得到的效果图如图所示:
2、将点固定在网格交叉点上
看到这里,各位看官你会想到我要做一个什么?哈哈,没错就是一个五子棋,用Shader来写一个五子棋,想想都很激动。好,回归正题,有了上一步的基础,现在我们只需要将鼠标点击的点和附近网格交叉点之间做一个判断,看看是否达到了阈值范围内,如果是,就让这个红色点固定绘制在这个交叉点上。完整的代码如下:
using UnityEngine;using System.Collections;using System.Collections.Generic;/// <summary>/// 这个脚本实现的点击单个点的效果/// </summary>public class GameControl : MonoBehaviour {    public Material mat;    //设置点中的最小误差    public float clickMinError;    //网格点的坐标集    private List<Vector2> list_gridIntersectionPos = new List<Vector2>();    //网格点的数量    private int gridIntersectionNums;    private float gridSpace;    private Vector2 vec_mouseBtnPos;    // Use this for initialization    void Start()    {        gridSpace = mat.GetFloat("_tickWidth");        //单个坐标轴上网格点的数量等于横轴坐标间距除以网格间距        gridIntersectionNums = (int)Mathf.Floor(1.0f / gridSpace); //这里不能只用强制类型转换,如果使用强制类型转换会丢失数据,比如1.0/0.1最后的结果是9        for (int i = -gridIntersectionNums; i <= gridIntersectionNums; i++)        {            float x = gridSpace * i;            for (int j = -gridIntersectionNums; j <= gridIntersectionNums; j++)            {                float y = gridSpace * j;                list_gridIntersectionPos.Add(new Vector2(x, y));            }        }    }// Update is called once per framevoid Update () {        if(Input.GetMouseButtonDown(0))        {            vec_mouseBtnPos = Input.mousePosition;            //将鼠标的位置除以屏幕参数得到范围为0~1的坐标范围            vec_mouseBtnPos = new Vector2(vec_mouseBtnPos.x / Screen.width, vec_mouseBtnPos.y / Screen.height);            //设定坐标原点为中点            vec_mouseBtnPos -= new Vector2(0.5f,0.5f);            vec_mouseBtnPos *= 2;            vec_mouseBtnPos.y = -vec_mouseBtnPos.y;          /*  mat.SetFloat("_MouseBtnPosX", vec_mouseBtnPos.x);            mat.SetFloat("_MouseBtnPosY", vec_mouseBtnPos.y);*/               //如果点中了网格的交叉点出就显示圆点               int index = CheckClikedIntersection(vec_mouseBtnPos);               if (index != -1)               {                   //将准确的网格点的位置赋值给vec_mouseBtnPos                   vec_mouseBtnPos = list_gridIntersectionPos[index];                   mat.SetFloat("_MouseBtnPosX", vec_mouseBtnPos.x);                   mat.SetFloat("_MouseBtnPosY", vec_mouseBtnPos.y);                 }               Debug.Log("x:" + vec_mouseBtnPos.x + "y:" + vec_mouseBtnPos.y);        }}    /// <summary>    /// 判断鼠标点中的地方是否在网格的交叉点处    /// </summary>    /// <param name="vec2"></param>    /// <returns></returns>    private int CheckClikedIntersection(Vector2 vec2)    {        int clickIndex = -1;        for (int i = 0; i < list_gridIntersectionPos.Count; i++)        {            float errorx = Mathf.Abs(vec2.x- list_gridIntersectionPos[i].x);            float errory = Mathf.Abs(vec2.y - list_gridIntersectionPos[i].y);            //如果误差的值小于预设的值则判定点中了            float error = Mathf.Sqrt(errorx * errorx + errory * errory);            if(error<clickMinError)            {                clickIndex = i;                break;            }        }        return clickIndex;    }}
这里使用了“CheckClikedIntersection”方法来进行判断和校正,并设置了一个最小误差“clickMinError”。
效果图如图所示:
3、完整的Shader代码如下:
Shader "Unlit/Backgammon"{Properties{_backgroundColor("面板背景色",Color) = (1.0,1.0,1.0,1.0)_axesColor("坐标轴的颜色",Color) = (0.0,0.0,1.0)_gridColor("网格的颜色",Color) = (0.5,0.5,0.5)_tickWidth("网格的间距",Range(0.1,1)) = 0.1_gridWidth("网格的宽度",Range(0.0001,0.01)) = 0.008_axesXWidth("x轴的宽度",Range(0.0001,0.01)) = 0.006_axesYWidth("y轴的宽度",Range(0.0001,0.01)) = 0.007_MouseBtnPosX("鼠标点击的X方向位置",float) = 0.0_MouseBtnPosY("鼠标点击的Y方向位置",float) = 0.0_MouseBtnRadius("鼠标点中位置圆盘的半径",float) = 0.001_MouseDownColor("鼠标点中的颜色",Color) = (1.00, 0.329, 0.298,1.0)}SubShader{//去掉遮挡和深度缓冲Cull OffZWrite Off//开启深度测试ZTest AlwaysCGINCLUDE//添加一个计算方法float mod(float a,float b){//floor(x)方法是Cg语言内置的方法,返回小于x的最大的整数return a - b*floor(a / b);}//添加第二个计算方法,根据半径,原点和颜色来绘制圆盘fixed3 disk(fixed2 r,fixed2 center,fixed radius,fixed3 color,fixed3 pixel){fixed3 col = pixel;if (length(r - center) < radius){col = color;}return col;}ENDCGPass{CGPROGRAM//敲代码的时候要注意:“CGPROGRAM”和“#pragma...”中的拼写不同,真不知道“pragma”是什么单词#pragma vertex vert#pragma fragment frag#include "UnityCG.cginc"uniform float4 _backgroundColor;uniform float4 _axesColor;uniform float4 _gridColor;uniform float _tickWidth;uniform float _gridWidth;uniform float _axesXWidth;uniform float _axesYWidth;uniform float _MouseBtnPosX;uniform float _MouseBtnPosY;uniform float _MouseBtnRadius;uniform float4 _MouseDownColor;struct appdata{float4 vertex:POSITION;float2 uv:TEXCOORD0;};struct v2f{float2 uv:TEXCOORD0;float4 vertex:SV_POSITION;};v2f vert(appdata v){v2f o;o.vertex = mul(UNITY_MATRIX_MVP,v.vertex);o.uv = v.uv;return o;}fixed4 frag(v2f i) :SV_Target{//将坐标的中心从左下角移动到网格的中心float2 r = 2.0*(i.uv - 0.5);float aspectRatio = _ScreenParams.x / _ScreenParams.y;//r.x *= aspectRatio;fixed3 backgroundColor = _backgroundColor.xyz;fixed3 axesColor = _axesColor.xyz;fixed3 gridColor = _gridColor.xyz;fixed3 pixel = backgroundColor;//定义网格的的间距const float tickWidth = _tickWidth;if (mod(r.x, tickWidth) < _gridWidth){pixel = gridColor;}if (mod(r.y, tickWidth) < _gridWidth){pixel = gridColor;}//画两个坐标轴if (abs(r.x) < _axesXWidth){pixel = axesColor;}if (abs(r.y) < _axesYWidth){pixel = axesColor;}//画一个点pixel = disk(r, fixed2(_MouseBtnPosX, _MouseBtnPosY), _MouseBtnRadius, _MouseDownColor, pixel);return fixed4(pixel, 1.0);}ENDCG}}}
代码中添加了用来接收C#脚本传递的鼠标点击的位置
“uniform float _MouseBtnPosX;uniform float_MouseBtnPosY;”
到了这一步还是不能说可以完整的做出一个五子棋,因为现在只能画一个点,当然你可以通过在Shader代码中增加画点的代码来实现画多个点,然而并不能达到我们理想的状况。下一节将会给出一个技巧来实现C#脚本怎么向Shader中传数组来实现Shader代码画多个点。
附上本节的工程下载地址百度网盘工程下载地址,使用的是Unity5.3.3版本



1 0