关于Unity中的旋涡特效的制作(捕鱼达人3技术)(专题八)
Mesh--材质--Shader
1: Mesh 是网格,包括顶点,法线,纹理坐标,切线,三角形。在每一个3D模型节点里面,有一个Mesh Filter组件来提取模型里面的网格数据;
2: Shader渲染算法,3D模型是按照什么方式怎么样绘制出来的;
3: 材质是给渲染算法的输入数据,当我们新创建一个材质的时候,先要选Shader,在Shader文件里面关联好输入,
材质实际上就是给Shader提供初始化数据的,比如Shader里面需要贴图纹理,材质就把自己的贴图纹理数据传递给自己关联的Shader,作为Shader的输入。
或者这样理解,材质选好一种Shader,这个Shader就要求材质提供一些输入数据,材质就是把这些数据准备好,填好传递给Shader,Shader有了这些数据就能准确地绘制出来。
有了网格数据后,使用Shader的算法,我们就能把3D模型绘制出来
4: 代码修改材质参数,能修改给渲染算法的数据从而获得不同的效果;
模型的网格里面,有很多三角形的小网格,每一个小网格的在贴图上是有一个纹理坐标的,我们把每一个小网格的顶点在贴图上的纹理坐标叫做UV,我这样理解,UV坐标(0,0)对应的就是贴图中心的那个像素点,两者关联绑定好,模型的(0,0)对应贴图的(0,0),贴图的(0,0)什么颜色,模型的(0,0)上面贴的也就是什么颜色
绘制的过程
小网格的顶点在模型上会给定一个UV坐标,那么这个时候,每一个像素点的颜色就是在这个附近的网格顶点的纹理的UV坐标决定的
实际上就是每一个小网格顶点的纹理坐标决定了每个顶点之间要绘制什么纹理,有一个插值计算的过程。
最终小三角形网格就着色成了对应贴图里面对应坐标的那个颜色
由每个小三角形网格的顶点的纹理坐标决定要加入哪些图像,把图像从贴图拷贝到网格上绘制出来。
旋涡特效原理
改变纹理坐标的寻址,使用一个算法扭曲画面,其实是扭曲完纹理坐标后,再去寻址,这样寻到的就是扭曲后的图像,也就是要把每一个小三角形网格的顶点的纹理坐标进行数学扭曲,扭曲好了以后再寻址贴图,把图像贴上去绘制。
扭曲一下每一个顶点所在原贴图的纹理坐标,这样在贴图寻址的时候找到的是扭曲后的坐标,旋涡效果就出来了。
漩涡特效粗糙原因和解决方案
1: mesh的点不够多,导致旋转的精度不够细;
2: 加大mesh点的密度;
3: 漩涡特效在场景地图切换中的使用:
截图生成纹理对象,设置到漩涡的材质,运行漩涡特效
旋涡特效搭建
1: 创建一个平面;
2: 给这个平面关联一个材质;
3: 纹理会自动对齐到2^N方,如果要原始大小,可以设置为”Advance”;
4: 创建一个材质,先使用默认的Diffuse Shader;
5: 旋转平面, 调整能正常显示出来,注意平面的正反面,反面摄像机默认是不绘制的(剔除);
6: 调整这个平面的大小和距离, 让它能盖住屏幕;
旋涡特效实例
1.创建Unity工程和文件目录,保存场景
2.导入旋涡纹理ui_main_tex_b_06.png到Resources,导入后发现贴图的宽高都变成2的n次方大小
其实可以修改成原始大小,选择纹理---->(Texture Tyoe)---->Advanced---->Non Power of 2---->none---->Apply
3.创建一个平面plane,设置大小为旋涡纹理一样大,所以ScaleX=14.22,ScaleZ=8
4.创建一个材质球plane,设置材质球的shader---->mobile---->diffuse,把旋涡纹理拖进材质球,关联材质球到plane节点
5.调整plane到可以看到整个平面无黑边,调整过程中旋转的时候如果只是改变Inspector面板里面的Rotation值有时会没有效果,因为欧拉角旋转有一个顺序
这时候有两种方法,一种是在Scene视图里面手动转,不准,一种是把Scale的X值变成负的可以上下颠倒贴图方向,Scale的Z值变成负的可以左右颠倒贴图方向
6.创建一个文件夹Shaders,进入创建一个Shader---->unlit shader,叫vortex
打开vortex.shader
Shader "Custom/vortex"
{
Properties
{
_MainTex ("Texture", 2D) = "white" {}
radius("Radius",Float)=0.0//绑定到材质属性里面,作为初始输入数据
angle("Angle",Float)=0.0//绑定到材质属性里面,作为初始输入数据
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc" struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;//UV纹理坐标
}; struct v2f
{
float2 uv : TEXCOORD0;
float4 vertex : SV_POSITION;
}; sampler2D _MainTex;
float4 _MainTex_ST; float radius; //扭曲的半径
float angle; //扭曲的角度 //顶点
v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex); //自己干预纹理坐标,下面的代码就是UV坐标的变换
float2 uv = v.uv;//获取顶点的纹理坐标[0,1] //这两个参数不能写死在Shader里面,我们要改变这个值,才会有旋涡的动画效果
//float radius = 0.5f; //半径,表示多少范围内的图像发生扭曲,纹理坐标才0到1,所以0.5挺大的了
//float angle = 1.0f;//角度,单位是弧度,表示扭曲的程度,正弦计算的时候要用到的角度 uv -= float2(0.5, 0.5);//把UV坐标的原点转移到图像中心,就是以中心点为UV的原点,左下角变成了[-0.5,-0.5]
float dist = length(uv);//计算出当前的坐标到纹理中心的距离
float percent = (radius - dist) / radius;//计算出当前的坐标到纹理中心的距离占半径的百分比
if ( percent < 1.0 && percent >= 0.0) {//如果百分比在0到1,说明当前的坐标在扭曲的范围内,执行扭曲
//扭曲算法
float theta = percent * percent * angle * 8.0;
float s = sin(theta);
float c = cos(theta);
uv = float2(dot(uv, float2(c, -s)), dot(uv, float2(s, c)));
} uv += float2(0.5, 0.5);//变换回纹理坐标寻址的原点 o.uv = uv;//这样,顶点就有了对应纹理的坐标
return o;
} //着色
fixed4 frag (v2f i) : SV_Target
{
fixed4 col = tex2D(_MainTex, i.uv);
return col;
}
ENDCG
}
}
}
UV纹理坐标范围
半径0.5的长度
UV原点转移
关联到材质属性面板
7.再写一个脚本vertex挂载在plane节点下,来控制材质球plane的radius和angle属性动态改变,实现旋涡的效果
using UnityEngine;
using System.Collections;
using System.Collections.Generic; public class vortex : MonoBehaviour
{
// (1)关联材质对象带代码,手动拖或者使用代码加载
public Material mat; // 手动拖,理解为用来配置shader输入数据的文件; float radius_speed = 0.1f; // 半径的扩散速度,2s变换到 1 radis;
float angle_speed = 0.5f; // 弧度的改变速度,2s --> 2弧度 float total_time = 6.0f; // 特效持续的时间;
float run_time = 0.0f; // 当前特效的运行的时间; float now_radius = 0.0f; // 现在的波及范围;
float now_angle = 0.0f; // 现在的波及角度; // Use this for initialization
void Start()
{
//这里面的代码是为了提高网格顶点的数量,为了提高旋涡的精度而写的
//之前有讲过网格顶点的扩充
for (int j = ; j < ; j++)
{
// 执行一次, 10, 20, 40, 80
Mesh self_mesh = this.GetComponent<MeshFilter>().mesh; List<Vector3> vertices = new List<Vector3>();
List<int> triangles = new List<int>();
List<Vector3> normals = new List<Vector3>();
List<Vector2> uv = new List<Vector2>();
List<Vector4> tangents = new List<Vector4>(); for (int i = ; i < self_mesh.triangles.Length / ; i++)
{
Vector3 t0 = self_mesh.vertices[self_mesh.triangles[i * + ]];
Vector3 t1 = self_mesh.vertices[self_mesh.triangles[i * + ]];
Vector3 t2 = self_mesh.vertices[self_mesh.triangles[i * + ]]; Vector3 t3 = Vector3.Lerp(t0, t1, 0.5f);
Vector3 t4 = Vector3.Lerp(t1, t2, 0.5f);
Vector3 t5 = Vector3.Lerp(t0, t2, 0.5f); int count = vertices.Count;
vertices.Add(t0); // count + 0
vertices.Add(t1); // count + 1
vertices.Add(t2); // count + 2
vertices.Add(t3); // count + 3
vertices.Add(t4); // count + 4
vertices.Add(t5); // count + 5 triangles.Add(count + ); triangles.Add(count + ); triangles.Add(count + );
triangles.Add(count + ); triangles.Add(count + ); triangles.Add(count + );
triangles.Add(count + ); triangles.Add(count + ); triangles.Add(count + );
triangles.Add(count + ); triangles.Add(count + ); triangles.Add(count + ); Vector3 n0 = self_mesh.normals[self_mesh.triangles[i * + ]];
Vector3 n1 = self_mesh.normals[self_mesh.triangles[i * + ]];
Vector3 n2 = self_mesh.normals[self_mesh.triangles[i * + ]]; Vector3 n3 = Vector3.Lerp(n0, n1, 0.5f);
Vector3 n4 = Vector3.Lerp(n1, n2, 0.5f);
Vector3 n5 = Vector3.Lerp(n0, n2, 0.5f); normals.Add(n0); // count + 0
normals.Add(n1); // count + 1
normals.Add(n2); // count + 2
normals.Add(n3); // count + 3
normals.Add(n4); // count + 4
normals.Add(n5); // count + 5 Vector2 uv0 = self_mesh.uv[self_mesh.triangles[i * + ]];
Vector2 uv1 = self_mesh.uv[self_mesh.triangles[i * + ]];
Vector2 uv2 = self_mesh.uv[self_mesh.triangles[i * + ]]; Vector2 uv3 = Vector3.Lerp(uv0, uv1, 0.5f);
Vector2 uv4 = Vector3.Lerp(uv1, uv2, 0.5f);
Vector2 uv5 = Vector3.Lerp(uv0, uv2, 0.5f); uv.Add(uv0); // count + 0
uv.Add(uv1); // count + 1
uv.Add(uv2); // count + 2
uv.Add(uv3); // count + 3
uv.Add(uv4); // count + 4
uv.Add(uv5); // count + 5 Vector4 tan0 = self_mesh.tangents[self_mesh.triangles[i * + ]];
Vector4 tan1 = self_mesh.tangents[self_mesh.triangles[i * + ]];
Vector4 tan2 = self_mesh.tangents[self_mesh.triangles[i * + ]]; Vector4 tan3 = Vector3.Lerp(tan0, tan1, 0.5f);
Vector4 tan4 = Vector3.Lerp(tan1, tan2, 0.5f);
Vector4 tan5 = Vector3.Lerp(tan0, tan2, 0.5f); tangents.Add(tan0); // count + 0
tangents.Add(tan1); // count + 1
tangents.Add(tan2); // count + 2
tangents.Add(tan3); // count + 3
tangents.Add(tan4); // count + 4
tangents.Add(tan5); // count + 5
}
self_mesh.Clear();
self_mesh.vertices = vertices.ToArray();
self_mesh.triangles = triangles.ToArray();
self_mesh.normals = normals.ToArray();
self_mesh.uv = uv.ToArray();
self_mesh.tangents = tangents.ToArray(); self_mesh.RecalculateBounds();
}
} // Update is called once per frame
void Update()
{
float dt = Time.deltaTime;
this.run_time += dt;//当前的时间不断累加 this.now_radius += (this.radius_speed * dt);//随着时间的推移不断改变半径
//if (this.now_radius >= 0.5f)//如果波及范围的半径大于0.5,就不用再扩散了
//{
//this.now_radius = 1.0f;
//}
this.now_angle += (this.angle_speed * dt);//随着时间的推移不断改变弧度 //在代码中设置所关联的材质球面板的属性
this.mat.SetFloat("radius", this.now_radius);
this.mat.SetFloat("angle", this.now_angle); // 当前时间大于特效时间,表示当前特效结束了
if (this.run_time >= this.total_time)
{
// 重新转一次
this.now_angle = ;
this.now_radius = ;
this.run_time = ;
// end
}
}
}
顶点初始时的网格
顶点扩充后的网格
漩涡特效总结
1: 创建一个Shader: Unlit-->Shader;
2: 漩涡特效分析:
(1)纹理坐标的范围[0, 1];,不是像素那种的坐标,这样的好处是不管材质贴图是多少乘多少像素的,都可以用百分比对应到
(2)扭曲顶点的纹理坐标, 扭曲的角度+波及的半径;
(3)将扭曲的角度与半径数据绑定到材质;
(4)设置扭曲的角速度, 随着时间的推移加大扭曲角度;
(5)设置波及范围的速度,随着时间的推移不断的加大波及半径;
(6)编写代码来控制参数,实现动态的旋转;
3: 扭曲代码:
float2 uv = v.uv;
float radius = 0.5f; float angle = 1.0f;
uv -= float2(0.5, 0.5);
float dist = length(uv);
float percent = (radius - dist) / radius;
if ( percent < 1.0 && percent >= 0.0) {
float theta = percent * percent * angle * 8.0;
float s = sin(theta);
float c = cos(theta);
uv = float2(dot(uv, float2(c, -s)), dot(uv, float2(s, c)));
}
uv += float2(0.5, 0.5);
关于Unity中的旋涡特效的制作(捕鱼达人3技术)(专题八)的更多相关文章
- Unity中利用柏林噪声(perlinnoise)制作摇摆效果
perlinnoise是unity中Mathf下的一个函数,需要两个float参数x和y进行采样,返回一个0-1的float型. 项目里经常要随机摇摆某些东西,比如摄像机,某个随机运动的目标等等,都可 ...
- Unity中的粒子特效的 RendererQ 排序
这里接https://www.cnblogs.com/luguoshuai/p/10021660.html 这里介绍两套粒子排序的方法. 首先声明,这两套排序方法均不是本人所写,是在项目开发的过程当中 ...
- unity中利用纯物理工具制作角色移动跳跃功能
using System.Collections;using System.Collections.Generic;using UnityEngine; public class Player : M ...
- 【Unity技巧】Unity中的优化技术
http://blog.csdn.net/candycat1992/article/details/42127811 写在前面 这一篇是在Digital Tutors的一个系列教程的基础上总结扩展而得 ...
- Unity中的优化技术
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明.本文链接:https://blog.csdn.net/candycat1992/article/ ...
- Unity教程之再谈Unity中的优化技术
这是从 Unity教程之再谈Unity中的优化技术 这篇文章里提取出来的一部分,这篇文章让我学到了挺多可能我应该知道却还没知道的知识,写的挺好的 优化几何体 这一步主要是为了针对性能瓶颈中的”顶点 ...
- unity中的动画制作方法
Unity中的动画制作方法 1.DOTween DoTween在5.0版本中已经用到了,到官网下载好插件之后,然后通过在项目中导入头using DG.Tweening;即可. 一些常用的API函数 D ...
- Unity中制作游戏的快照游戏支持玩家拍快照
Unity中制作游戏的快照游戏支持玩家拍快照 有些游戏支持玩家“拍快照”,也就是将游戏的精彩瞬间以图片的形式记录下来的功能.这个功能比较有趣,而且以后的用途也会很广,为此本节打算介绍:截取矩形区域内游 ...
- unity 中UGUI制作滚动条视图效果(按钮)
1.在unity中创建一个Image作为滚动条视图的背景: 2.在Image下创建一个空物体,在空物体下创建unity自带的Scroll View组件: 3.对滑动条视图的子物体进行调整: 4.添加滚 ...
随机推荐
- [svc]linux正则实战(grep/sed/awk)
企业实战: 过滤ip 过滤出第二行的 192.168.2.11. eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500 ine ...
- Java 必须掌握的 12 种 Spring 常用注解!
1.声明bean的注解 @Component 组件,没有明确的角色 @Service 在业务逻辑层使用(service层) @Repository 在数据访问层使用(dao层) @Controller ...
- HTTP 代理服务器技术选型之旅
HTTP 代理服务器技术选型之旅 背景 长期以来,贴吧开发人员多,业务耦合大,需求变化频繁,因此容易产生 bug.而我所负责的广告相关业务,和 UI 密切相关,一旦因为某种原因(甚至是被别人改了代码) ...
- [mBean]-Delphi框架,回归简单,自然。
[mBean]的萌芽 最近公司要求把我们公司的任务可以外包,问我有没有好的方案. 如果要其他程序员的人来做我们内部的框架会导致了,内部的框架需要公布很多单元和逻辑,思路.其次要把我们的思路和规则强加给 ...
- pyspark实现自动提示以及代码高亮
pyspark实现自动提示以及代码高亮 起因 打开pyspark发现啥提示都没有,太不友好了啊,然后忍不住谷歌了一下,发现了这篇文章,内容如下: 1.pip install ptpython; 2.e ...
- Markdown语法学习(二)
概述 宗旨 Markdown 的目标是实现「易读易写」. 可读性,无论如何,都是最重要的.一份使用 Markdown 格式撰写的文件应该可以直接以纯文本发布,并且看起来不会像是由许多标签或是格式指令所 ...
- 【Ubuntu】VirtualBox 您没有查看“sf_VirtualDisk”的内容所需的权限。
转自:https://www.cnblogs.com/laishenghao/p/5346651.html 最终解决办法: sudo adduser lqr vboxsf 这里lqr是我的用户名 然后 ...
- 【Bayesian】贝叶斯决策方法(Bayesian Decision Method)
已知某条件概率,如何得到两个事件交换后的概率,也就是在已知P(A|B)的情况下如何求得P(B|A).这里先解释什么是条件概率: 表示事件B已经发生的前提下,事件A发生的概率,叫做事件B发生下事件A的条 ...
- Error Installing Tivoli Directory Server (TDS) for TNPMW1.3
Install TDS with error "GLPCTL004E Failed to create database instance: 'ldapdb2'." Error ...
- [转]MySQL中int(11)最大长度是多少?
原文地址:https://blog.csdn.net/allenjay11/article/details/76549503 今天在添加数据的时候,发现当数据类型为 int(11) 时,我当时让用户添 ...