目的
文章解决的目的
1、起初目的是因为XChart的3D图表没有提供美化3D环形圆柱饼图的效果;
目标效果和当下的效果
2、下面是我们UI设计的效果。
3、这是默认画出的效果
4、目前达到的效果
分析
UIShader来实现
1、搜索了很多效果实现方案,其中知乎作者的UIShader效果中找到了一个扫光效果
https://zhuanlan.zhihu.com/p/399478677
2、但是经过测试,扫光效果对XChart生成的Mesh并没有影响,于是我想到了先用贴图叠加的方式写Shader
// Properties
_Tex("Tex", 2D) = "white" {}//叠加颜色
//Pass
sampler2D _Tex;//自己的
float4 _Tex_ST;
//Vert //这是把新贴图的UV映射到UI上
OUT.texcoord[0] = IN.texcoord;
//OUT.texcoord[0] = TRANSFORM_TEX(IN.texcoord, _MainTex);
OUT.texcoord[1] = TRANSFORM_TEX(IN.texcoord.xy, _Tex);
//frag
half4 color = tex2D(_MainTex,IN.texcoord[0]) * IN.color * _Color;//这是原来UI的
fixed4 col = tex2D(_Tex, IN.texcoord[1]);//它的问题在于没有UV
color.rgb*=col.rgb*_Factor;//额外图片叠加
color.a*=(0.1+col.a);//确保遇到透明的部位不是绝对透明
return color;
最终问题所在
3、经过测试发现是因为XChart利用继承MaskableGraphic 重写OnPopulateMesh实现图形绘制时候吧所有UV都设置到了一个点Vector.zero,导致贴图无法顺利叠加。
4、下图分析如何设置顶点UV坐标地址
实施
抄了一遍XChart的实现绘制代码
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
[RequireComponent(typeof(CanvasRenderer))]//MaskableGraphic必须依赖CanvasRenderer
public class MyPainter : MaskableGraphic
{
protected override void OnPopulateMesh(VertexHelper vh)
{
vh.Clear();
var currentVertCount = vh.currentVertCount;
int count = 250;
Vector3 center = new Vector3(0, -80, 0);
float hig = 30;
float startAngle = 0;
float endAngle = 356;
var diff = (endAngle - startAngle) / count;
float inw = 200;
float inh = 130;
float outw = 240;
float outh = 156;
var isLeft = false;
Color startSideColor = new Color(0, 1, 0, 1);
Color endSideColor = new Color(0, 0,1, 1);
Color sideColor = new Color(1, 0, 0, 1);
Color color_w = new Color(1, 1, 1, 1);
var index = 1;
Debug.Log(string.Format("count:{0} ,center{1} ,hig {2} diff{3} inw{4} inh{5} outw{6} outh{7} ",
count, center, hig, diff, inw, inh, outw, outh));
for (int i = 0; i <= count; i++)
{
var angle = startAngle + i * diff;
var rad = (360 - angle) * Mathf.Deg2Rad;
var sin = Mathf.Sin(rad);
var cos = Mathf.Cos(rad);
var ix = center.x + inw * cos;
var iy = center.y + inh * sin;
var ox = center.x + outw * cos;
var oy = center.y + outh * sin;
var ipb = new Vector3(ix, iy);
var ipt = new Vector2(ix, iy + hig);
var opb = new Vector3(ox, oy);
var opt = new Vector2(ox, oy + hig);
isLeft = opb.x < center.x;
if (i == 0)
{
//slice.topPolygons.Add(ipt);
//slice.topPolygons.Add(opt);
//slice.sidePolygons.Add(opb);
//slice.sidePolygons.Add(opt);
vh.AddVert(ipb, Color.red, Vector2.zero); //0
vh.AddVert(ipt, Color.blue, Vector2.zero); // 1
vh.AddVert(opb, Color.green, Vector2.zero); // 2
vh.AddVert(opt, Color.yellow, Vector2.zero); // 3
}
vh.AddVert(ipb, color_w, new Vector2(i,190)); //4 //圆环上部内壁下方 //0 blue
vh.AddVert(ipt, color_w, new Vector2(i, 250)); //5 //圆环上部内壁上方 //0 yellow
vh.AddVert(ipt, color_w, new Vector2(i, 0)); //6 //圆环 整个上面内环 //1
vh.AddVert(opb, color_w, new Vector2(i, 75)); //7 圆环 外壁下环 //2 red
vh.AddVert(opt, color_w, new Vector2(i, 135)); //8//圆环 外壁上环 //2 green
vh.AddVert(opt, color_w, new Vector2(i, 60)); //9 //圆环 整个上面外环 //1
index = currentVertCount + (i * 6) + 4;
if (i == 0)
{
if (isLeft &&true /*startSide*/)
{
//slice.startOrEndPolygons.Add(ipb);
//slice.startOrEndPolygons.Add(ipt);
//slice.startOrEndPolygons.Add(opt);
//slice.startOrEndPolygons.Add(opb);
vh.AddTriangle(currentVertCount, currentVertCount + 1, currentVertCount + 3); // startSide: 0 1 3
vh.AddTriangle(currentVertCount, currentVertCount + 3, currentVertCount + 2); //startSide: 1 5 6
}
}
else
{
//inside
if (angle >= 180 && angle <= 360)
{
vh.AddTriangle(index, index + 1, index - 5); // inside: 10 11 5
vh.AddTriangle(index, index - 5, index - 6); // inside: 10 5 4
}
//top
vh.AddTriangle(index - 4, index - 1, index + 2); // top: 6 9 12
vh.AddTriangle(index + 2, index - 1, index + 5); // top: 12 9 15
// //outside
if (angle >= 0 && angle <= 180)
{
vh.AddTriangle(index + 3, index + 4, index - 2); // outside: 13 14 8
vh.AddTriangle(index + 3, index - 2, index - 3); // outside: 13 8 7
}
}
if (i == count)
{
//slice.topPolygons.Add(opt);
//slice.topPolygons.Add(ipt);
//slice.sidePolygons.Add(opt);
//slice.sidePolygons.Add(opb);
if (!isLeft &&true /*endSide*/)
{
//slice.startOrEndPolygons.Add(ipb);
//slice.startOrEndPolygons.Add(ipt);
//slice.startOrEndPolygons.Add(opt);
//slice.startOrEndPolygons.Add(opb);
vh.AddVert(ipb, endSideColor, Vector2.zero); //22 //截面积 左下角
vh.AddVert(ipt, endSideColor, Vector2.zero); //23 //截面积 左上角
vh.AddVert(opb, endSideColor, Vector2.zero); //24 //截面积 右下角
vh.AddVert(opt, endSideColor, Vector2.zero); //25 //截面积 右上角
vh.AddTriangle(index + 6, index + 7, index + 9); // endSide: 22 23 25 //一个三角形顺时针画
vh.AddTriangle(index + 6, index + 9, index + 8); // endSide: 22 25 24 //都是顺时针画 好像是优先画和之前画的相邻的边
}
}
}
}
}
Shader源代码
Shader "UGUI/UIShaderTexAdd"
{
Properties
{
[PerRendererData] _MainTex("MainTex", 2D) = "white" {}
_Color("Tint", Color) = (1,1,1,1)
[Header(Stencil)]
_StencilComp("Stencil Comparison", Float) = 8
_Stencil("Stencil ID", Float) = 0
_StencilOp("Stencil Operation", Float) = 0
_StencilWriteMask("Stencil Write Mask", Float) = 255
_StencilReadMask("Stencil Read Mask", Float) = 255
_Factor("_Factor",Float)=1
_Width("宽度",float)=1// 6 7 消融和扫光都用
[HDR]_Brightness("扫光颜色",Color)=(1,1,1,1) //扫光用
_Softness("柔和度",float)=1//扫光用
_Gloss("光泽度",float)=1//扫光用
_Rotation("旋转度",float)=15//扫光用
_Tex("Tex", 2D) = "white" {}//叠加颜色
_ColorMask("Color Mask", Float) = 15 //_ColorMask:设置颜色通道写入遮罩。写入 ColorMask 0 可关闭对所有颜色通道的渲染。
[Toggle(UNITY_UI_ALPHACLIP)] _UseUIAlphaClip("Use Alpha Clip", Float) = 0 //_UseUIAlphaClip是否用clip来进行不显示 。[Toggle(UNITY_UI_ALPHACLIP)], 如果Toggle是true , 则自动#define UNITY_UI_ALPHACLIP ,可以用#ifdef进行预处理。
}
SubShader
{
Tags
{
"Queue" = "Transparent"
"IgnoreProjector" = "True"
"RenderType" = "Transparent"
"PreviewType" = "Plane"
"CanUseSpriteAtlas" = "True"
}
Stencil
{
Ref[_Stencil] //_Stencil 模板测试相关属性,Unity会自动修改参数,UI可以使用模板测试进行不显示。
Comp[_StencilComp]
Pass[_StencilOp]
ReadMask[_StencilReadMask]
WriteMask[_StencilWriteMask]
}
Cull Off //关闭剔除
Lighting Off
ZWrite Off //关闭深度写人
ZTest[unity_GUIZTestMode]
Blend SrcAlpha OneMinusSrcAlpha
ColorMask[_ColorMask]
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile __ UNITY_UI_ALPHACLIP
#include "UnityCG.cginc"
#include "UnityUI.cginc"
struct appdata_t
{
float4 vertex : POSITION;
float4 color : COLOR;
float2 texcoord : TEXCOORD0;
UNITY_VERTEX_INPUT_INSTANCE_ID
};
struct v2f
{
float4 vertex : SV_POSITION;
float4 color : COLOR;
half2 texcoord[2] : TEXCOORD0;
UNITY_VERTEX_OUTPUT_STEREO
};
sampler2D _MainTex;
float4 _MainTex_TexelSize;
float4 _Color;
float _Factor;
float _Width;//消融 扫光
float4 _Brightness;//扫光用
float _Softness;//扫光
float _Gloss;//扫光用
float _Rotation;//扫光用
sampler2D _Tex;//自己的
float4 _Tex_ST;
v2f vert(appdata_t IN)
{
v2f OUT;
UNITY_SETUP_INSTANCE_ID(IN);
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(OUT);
OUT.vertex = UnityObjectToClipPos(IN.vertex);
OUT.color = IN.color;
OUT.texcoord[0] = IN.texcoord;
//OUT.texcoord[0] = TRANSFORM_TEX(IN.texcoord, _MainTex);
OUT.texcoord[1] = TRANSFORM_TEX(IN.texcoord.xy, _Tex);
return OUT;
}
//=================UV旋转算法
float2x2 Rotation(half Angle)
{
Angle=UNITY_PI/180*Angle;
half cosAngle = cos(Angle);
half sinAngle = sin(Angle);
float2x2 rot = float2x2(cosAngle,-sinAngle,sinAngle,cosAngle);
return rot;
}
fixed4 frag(v2f IN) : SV_Target
{
half4 color = tex2D(_MainTex,IN.texcoord[0]) * IN.color * _Color;
fixed4 col = tex2D(_Tex, IN.texcoord[1]);//它的问题在于没有UV
//=========================================================
//=================7 扫描光 可以用贴图或代码得到扫光线,叠加在纹理贴图上,用_Factor控制扫光位置。
float2 shinyUv=mul(Rotation(_Rotation),IN.texcoord[0]-0.5);
fixed nomalizedPos = shinyUv.x;
half location = _Factor * 2 - 0.5;
half normalized = 1 - saturate(abs((nomalizedPos - location) / _Width));
half shinePower = smoothstep(0, _Softness, normalized);
half3 reflectColor = lerp(fixed3(1,1,1), color.rgb * 7, _Gloss);
color.rgb += color.a * (shinePower / 2) * _Brightness * reflectColor*_Color;
//=========================================================
color.rgb*=col.rgb*_Factor;//额外图片叠加
color.a*=(0.1+col.a);
//更多 https://zhuanlan.zhihu.com/p/399478677
return color;
}
ENDCG
}
}
}
//加入颜色叠加
fixed4 ifFlag= step(color,fixed4(0.5,0.5,0.5,0.5));//叠加
fixed4 C=ifFlag*color*blCol*2+(1-ifFlag)*(1-(1-color)*(1-blCol)*2);//叠加
return C;