Unity 引擎做残影效果——3、顶点偏移方式
Unity实现残影效果
大家好,我是阿赵。
继续讲Unity引擎的残影做法。这次的残影效果和之前两种不太一样,是通过顶点偏移来实现的。
具体的效果是这样:
与其说是残影,这种效果更像是移动速度很快时造成的速度线,所以在移动过程中的效果还是非常好的,截图的感觉没有视频的感觉那么有冲击力。
一、原理介绍
1、顶点偏移
这个做法很简单,在c#里面对比当前帧和上一帧的坐标,如果坐标有变化,就把两个坐标相减,算出一个世界空间坐标的向量出来,并做标准化处理。
得到了这么一个向量之后,就可以把它传入到shader里面了。
既然是顶点偏移,那么肯定是在顶点着色器程序里面做修改了。不过这里有个比较值得注意的问题。一般写shader的时候,如果没有特殊的需要,我们都是直接把顶点坐标从物体局部坐标直接就转换到裁剪空间了,比如使用Untiy提供的方法:
UnityObjectToClipPos(v.vertex);
但在这个例子里面,我们从c#传入的是世界空间坐标的向量,所以我们不能通过模型局部坐标或者裁剪空间去叠加这个偏移向量,而应该在同样的世界坐标上面去偏移。
float4 worldPos = mul(unity_ObjectToWorld, v.vertex);
worldPos += _MoveDir * _MoveDis;
其中_MoveDir就是标准化后的移动向量, _MoveDis是用于控制偏移距离的变量。
这个时候,可以看到,整个模型都在拉伸了

但这种效果明显不是我们想要的。
2.控制偏移的范围
我们想要的效果是怎样的呢?是不是应该是移动的过程中,前面的部分是不变形的,只有最后一点位置出现拉伸?
怎么去控制拉伸的范围呢?其实很简单,假如我们把刚才那个移动的向量想象成是灯光,就可以求出这样的一个效果:

很明显的,模型高亮的部分,其实就是我们想拉伸的方向了。所以,其实很简单,把移动方向和模型的法线做点乘,就可以求出一个我们想偏移的范围了。
不过现在高亮的部分还是有点多,所以我们用一个power运算,让对比度变得更高,就变成了下面这个情况。

现在,白色的部分只有很小的范围了。接下来就对这个白色的范围做顶点偏移:

基本上就出现了我们想要的效果了。
二、优缺点
1、优点
首先,之前介绍的不管是BakeMesh还是屏幕后处理,都是基于对模型的多次渲染上的,所以性能上并不那么友好。
用顶点偏移的方式,并没有太多额外的计算量,所以从性能上来说,它是比前两种方式都要好的。
然后,顶点偏移这种手段,其实是一种非常简单的技术含量比较低的手段,难点是在于你怎样想出这个办法而已,所以知道了方法之后,其实非常容易就能实现了。
2、缺点
这个做法的缺点也是很明显的。
首先,它不是真正的残影效果,只是速度线的类似效果,所以我个人感觉只适合用于特定的风格里面,比如卡通之类。如果写实风格的游戏用这种效果可能不太行。
然后,由于是要对模型做顶点偏移,那么就要意味着需要修改模型原有的Shader了。对于比较正规的团队来说,这问题不大,因为项目里面每个Shader都应该是经过TA的定制的,需要统一加入一些元素是很简单的。
但对于不那么正规的团队,甚至是纯美术人员组成的团队来说,说不定已经在用的Shader都是从不知道哪个网站上面复制下来的,要统一修改,难度会非常大。
所以,如果想使用这种技术手段,还是要先考虑一下自己的实际情况的。
三、代码
同样的,只是demo,所以代码只是在于实现部分,没有过多优化。
1、C#
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class MoveVertexOffsetCtrl : MonoBehaviour
{public float moveDis = 1;private bool isMove = false;private Vector3 oldPos = Vector3.zero;public GameObject role;private Vector3 moveDir = Vector3.zero;private List<Material> matList;public float pow = 1;// Start is called before the first frame updatevoid Start(){matList = new List<Material>();if(role){SkinnedMeshRenderer[] renders = role.GetComponentsInChildren<SkinnedMeshRenderer>();for(int i = 0;i<renders.Length;i++){for(int j = 0;j<renders[i].materials.Length;j++){Material mat = renders[i].materials[j];if (matList.IndexOf(mat) < 0){matList.Add(mat);}}}}}// Update is called once per framevoid Update(){CheckMove();SetMaterial();}private void CheckMove(){if(role){if(Vector3.Distance(role.transform.position,oldPos)>0){moveDir = oldPos - role.transform.position;moveDir = moveDir.normalized;oldPos = role.transform.position;isMove = true;}else{isMove = false;}}else{isMove = false;}}private void SetMaterial(){if(matList!=null&&matList.Count>0){for(int i = 0;i<matList.Count;i++){if(isMove==false){matList[i].SetFloat("_MoveDis", 0);}else{matList[i].SetFloat("_MoveDis", moveDis);matList[i].SetVector("_MoveDir", moveDir);matList[i].SetFloat("_MovePow", pow);}}}}
}
2、Shader
Shader "azhao/MoveVertexBase"
{Properties{_MainTex ("Texture", 2D) = "white" {}_MoveDir("MoveDir",Vector) = (0,0,0,0)_MoveDis("MoveDis",float) = 0_MovePow("MovePow",float) = 1}SubShader{Tags { "RenderType"="Opaque" }LOD 100Pass{CGPROGRAM#pragma vertex vert#pragma fragment frag#include "UnityCG.cginc"struct appdata{float4 vertex : POSITION;float2 uv : TEXCOORD0;half3 normal : NORMAL;};struct v2f{float2 uv : TEXCOORD0;float4 vertex : SV_POSITION;};sampler2D _MainTex;float4 _MainTex_ST;float4 _MoveDir;float _MoveDis;float _MovePow;v2f vert (appdata v){v2f o;o.uv = TRANSFORM_TEX(v.uv, _MainTex);float4 worldPos = mul(unity_ObjectToWorld, v.vertex);float3 worldNormal = mul(v.normal, (float3x3)unity_WorldToObject);float ndotl = saturate(dot(worldNormal, _MoveDir));ndotl = pow(ndotl, _MovePow);worldPos += _MoveDir * _MoveDis*ndotl;//世界空间顶点坐标转观察空间坐标float4 viewPos = mul(UNITY_MATRIX_V, worldPos);//观察空间坐标转裁剪空间坐标float4 clipPos = mul(UNITY_MATRIX_P, viewPos);o.vertex = clipPos;return o;}half4 frag (v2f i) : SV_Target{// sample the texturehalf4 col = tex2D(_MainTex, i.uv);return col;}ENDCG}}
}
相关文章:
Unity 引擎做残影效果——3、顶点偏移方式
Unity实现残影效果 大家好,我是阿赵。 继续讲Unity引擎的残影做法。这次的残影效果和之前两种不太一样,是通过顶点偏移来实现的。 具体的效果是这样: 与其说是残影,这种效果更像是移动速度很快时造成的速度线,所以在移…...
【Linux】权限
1、shell命令以及运行原理 Linux 严格意义上说的是一个操作系统,我们称之为“核心(kernel)“ ,但我们一般用户,不能直接使用 kernel。而是通过 kernel 的“外壳”程序,也就是所谓的shell,来与 k…...
Excel导入日期格式时自动转为五位数文本
问题描述:Excel导入数据时,当数据是日期可能会存在问题,日期格式转为文本了,例如“2023-07-31”接收时变为“45138”,导致后端解析日期出错,无法导入。 解决方法: 方法一:将Excel日…...
Mac使用brew安装软件报错
在使用brew安装软件时报错Failed to upgrade Homebrew Portable Ruby! brew install --cask --appdir/Applications docker> Downloading https://ghcr.io/v2/homebrew/portable-ruby/portable-ruby/blobs/sha256:0cb1cc7af109437fe0e020c9f3b7b95c3c709b140bde9f991ad2c143…...
Android 实现MQTT客户端,用于门禁消息推送
添加MQTT依赖 implementation ‘org.eclipse.paho:org.eclipse.paho.client.mqttv3:1.2.2’ implementation ‘org.eclipse.paho:org.eclipse.paho.android.service:1.1.1’ 在Manifest清单文件中添加服务 <service android:name"org.eclipse.paho.android.service.Mq…...
跨境电商的广告推广怎么做?7个方法
在跨境电商竞争日趋激烈的市场环境下,跨境电商店铺引流成了制胜关键点。这里给大家分享一套引流推广的方法。 一、搜索引擎营销推广 搜索引擎有两个最大的优点是更灵活、更准确。搜索引擎营销的目标定位更精确,且不受时间和地理位置上的限制࿰…...
《Java-SE-第二十八章》之CAS
前言 在你立足处深挖下去,就会有泉水涌出!别管蒙昧者们叫嚷:“下边永远是地狱!” 博客主页:KC老衲爱尼姑的博客主页 博主的github,平常所写代码皆在于此 共勉:talk is cheap, show me the code 作者是爪哇岛的新手,水平很有限&…...
git之reflog分析
写在前面 本文一起看下reflog命令。 1:场景描述 在开发的过程中,因为修改错误,想要通过git reset命令恢复到之前的某个版本,但是选择提交ID错误,导致多恢复了一个版本,假定,该版本对应的内容…...
《吐血整理》进阶系列教程-拿捏Fiddler抓包教程(18)-Fiddler如何接口测试,妈妈再也不担心我不会接口测试了
1.简介 Fiddler最大的优势在于抓包,我们大部分使用的功能也在抓包的功能上,fiddler做接口测试也是非常方便的。 领导或者开发给你安排接口测试的工作任务,但是没有给你接口文档(由于开发周期没有时间出接口文档)&…...
Oracle open JDK和 Amazon Corretto JDK的区别
Oracle OpenJDK和Amazon Corretto JDK都是基于Java开放源代码项目的发行版,它们之间有一些区别。 1. 来源:Oracle OpenJDK是由Oracle公司领导和支持的,它是Java的官方参考实现之一。而Amazon Corretto JDK是由亚马逊公司开发和支持的…...
Spark写PGSQL分区表
这里写目录标题 需求碰到的问题格式问题分区问题(重点) 解决完整代码效果 需求 spark程序计算后的数据需要往PGSQL中的分区表进行写入。 碰到的问题 格式问题 使用了字符串格式,导致插入报错。 val frame df.withColumn("insert_t…...
Git 命令行登录
有时候登录命令行版本的git会出现这个错误 1remote: Support for password authentication was removed on August 13, 2021. 2remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for …...
性能分析记录
4实例压测TPS浮动在200-300 1.TPS浮动200-300,ART浮动的可能性是10-20ms,链路复杂是可接受的,链路简单则需要分析原因。 1)缓存没命中,对某些账号缓存没命中,或缓存失效后导致隔段时间耗时升高。 2&…...
Java反射学习(大综合)
第一天 Java反射及动态代理... 2 一、 Java反射... 2 1、什么是反射:... 2 2、反射的原理... 2 3、反射的优缺点:... 2 4、反射的用途:... 3 5、反射机制常用的类:... 3 1、获得Class:主要有三…...
Vite+Vue3 开发UI组件库并发布到npm
一直对开源UI组件库比较感兴趣,摸索着开发了一套,虽然还只是开始,但是从搭建到发布这套流程基本弄明白了,现在分享给大家,希望对同样感兴趣的同学有所帮助。 目前我的这套名为hasaki-ui的组件库仅有两个组件࿰…...
vue- form动态表单验证规则-表单验证
前言 以element官网的form表单的-动态增减表单项为例讲解表单验证规则 动态的功能就是v-model配合push v-for 便利来实现的 我们需要熟知2个知识点prop表单验证需要跟v-model绑定的值是一样的, 如果是一个数组便利的表单,那就需要绑定这个数组每一项…...
FPGA学习—通过数码管实现电子秒表模拟
文章目录 一、数码管简介二、项目分析三、项目源码及分析四、实现效果五、总结 一、数码管简介 请参阅博主以前写过的一篇电子时钟模拟,在此不再赘述。 https://blog.csdn.net/qq_54347584/article/details/130402287 二、项目分析 项目说明:本次项目…...
区块链媒体发稿:区块链媒体宣发常见问题解析
据统计,由于区块链应用和虚拟货币的兴起,越来越多媒体对区块链领域开展报导,特别是世界各国媒体宣发全是热火朝天。但是,随着推卸责任媒体宣发的五花八门,让很多人因而上当受骗,乃至伤害一大笔资产。身为投…...
openGauss学习笔记-28 openGauss 高级数据管理-NULL值
文章目录 openGauss学习笔记-28 openGauss 高级数据管理-NULL值28.1 IS NOT NULL28.2 IS NULL openGauss学习笔记-28 openGauss 高级数据管理-NULL值 NULL值代表未知数据。无法比较NULL和0,因为它们是不等价的。 创建表时,可以指定列可以存放或者不能存…...
DAO和XML文件参数和返回值
①MyBatis中resultType和resultMap的区别 1.使用MyBatis查询数据库记录时,返回类型常用的有两种:resultType和resultMap。那么两者之间有什么区别呢? 如果只是返回一个值,简单类型,比如说String或者int,那…...
网络编程(Modbus进阶)
思维导图 Modbus RTU(先学一点理论) 概念 Modbus RTU 是工业自动化领域 最广泛应用的串行通信协议,由 Modicon 公司(现施耐德电气)于 1979 年推出。它以 高效率、强健性、易实现的特点成为工业控制系统的通信标准。 包…...
uniapp 对接腾讯云IM群组成员管理(增删改查)
UniApp 实战:腾讯云IM群组成员管理(增删改查) 一、前言 在社交类App开发中,群组成员管理是核心功能之一。本文将基于UniApp框架,结合腾讯云IM SDK,详细讲解如何实现群组成员的增删改查全流程。 权限校验…...
Chapter03-Authentication vulnerabilities
文章目录 1. 身份验证简介1.1 What is authentication1.2 difference between authentication and authorization1.3 身份验证机制失效的原因1.4 身份验证机制失效的影响 2. 基于登录功能的漏洞2.1 密码爆破2.2 用户名枚举2.3 有缺陷的暴力破解防护2.3.1 如果用户登录尝试失败次…...
谷歌浏览器插件
项目中有时候会用到插件 sync-cookie-extension1.0.0:开发环境同步测试 cookie 至 localhost,便于本地请求服务携带 cookie 参考地址:https://juejin.cn/post/7139354571712757767 里面有源码下载下来,加在到扩展即可使用FeHelp…...
云启出海,智联未来|阿里云网络「企业出海」系列客户沙龙上海站圆满落地
借阿里云中企出海大会的东风,以**「云启出海,智联未来|打造安全可靠的出海云网络引擎」为主题的阿里云企业出海客户沙龙云网络&安全专场于5.28日下午在上海顺利举办,现场吸引了来自携程、小红书、米哈游、哔哩哔哩、波克城市、…...
Mybatis逆向工程,动态创建实体类、条件扩展类、Mapper接口、Mapper.xml映射文件
今天呢,博主的学习进度也是步入了Java Mybatis 框架,目前正在逐步杨帆旗航。 那么接下来就给大家出一期有关 Mybatis 逆向工程的教学,希望能对大家有所帮助,也特别欢迎大家指点不足之处,小生很乐意接受正确的建议&…...
CMake基础:构建流程详解
目录 1.CMake构建过程的基本流程 2.CMake构建的具体步骤 2.1.创建构建目录 2.2.使用 CMake 生成构建文件 2.3.编译和构建 2.4.清理构建文件 2.5.重新配置和构建 3.跨平台构建示例 4.工具链与交叉编译 5.CMake构建后的项目结构解析 5.1.CMake构建后的目录结构 5.2.构…...
从零开始打造 OpenSTLinux 6.6 Yocto 系统(基于STM32CubeMX)(九)
设备树移植 和uboot设备树修改的内容同步到kernel将设备树stm32mp157d-stm32mp157daa1-mx.dts复制到内核源码目录下 源码修改及编译 修改arch/arm/boot/dts/st/Makefile,新增设备树编译 stm32mp157f-ev1-m4-examples.dtb \stm32mp157d-stm32mp157daa1-mx.dtb修改…...
ElasticSearch搜索引擎之倒排索引及其底层算法
文章目录 一、搜索引擎1、什么是搜索引擎?2、搜索引擎的分类3、常用的搜索引擎4、搜索引擎的特点二、倒排索引1、简介2、为什么倒排索引不用B+树1.创建时间长,文件大。2.其次,树深,IO次数可怕。3.索引可能会失效。4.精准度差。三. 倒排索引四、算法1、Term Index的算法2、 …...
拉力测试cuda pytorch 把 4070显卡拉满
import torch import timedef stress_test_gpu(matrix_size16384, duration300):"""对GPU进行压力测试,通过持续的矩阵乘法来最大化GPU利用率参数:matrix_size: 矩阵维度大小,增大可提高计算复杂度duration: 测试持续时间(秒&…...
