前言

在逛论坛的时候偶然发现有人在问动态低多边形(Lowpoly)是如何实现的,因为经常编写UGUI拓展对顶点操作较为熟悉的我立马就想到利用继承UnityEngine.Graphic,重写OnPopulateMesh方法绘制顶点、赋值颜色,在Update方法中计算顶点位置使得顶点在进行连续且不无断点的路径上产生位移即可,当然这只是初步的设想,这种方式能实现动态低边效果,但是不同的三角面展现的高光效果在UI也不是一件简单的事情,所以我们摒弃2DUGUI的方式,使用网格编程和Shader(着色器)来实现这一效果。

本文合适对向量运算和Shader有一定了解的人员,当然你也可以直接使用成果。

实现效果

网格使用代码生成,使得我们有更多可配置的余地

  • 变种一

  • 变种二

  • Inspecetor

主要内容

  • 绘制网格
  • 网格持久化
  • 编写Shader
  • 网格动态化

详细设计

Unity中网格要可见还需要两个额外的好搭档Material和Shader,所以我们先创建好三个必备文件Lowpoly.cs(C#代码绘制网格)、Lowpoly.material(材质球)和LowpolyShader(着色器,用于给材质球着色)。

  • 先补一个最终想要实现的大致效果

嗯,请脑补掉企鹅大厂的Logo : )

绘制网格

首先通过观察图片我们需要得知大致的绘制思路,大概如下几点:

  • 绘制一个NxM的网格
  • 改变网格顶点的位置,实现网格错乱
  • 重点:每个三角形颜色一致,并没有顶点到顶点的颜色过度
  • 要计算法线,来实现不同角度的反射不同程度的来亮度
  • 将改变网格的算法移动到Update函数中,实现动画效果
绘制原理

我们就根据上述已经总结好的几点思路来逐步讲解原理

  • 上一个NxM的效果图

  • 绘制NxM的网格

    1. 在Unity中网格存储在MeshFilter组件的mesh属性里,所以我们在将绘制好的网格存入MeshFilter.mesh属性即可。
    2. 要绘制mesh网格需要向mesh网格中写入顶点位置,贴图uv值(如果你不贴贴图的话也可以不赋值),三角面对应顶点的序号,顶点法线(如果不需要模型的细节也不可以不赋值法线,稍后我们再谈法线的问题),顶点切线(用于处理细节),这里暂不考虑tangent。
    3. 着色器的会为每一个顶点进行光照计算并并给顶点赋值颜色,如果A点到B点是三角面上的一条边,那么A点到B点的中间的颜色为A点的颜色到B点颜色的过渡色。但是因为我们要实现的效果中,三角面的颜色不存在过渡色,也就意味着着色器处理过后的三个顶点的颜色值一样,也就要求三个顶点的顶点法线是相同的,所以在处理网格法线时,我们需要将构成三角面的网格的三个顶点法线设置为同一个向量,同时这样也就意味着看似在一起的顶点也不能够共用,共用同一个顶点就会导致相邻两个三角面的法向量相同,从而导致所有顶点法向量相同导致无法曾现层次感。所以我们要为每一个三角面创建三个顶点。又因为每个四边形都需要利用两个三角面来组合绘制,因此我们可计算出绘制动态低多边形所需要的三角面个数(TrianglesCount)顶点个数(VerticesCount)UV坐标(UVsCount)法线向量个数(NormalsCount):
TrianglesCount = N*M*2VerticesCount = TrianglesCount*3UVsCount = VerticesCountNormalsCount = VerticesCount

有了以上大致的了解我们来计算顶点,顶点计算代码如下,为方便计算我们先计算一个矩形中右下角的三角形,再计算左上角的三角形

Mesh mesh = new Mesh();
mesh.name = "LowPoly";
size = new Vector3(1,1,0);
origin = new Vector3 (-size.x / 2.0f,-size.y/2.0f,0);
perX = size.x / XCount;
perY = size.y / YCount;// 右下角三角面
for (int i = 0; i <= YCount; i++)
{for (int j = 0; j <= XCount; j++){if (j.Equals (XCount))continue;if (i.Equals (YCount))continue;m_vertices.Add (PosNormal (j,i));m_vertices.Add(PosNormal(j+1,i));m_vertices.Add(PosNormal(j+1,i+1));m_uvs.Add ( new Vector2( j * perX, i * perY));m_uvs.Add ( new Vector2( (j+1) * perX, i * perY));m_uvs.Add ( new Vector2( (j+1) * perX, (i+1) * perY));}
}
// 左下角三角面
for (int i = 0; i <= YCount; i++)
{for (int j = 0; j <= XCount; j++){if (j.Equals (XCount))continue;if (i.Equals (YCount))continue;m_vertices.Add (PosNormal (j,i));m_vertices.Add(PosNormal(j+1,i+1));m_vertices.Add(PosNormal(j,i+1));m_uvs.Add ( new Vector2( j * perX, i * perY));m_uvs.Add ( new Vector2( (j+1) * perX, (i+1) * perY));m_uvs.Add ( new Vector2( (j) * perX, (i+1) * perY));}
}

以上干货部分没有看懂的同学也不着急,我从外网找到一篇很有价值的网格入门教程,我会抽空翻译出来,详细原理看那篇文章,链接我也会附在这里。原文飞机票:http://catlikecoding.com/unity/tutorials/procedural-grid/

  • 赋值三角面序号

赋值三角面序号的过程就是告诉着色器每个三角面的三个顶点对应传入顶点数组中的哪个顶点,所以每个三角面都要指定三个顶点坐标位置。

这里需要注意顶点的渲染顺序,序号按逆时针顺序传入相机正面可见,顺时针传入相机逆面可见。

// 指定右下角三角面序号
m_triangles = new int[XCount * YCount * 6];
for (int i = 0 ,count = 0 ,total = 0 ; i < m_triangles.Length / 2 ; count ++ )
{if (((count + 1) % (XCount + 1)).Equals (0))continue;m_triangles[i] = total + 1;m_triangles[i + 1] = total;m_triangles[i + 2] = total + 2; i += 3;total += 3;
}
// 指定左上角的三角面序号
int startIndex = m_vertices.Count / 2;
for (int i = m_triangles.Length / 2, count = 0 ,total = m_vertices.Count / 2; i < m_triangles.Length; count++)
{if (((count + 1) % (XCount + 1)).Equals (0))continue;m_triangles [i] = total + 2;m_triangles [i + 1] = total + 1;m_triangles [i + 2] = total ;i += 3;total += 3;
}
  • 计算法向量

    1. 严格上来说,一个顶点不可能有法线。但当使用Phong或Gouraud着色过程进行光照计算时,点法线提供了模拟光滑表面的一种方式。想象一个人体的多边形网格模型:这个模型只是一些多边形。但是这个网格模型能模拟一个人体。如果一个多边形里面的所有像素都使用相同的颜色着色,那么这个多边形看起来会非常平坦;但是通过使用点法线,我们能够对三角形的不同顶点应用不同的光照,这样就能够产生比较光滑的显示效果。(该段内容参考至:生成点法线(Generating Vertex Normals)) 又因为我们的效果中要求三个顶点的法向量相同,所以我们就没必要去计算法向量,直接用面法向量代替即可。如果你纠结顶点法向量的计算方法,点击使用这张飞机票 关于点法线向量的计算

    2. 面法向量如何计算呢?组成一个面的两个向量进行叉积就是法向量,不明白的同学也可以看看关于点法线向量的计算。

按照下图两个向量的叉乘即可求出三角面的面法向量

计算面法向量参考图:

m_normals = new Vector3[m_vertices.Count];
for (int i = 0; i < m_normals.Length; i+=3 )
{// 计算三角面上的两条向量Vector3 v1 = m_vertices[i + 1] - m_vertices[i];Vector3 v2 = m_vertices[i + 2] - m_vertices[i];// 叉乘获取面法向量Vector3 argNormal = -Vector3.Cross(v1,v2).normalized;// 赋值这三个顶点的法向量m_normals[i] = argNormal;m_normals[i + 1] = argNormal;m_normals[i + 2] = argNormal;
}

网格持久化

相信大家也已经发现,编写好的网格只能在程序运行的时候才能看到,那么我们应该如何把它保存下来呢,这里需要用到编辑器拓展方法,在编辑器拓展方法中调用我们上面已经编写好的网格生成算法即可把网格记录到MeshFilter组件中。

using UnityEditor;
using UnityEngine;// 网格持久化
public class MeshPresistence
{// 使用此特性在工具栏生成按钮以调用改方法[MenuItem("Tools/Mesh/Presistence")]public static void Presistence(){// 获取当前选中的游戏物体GameObject selectedGo = Selection.activeGameObject;MeshFilter meshFilter = selectedGo.GetComponent<MeshFilter>();// 调用网格生成算法并记录持久化meshFilter.mesh = selectedGo.GetComponent<LowPoly>().GenerateLowPoly();}
}

然后再工具栏里找到我们创建的按钮,点击!然后双击游戏物体MeshFilter组件上Mesh就可以在Unity的右下角明确看到当前创建的网格的顶点个数和三角面个数和对模型的预览。

编写Shader

Shader的书写方法这里不再讲解,如果你不会编写Shader那就直接赋值一下Shader代码,并在unity中创建一个着色器附到材质球查看效果。


本文Shader的原理和边缘光的Shader类似,计算Diffuse漫反射光凸显模型轮廓,计算边缘光使得模型有较亮或叫暗的面,本shader是片面着色器,使用表面着色器应该会更加简单,后续我会附上表面着色器和固定渲染着色器,供大家参考学习。

Shader "Custom/LowpolyShader"
{Properties {       _MainTex ("Albedo (RGB)", 2D) = "white" {}_Color ("Color", Color) = (1,1,1,1)// 高亮/边缘光颜色_SpecularColor ("Specular Color",Color) = (0.1,0.1,1,1)// 高亮/边缘光强度_SpecualrStrength ("Specular Strength",float) = 1.0 }SubShader {Tags { "RenderType"="Opaque"  }pass{CGPROGRAM#pragma vertex vert#pragma fragment frag#include "UnityCG.cginc"#include "UnityLightingCommon.cginc"sampler2D _MainTex;fixed4 _Color;fixed4 _SpecularColor;float _SpecualrStrength;struct Input {float4 position : POSITION;float3 normal : NORMAL;float2 uv : TEXCOORD0;};struct Out{float4 pos : SV_POSITION;float2 uv : Texcoord0;float3 normal : NORMAL;};Out vert( Input i ){Out o;// 转化屏幕坐标系位置o.pos = mul(UNITY_MATRIX_MVP,i.position);// 将本地坐标系法向量转化为世界坐标系方向量o.normal = mul(float4(i.normal,1),_World2Object).xyz;o.uv = i.uv;return o;}fixed4 frag( Out o ) : COLOR{// 法向量标准化float3 normal = normalize(o.normal);// 获取平行光源方向并标准化float3 lightDir = normalize(_WorldSpaceLightPos0.xyz);// 获取贴图纹理 这步可有可无,取决于你是否贴贴图float3 texColor = tex2D(_MainTex,o.uv);// 计算漫反射fixed3 diffuseColor =  texColor * _Color * max(0,dot( normal,lightDir )) * _LightColor0.rgb;// 计算边缘光float spe = 1 - max(0,dot(normal,lightDir)) ;fixed3 speColor= _SpecularColor.rgb * pow(spe,_SpecualrStrength) ;// 混合输出return fixed4(diffuseColor + speColor,1.0);}ENDCG}}FallBack "Diffuse"
}

网格动态化

网格动态化原理:获取网格中心点的顶点(非边缘点)在update函数中赋值新的顶点位置并重新绘制网格,即可实现动态化,只修改中心顶点,可避免模型变形。

var indexVertices = new List<Vector3>();
timer += Time.deltaTime * Speed;
for (int i = 0; i <= YCount; i++)
{           for (int j = 0; j <= XCount; j++){indexVertices.Add(PosNormal(j, i));if (i.Equals(YCount) || j.Equals(XCount) || i.Equals(0) || j.Equals(0))continue;                // 计算xyz的偏移值,Z轴的偏移值决定了顶点的法线向量和三角面的颜色亮度float offsetX = Mathf.Cos(timer) / 15;float offsetY = Mathf.Sin(timer) / 20;float offsetZ = Mathf.Sin(timer) * 10;// 乘以随机权重值,每个顶点的位移权重值不同,再与第一次绘制的顶点位置相加,避免直接操作顶点导致顶点位置跑偏Vector3 pos = new Vector3(offsetX,offsetY,offsetZ) * randomWeight[(XCount+1) * i + j] + originRandom[ (XCount+1) * i + j ];indexVertices[indexVertices.Count - 1] = pos;}
}
// 将新计算的顶点用于重新绘制网格
TransformLowpoly(indexVertices);
  • 场景中的最终效果

后续拓展

1.通过上述我们已经实现动态低多边形的效果,但是和QQ登录界面的效果还是有一定的差距,主要差在金属的反光效果和动态流光,后续我会考虑升级该效果,加入动态聚光灯来模拟实现。
2.后续我也继续更新一些其他的网格编程结合Shader的文章,比入利用网格shader实现积雪效果,实现海浪效果。嗯嗯,期待吧……因为我还要更新UGUI组件。
3.该篇博客的脚本和shader需要的话在评论下面留下邮箱吧,小内容不想上传github..

UGUI组件系列

  • Unity自定义UI组件(十一) 雷达图、属性图
  • Unity自定义UI组件(十) 折线图
  • Unity自定义UI组件(九) 颜色拾取器(下)
  • Unity自定义UI组件(八) 颜色拾取器(上)
  • Unity自定义UI组件(七)渐变工具、渐变色图片、渐变遮罩
  • Unity自定义UI组件(六)日历、日期拾取器
  • Unity自定义组件之(五) 目录树 UITree
  • Unity自定义UI组件(四)双击按钮、长按按钮
  • Unity自定义UI组件(三)饼图篇
  • Unity自定义UI组件(二)函数图篇(下)
  • Unity自定义UI组件(一)函数图篇(上)

Unity框架解读系列

  • [Unity]PureMVC框架解读(下)
  • [Unity]PureMVC框架解读(上)

分享地址(置顶目录包含所有组件的最新下载地址)

  • Github :https://github.com/ll4080333/UnityCodes
  • CSDN : http://blog.csdn.net/qq_29579137
  • 博客专栏 : http://blog.csdn.net/column/details/16329.html
  • QQ群 : 593906968 有什么不懂的可以加群咨询互相学习
    如果你想了解UGUI的更多拓展组件,欢迎关注我的博客,我会持续更新,支持一下我这个博客新手。如果以上文章对你有帮助,点个赞,让更多的人看到这篇文章,我们一起学习。如果有什么指点的地方欢迎在评论区留言,秒回复。

Unity Shader(一) Lowpoly动态低多边形 (QQ登录界面低边动画)相关推荐

  1. html5 特效 背景 腾讯,html5腾讯QQ登录界面背景动画特效

    特效描述:html5 腾讯QQ 登录界面 背景动画特效.腾讯QQ登陆界面动态背景,直接从腾讯网站获取,js代码有加密,做了个简单地示例 代码结构 1. 引入JS 2. HTML代码 *{margin: ...

  2. Android实现仿QQ登录界面背景动画效果

    登录QQ的时候,我们会看到在登录界面的背景不是静态的,而是一段动画效果,刚开始觉得蛮好奇的,现在我们也来实现一下这种效果,实现起来还是挺简单的. 实现步骤: 1.自定义CustomVideoView类 ...

  3. Unity Shader学习:动态模糊(shutter angle方式)

    Unity Shader学习:动态模糊 动态模糊一般有帧混合和motion vector两种,这里主要介绍motion vector的方法. Keijiro源码:https://github.com/ ...

  4. Unity Shader学习:动态雾

    Unity Shader学习:动态雾 先将相机近裁面四个角向量传给shader,再通过观察空间下的深度值和相机位置算出像素在世界坐标系的位置,通过世界空间高度值来设定雾的范围和浓度,然后通过噪声和uv ...

  5. QT学习日志(附:简易计算器,qq登录界面,简易绘图板,植物大战僵尸魔改版项目实践)

    目录 写在前面 实训前一天 头文件无法生成 项目栏不见了 不知道如何发布程序 实训第一天(附简易计算器的实现) 自定义命名空间的相关问题 关于隐式调用构造函数的问题 实训第二天(不附地址薄的实现) 关 ...

  6. QQ登录界面测试用例

    QQ登录界面功能点:登录.设置.最小化.关闭.头像.登录-下拉框.登录-软键盘.注册账号.找回密码.记住密码.自动登录.多账号登录.二维码等. 1:查看界面排版是否和UI保持一致; 预期:与UI保持一 ...

  7. JavaSwing仿QQ登录界面,注释完善,适合新手学习

    使用说明: 这是一个java做的仿制QQ登录界面,界面仅使用一个类, JDK版本为jdk-11 素材包的名字为:素材(下载)请在项目中新建一个名字为"素材"的文件夹. 素材: ht ...

  8. ios 仿电脑qq登录界面_1、IOS开发--iPad之仿制QQ空间(登录界面搭建+登录逻辑实现)...

    开始搭建登录界面 登录界面效果图: 步骤开始: 设置辅助窗口的位置在下方 快捷键option,然后拖拽复制之后: 这里就直接省去了将背景颜色改为经典黑了. 到这里QQ空间的登录界面搭建完毕. 下面进行 ...

  9. 用combobox扩展控件(dsCtrlComboBox)做出类似QQ登录界面的效果

    原文地址:http://www.uieasy.cn/blog/?p=513 传统的combobox 在使用方面有很多限制,很难满足我们combobox类型控件的需求.主要表现中combobox内置的l ...

最新文章

  1. openlayers 可以实现3d地图效果吗_OpenLayers教程:图形绘制之设置图形的样式
  2. 每日程序C语言29-将数组逆序输出
  3. java页面请求跑批处理sql的有关问题
  4. 轻量级锁的加锁和解锁逻辑-自旋锁
  5. 解决ansible报错“msg“: “Failed to import docker-py - cannot import name __version__.
  6. 转载一篇《Redis源码研究—哈希表》重点是如何重新哈希
  7. GPU Gems1 - 11 阴影贴图反走样
  8. 02如何抓住重点,系统高效地学习数据结构与算法?
  9. 一罐将其全部统治:Arquillian + Java 8
  10. slf4j + log4j原理实现及源码分析
  11. 求C语言中的32个关键字及其意思?
  12. Javascript——读取json文件方法总结
  13. C++ 课设 职工工资管理系统
  14. Spark学习资料汇总
  15. page8-JQ的点击隐藏与显示
  16. linux内核snat分析,(十)洞悉linux下的Netfilteriptables:网络地址转换原理之SNAT
  17. 小白搭建个人网站最详细的全过程
  18. 一曲相思用计算机怎么按,抖音这人间袅袅炊烟是什么歌 抖音一曲相思完整版...
  19. 关于音频情感分类的随笔(2)
  20. 安全绳使用方法图解_高空作业之安全绳的正确使用

热门文章

  1. 编程之类的文案_精选50句文案,个个都是让你灵感喷涌的句子!
  2. 2022最新可用网页百度分享按钮安装教程【网站添加百度分享按钮代码】
  3. 港股通不得不了解的汇率问题
  4. 两点天上来,爱情一线牵——Global Timing Debugger
  5. 超卓航科上市:募资9亿市值超60亿 成襄阳首家科创板企业
  6. PhotoShop - 滤色模式(screen) 的 响应曲线(关于加镜头光晕的思考)
  7. 机器指令-微指令存储相关基本概念
  8. Light OJ 1197
  9. 「Python 网络自动化」Nornir—— Inventory(主机清单)介绍
  10. 管理科学基础知识__后悔值计算