想要在Unity2D中实现一个漂亮的圆环,但是除了前面做过的各种笔记和自学,啥都不会,
但是成功摸索出来,在这里记录一下理解流程。
源代码和使用方式在最下面。
2D圆环的最简单实现
圆环最简单的实现就是自己画一个背景色透明的圆环:
(下面是一张图片,不信你把浏览器背景色调一下)
然后直接往场景里一放就行:
当然这种方式很不行,拉伸大了之后模糊感太大,很难看,而且不易控制大小:
使用Shader实现
2DShader的简单探索(最终代码在最后)
首先2D中用的都是Sprite
做渲染,所有显然不适合直接用Mesh顶点作图
的方案,
当然如果用Mesh
的话也有现成的方案:
https://blog.csdn.net/liuzonrze/article/details/83280463
但只要涉及到渲染肯定就用到Shader
,
所以先看看Sprite
中怎么用上Shader
吧。
首先在场景中创建一个新的2D正方形
。
新建一个Shader和材质作用到它上面,编写Shader代码进行探索。
这里就是用到Shader
的一种主要调试方式:将值直接当作颜色输出来
。
下面这一段代码做的工作就是,
试图将模型顶点在裁剪空间中的坐标输出,
结果是输出全白:
Shader "Custom/CircleEdgeShader"
{
Properties{
_MainTex ("Main Tex", 2D) = "white" {}
}
SubShader
{
// No culling or depth
Cull Off ZWrite Off ZTest Always
// 开启透明度
Blend SrcAlpha OneMinusSrcAlpha
// 设置渲染队列
Tags { "Queue"="Transparent" "RenderType"="Opaque" }
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
sampler2D _MainTex;
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 = UnityObjectToClipPos(v.vertex);
o.uv = v.uv;
return o;
}
// 片元着色
fixed4 frag (v2f i) : SV_Target
{
return i.vertex;
}
ENDCG
}
}
}
那看来从模型顶点入手是不行了,
毕竟二维精灵也没听说过顶点
的说法,
那就是原理上与生成Mesh顶点并调整顶点颜色完全不同。
下面看看从程序导入着色器的另一个参数uv坐标
,
将片元着色器代码调整如下:
fixed4 frag (v2f i) : SV_Target
{
return fixed4(i.uv.x, i.uv.y, 0, 1);
}
输出结果如下:
虽然是混乱的色彩,但我很开心,
这说明决定2维精灵各个像素颜色的变量就在其中。
我们可以通过取色器对各个像素取色,得出uv坐标的分布规律:
左上角是绿色RGB(0, 1, 0), 右上角是黄色RGB(1, 1, 0),
中间显然就是RGB(0.5, 0.5, 0)。
知道uv的分布规律后,我们直接就可以写出代码来画我们需要的一个高清圆弧了!
将片元着色器代码修改如下:
fixed4 frag (v2f i) : SV_Target
{
float disToCenter = (i.uv.x-0.5)*(i.uv.x-0.5) + (i.uv.y-0.5)*(i.uv.y-0.5);
if( 0.5*0.5 > disToCenter && disToCenter > 0.45*0.45 )
return fixed4(1, 1, 1, 1);
else
return fixed4(1, 1, 1, 0);
}
不管放多大,这种像素级的色彩定位是不会出现模糊的现象的:
接下来就把代码精进,提供参数接口给C#脚本即可
源代码
主要是两个参数:
一是Color
,用来确定圆环颜色,
二是CircleEdge
,用来确定圆环宽度占半径的比例(0~1)
Shader "Custom/CircleEdgeShader"
{
Properties{
_MainTex ("Main Tex", 2D) = "white" {}
_ColorTint ("Color", Color) = (1, 1, 1, 1)
_CircleEdge ("CircleEdge", float) = 0.1
}
SubShader
{
// No culling or depth
Cull Off ZWrite Off ZTest Always
// 开启透明度
Blend SrcAlpha OneMinusSrcAlpha
// 设置渲染队列
Tags { "Queue"="Transparent" "RenderType"="Opaque" }
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
sampler2D _MainTex;
fixed4 _ColorTint;
float _CircleEdge;
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 = UnityObjectToClipPos(v.vertex);
o.uv = v.uv;
return o;
}
// 片元着色
fixed4 frag (v2f i) : SV_Target
{
float _CircleEdgeInFrag = (1 - _CircleEdge)*0.5;
float disToCenter = (i.uv.x-0.5)*(i.uv.x-0.5) + (i.uv.y-0.5)*(i.uv.y-0.5);
if( 0.5*0.5 > disToCenter && disToCenter > _CircleEdgeInFrag*_CircleEdgeInFrag )
return fixed4(_ColorTint.xyz, 1);
else
return fixed4(_ColorTint.xyz, 0);
}
ENDCG
}
}
}
使用注意
圆环整体的大小通过Transform\Scale
进行控制,
Scale为1时,可以在场景中看到圆环外环半径刚好是网格线的一半。
例
让圆环缩放时,宽度保持不变的C#脚本如下,主要是控制Scale
和CircleEdge
的比例关系。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class FixedEdgeWidth : MonoBehaviour
{
public Material circleEdgeMaterial;
float rate;
void Start()
{
rate = circleEdgeMaterial.GetFloat("_CircleEdge");
rate *= transform.localScale.x;
}
void Update()
{
circleEdgeMaterial.SetFloat("_CircleEdge", rate / transform.localScale.x);
if(Input.GetKeyDown(KeyCode.UpArrow)){
transform.localScale = new Vector3(transform.localScale.x+0.5f, transform.localScale.y+0.5f, transform.localScale.z);
}
if(Input.GetKeyDown(KeyCode.DownArrow)){
transform.localScale = new Vector3(transform.localScale.x-0.5f, transform.localScale.y-0.5f, transform.localScale.z);
}
}
}