Unity 引擎做残影效果——2、屏幕后处理方式

Unity实现残影效果


  大家好,我是阿赵。
  这里继续介绍Unity里面做残影的方法。之前介绍了BakeMesh的方法做残影,这一期介绍的是用屏幕后处理的方法做残影。

一、原理

  之前的BakeMesh方法,是真的生成了很多个网格模型在场景里面。如果用后处理做,就没有这个过程。
在这里插入图片描述
在这里插入图片描述

  可以看到,虽然Game视图里面看到了残影,但实际上场景里面只有原理的一个角色的网格模型。
  其实用后处理做残影的方法非常的简单。首先复制一个和主摄像机一样的子摄像机,然后这个摄像机只看角色层,最后,给这个摄像机设置一个RenderTexture作为targetTexture。
在这里插入图片描述

  这样,我们就可以在主摄像机渲染完整的画面的同时,拿到了一个只有角色的RenderTexture。
  然后我们维护一个队列,这个队列保存着过去几帧里面的渲染角色的RenderTexture。至于需要保存多少帧,多久保存一帧,就看各位自己的需要了。
  得到了这个RenderTexture队列之后,剩下的事情就非常简单了。把这个队列传入到后处理的材质里面。
在这里插入图片描述

  这个时候,这几张RenderTexture实际上是下面这样的:
在这里插入图片描述

  后处理的Shader很简单,就是把这几张Texture按照先后顺序,用过不同的透明度去合成在一起:
在这里插入图片描述

  这样,残影的效果就做出来了。如果想修改残影的颜色,也是直接在后处理的时候,给残影的Texture乘以一个颜色就行了。

二、优缺点

1、优点

  对比起BakeMesh方法,这个后处理的方式,并不需要渲染多很多个角色的网格,只是需要多一个摄像机渲染多一次所有需要残影的角色而已。我们可以做一个优化,当某个角色需要做残影,就把它设置为专门的Layer,让这个残影摄像机能渲染到。平时没有需要残影的角色的时候,这个摄像机是什么都看不到。
  然后,场景里面就算有非常多的角色同时残影,最多也就是每个角色多渲染一次就够了,对于渲染方面的性能消耗还是很友好的。就是牺牲点内存,把这张RenderTexture复制并保存在内存里面。这个消耗我觉得并不是很大。
  如果想在这个基础上做其他效果,也是很轻松的,比如想对残影做模糊,或者Bloom,或者校色,其实就是对保存的这几张Texture做处理就行了,可以实现的效果非常多。

2、缺点

  由于是保存多张Texture作为合成残影的基础,所以究竟保存多少张合适,是一个问题。如果保存得少,那么残影的效果不是很明显,如果保存得多,内存的占用也会比较多。

三、代码

由于是Demo,所以写得比较简单一点,没有做优化,大家看个原理吧。

1、C#

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class MoveImageEffectCtrl : MonoBehaviour
{
    // Start is called before the first frame update
    private List<Texture> rtList;
    public Camera subCam;
    public bool isMove = false;
    private Texture2D blackTex;
    public Material mat;
    public int spaceTime = 10;
    private int countTime = 0;
    private Vector3 oldPos;
    public GameObject role;
    void Start()
    {
        CreateBlackTexture();
    }

    // Update is called once per frame
    void Update()
    {
        countTime++;
        if (countTime % spaceTime == 0)
        {
            CheckMove();
        }
    }

    private void CheckMove()
    {
        if (Vector3.Distance(oldPos, role.transform.position) > 0)
        {
            isMove = true;
            RenderTexture camTarget = subCam.targetTexture;
            RenderTexture rt = RenderTexture.GetTemporary(camTarget.width, camTarget.height);
            CopyRender(camTarget, rt);
            AddToRTList(rt);
            oldPos = role.transform.position;
        }
        else
        {
            isMove = false;
        }
    }
    private void CopyRender(RenderTexture source, RenderTexture destination)
    {
        Graphics.Blit(source, destination);
    }
    private void CreateBlackTexture()
    {
        blackTex = new Texture2D(128, 128);
        for (int i = 0; i < 128; i++)
        {
            for (int j = 0; j < 128; j++)
            {
                blackTex.SetPixel(i, j, Color.clear);
            }
        }
        blackTex.Apply();
    }

    private void AddToRTList(Texture rt)
    {
        if (rtList == null)
        {
            rtList = new List<Texture>();
        }
        rtList.Add(rt);
        if (rtList.Count > 5)
        {
            for (int i = 0; i < rtList.Count - 5; i++)
            {
                Texture tex = rtList[0];
                rtList.RemoveAt(0);
                if (tex is RenderTexture)
                {
                    RenderTexture.ReleaseTemporary((RenderTexture)tex);
                }
            }
        }
    }

    private void SetTexToMat()
    {
        if (isMove == false)
        {
            mat.SetFloat("_isMove", 0);
        }
        else
        {
            mat.SetFloat("_isMove", 1);
            for (int i = 0; i < 5; i++)
            {
                string key = "_Tex" + (i + 1);
                Texture tex = GetTexById(i);
                mat.SetTexture(key, tex);
            }
        }
    }

    private Texture GetTexById(int id)
    {
        if (rtList == null || rtList.Count <= id)
        {
            return blackTex;
        }
        else
        {
            return rtList[id];
        }
    }

    private void OnRenderImage(RenderTexture source, RenderTexture destination)
    {
        SetTexToMat();
        if(isMove)
        {
            Graphics.Blit(source, destination, mat);
        }
        else
        {
            Graphics.Blit(source, destination);
        }
        
    }
}

2、Shader

Shader "Unlit/MoveEffectCom"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
		_Tex1("Tex1",2D) = "black"{}
		_Tex2("Tex2",2D) = "black"{}
		_Tex3("Tex3",2D) = "black"{}
		_Tex4("Tex4",2D) = "black"{}
		_Tex5("Tex5",2D) = "black"{}
		_isMove("isMove",Float) = 0
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        LOD 100

        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"

            struct appdata
            {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
            };

            struct v2f
            {
                float2 uv : TEXCOORD0;
                float4 vertex : SV_POSITION;
            };

            sampler2D _MainTex;
            float4 _MainTex_ST;
			sampler2D _Tex1;
			sampler2D _Tex2;
			sampler2D _Tex3;
			sampler2D _Tex4;
			sampler2D _Tex5;
			float _isMove;
            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv = TRANSFORM_TEX(v.uv, _MainTex);
                UNITY_TRANSFER_FOG(o,o.vertex);
                return o;
            }

            half4 frag (v2f i) : SV_Target
            {
                // sample the texture
				half4 col = tex2D(_MainTex, i.uv);
				if (_isMove > 0)
				{
					half4 addTex1 = tex2D(_Tex1, i.uv);
					half4 addTex2 = tex2D(_Tex2, i.uv);
					half4 addTex3 = tex2D(_Tex3, i.uv);
					half4 addTex4 = tex2D(_Tex4, i.uv);
					half4 addTex5 = tex2D(_Tex5, i.uv);
					half3 rgb = col.rgb + saturate(addTex1.rgb*addTex1.a*0.6f + addTex2.rgb*addTex2.a*0.5f + addTex3.rgb*addTex3.a*0.3f + addTex4.rgb*addTex4.a*0.2f + addTex5.rgb*addTex5.a*0.1f)*(1-col.a)*float3(1,0,0);
					rgb = saturate(rgb);
					col = half4(rgb, col.a);
				}


                return col;
            }
            ENDCG
        }
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/60533.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

前端如何打开钉钉(如何唤起注册表中路径与软件路径不关联的软件)

在前端唤起本地应用时&#xff0c;我查询了资料&#xff0c;在注册表中找到腾讯视频会议的注册表情况&#xff0c;如下&#xff1a; 在前端代码中加入 window.location.href"wemeet:"; 就可以直接唤起腾讯视频会议&#xff0c;但是我无法唤起钉钉 之所以会这样&…

使用手机相机检测电脑屏幕刷新率Hz

使用手机相机检测电脑屏幕刷新率Hz 1、电脑打开https://www.testufo.com/frameskipping 2、相机专业模式&#xff1a;快门1/10、ISO自动&#xff0c;拍摄一张照片。120Hz至少要有12个亮块&#xff0c;50Hz至少有6个亮块。 更改刷新速率 1、选择 “开始>设置>系统>显示…

Nodejs 第八章(npm搭建私服)

构建npm私服 构建私服有什么收益吗&#xff1f; 可以离线使用&#xff0c;你可以将npm私服部署到内网集群&#xff0c;这样离线也可以访问私有的包。提高包的安全性&#xff0c;使用私有的npm仓库可以更好的管理你的包&#xff0c;避免在使用公共的npm包的时候出现漏洞。提高…

性能测试/负载测试/压力测试之间的区别

做测试一年多来&#xff0c;虽然平时的工作都能很好的完成&#xff0c;但最近突然发现自己在关于测试的整体知识体系上面的了解很是欠缺&#xff0c;所以&#xff0c;在工作之余也做了一些测试方面的知识的补充。不足之处&#xff0c;还请大家多多交流&#xff0c;互相学习。 …

网络安全(黑客)工具篇

大家好。这篇文章给大家盘点那些年&#xff0c;我们一起玩过的网络安全工具。 一、反恶意代码软件 1.Malwarebytes 这是一个检测和删除恶意的软件&#xff0c;包括蠕虫&#xff0c;木马&#xff0c;后门&#xff0c;流氓&#xff0c;拨号器&#xff0c;间谍软件等等。快如闪…

【JAVASE】多态

⭐ 作者&#xff1a;小胡_不糊涂 &#x1f331; 作者主页&#xff1a;小胡_不糊涂的个人主页 &#x1f4c0; 收录专栏&#xff1a;浅谈Java &#x1f496; 持续更文&#xff0c;关注博主少走弯路&#xff0c;谢谢大家支持 &#x1f496; 多态 1. 概念2. 实现条件3. 重写4. 向上…

ORALCE RAC总结

一&#xff1a;ORACLE RAC集群安装 ASM磁盘管理 二&#xff1a;ORACLE RAC集群报错排查总结 三&#xff1a;ORACLE RAC集群报错排查总结 3.1 ORA-29760: instance_number parameter not specified RAC1节点&#xff0c;RAC2节点 解决 export ORACLE_SIDa81&#xff08;…

重新理解 RocketMQ Commit Log 存储协议

最近突然感觉&#xff1a;很多软件、硬件在设计上是有 root reason 的&#xff0c;不是 by desgin 如此&#xff0c;而是解决了那时、那个场景的那个需求。一旦了解后&#xff0c;就会感觉在和设计者对话&#xff0c;了解他们的思路&#xff0c;学习他们的方法&#xff0c;思维…

基于FPGA的超声波测距——UART串口输出

文章目录 前言一、超声波模块介绍1、产品特点2、超声波模块的时序图 二、系统设计1、系统模块框图2、RTL视图 三、源码1、div_clk_us(1us的分频)2、产生驱动超声波的信号3、串口发送模块4、HC_SR04_uart(顶层文件) 四、效果五、总结六、参考资料 前言 环境&#xff1a; 1、Quar…

阿里云安全组设置

简介​ 云主机安全组必须打开如下端口&#xff1a; ssh&#xff1a;22http&#xff1a;80https&#xff1a;443ftp&#xff1a;21、20000&#xff5e;30000 阿里云安全组端口开放教程​ 腾讯云安全组端口开放教程​ 华为云安全组端口开放教程​

ES6新增的语法

ES6实际上是一个泛指&#xff0c;泛指 ES2015 及后续的版本 1&#xff0c;let用于声明变量的关键字 let 声明的变量只在所处于的代码块内有效 if (true) { let a 10 } console.log(a) // a is not defined 2, let 不存在变量提升 console.log(a) // a is not deined let a 1…

Vue + ElementUI 实现可编辑表格及校验

效果 完整代码见文末 实现思路 使用两个表单分别用于实现修改和新增处理。 通过一个editIndex变量判断是否是编辑状态来决定是否展示输入框&#xff0c;当点击指定行的修改后进行设置即可&#xff1a; <el-table-columnv-for"(column, index) in columns":key&qu…

华为QinQ技术的基本qinq和灵活qinq 2种配置案例

基本qinq配置&#xff1a; 运营商pe设备在收到同一个公司的ce发来的的包&#xff0c;统一打上同样的vlan &#xff0c;如上图&#xff0c;同一个家公司两边统一打上vlan 2&#xff0c;等于在原内网vlan 10或20过来的包再统一打上vlan 2的标签&#xff0c;这样传输就不会和其它…

基于text2vec和faiss开发实现文档查询系统初体验

最近接触到了一些文本向量化的预训练模型&#xff0c;感觉相比较自己去基于gensim去训练词向量来说&#xff0c;使用预训练模型可能是更高效的方式了&#xff0c;正好有一个想法一直在想能够以什么样的形式间接的实现问答&#xff0c;说白了这里的问答跟我们理解的chatGPT类型的…

Spark 3.1.1 遇到的 from_json regexp_replace组合表达式慢问题的解决

背景 目前公司在从spark 2.4.x升级到3.1.1的时候&#xff0c;遇到了一类SQL极慢的情况&#xff0c;该SQL的如下(只列举了关键的)&#xff1a; select device_personas.* from(selectdevice_id, ads_id, from_json(regexp_replace(device_personas, (?<(\\{|,))"devic…

【数据结构】——线性表的相关习题

目录 题型一&#xff08;线性表的存储结构&#xff09;题型二&#xff08;链表的判空&#xff09;题型三&#xff08;单链表的建立&#xff09;题型四&#xff08;顺序表、单链表的插入删除操作&#xff09;题型五&#xff08;双链表的插入删除操作&#xff09;题型六&#xff…

【ARM Coresight 系列文章 2.5 - Coresight 寄存器:PIDR0-PIDR7,CIDR0-CIDR3 介绍】

文章目录 1.1 JEDEC 与 JEP1061.2 PIDR0-PIDR7(peripheral identification registers)1.2 CIDR0-CIDR3(Component Identification Registers) 1.1 JEDEC 与 JEP106 JEDEC和JEP106都是来自美国电子工业联合会&#xff08;JEDEC&#xff0c;Joint Electron Device Engineering C…

SpringBoot单元测试

目录 1.什么是单元测试? 2.单元测试有哪些好处? 3.Spring Boot单元测试使⽤ 单元测试的实现步骤 1. ⽣成单元测试类 2. 添加单元测试代码 2.1 .添加Spring Boot框架测试注解:SpringBootTest 2.2 添加单元测试业务逻辑 简单的断⾔说明 1.什么是单元测试? 单元测试(un…

分页Demo

目录 一、分页对象封装 分页数据对象 分页查询实体类 实体类用到的utils ServiceException StringUtils SqlUtil BaseMapperPlus,> BeanCopyUtils 二、示例 controller service dao 一、分页对象封装 分页数据对象 import cn.hutool.http.HttpStatus; import com.…

Python-flask项目入门

一、flask对于简单搭建一个基于python语言-的web项目非常简单 二、项目目录 示例代码 git路径 三、代码介绍 1、安装pip依赖 通过pip插入数据驱动依赖pip install flask-sqlalchemy 和 pip install pymysql 2.配置数据源 config.py DIALECT mysql DRIVER pymysql USERN…