1.command buffer具有很高的灵活性,它的作用是预定义一些渲染指令,然后在我们想要执行的时候去执行这些指令(见图1),绿点表示可以在“Forward Rendering Path”和“Deferred Path”中执行这些命令的阶段。

  查看Unity文档,我们有以下事件可以在其中添加CommandBuffer的指令。

  https://docs.unity3d.com/ScriptReference/Rendering.CameraEvent.html

图1.1 渲染管线

  通常,在实现某些屏幕后处理时(比如Bloom效果、屏幕灰化、物体的外轮廓等),场景的所有物体都会受到处理,用command buffer的话,就能选择性地处理物体的效果。command buffer还有一个重要的功能,就是它能代替GrabPass{},去截取在它身后的图像,然后去实现水面的扰动、火焰的热空气扰动、玻璃的伪折射等的效果,在移动端,它所消耗的性能比GrabPass{}的低很多。

2. 用command buffer实现物体发光轮廓

  

   为了减少代码的重复书写,首先我们将创建一个Common.cginc文件,以便各个shader都能调用。

  我们还需要一些GaussianBlur以达到我们要处理的效果。关于高斯模糊可以看这一点。 Gaussian Kernel Calculator.

   现在,给出原图的图片。

  图2.1

  

在初始状态下,我们想要中间的正方体是一个被选中的状态,粗略分解一下该操作步骤

1) 我们使用CommandBuffer中的DrawRenderer方法,将要实现效果的物体绘制出来,该(见图2.2)。

图2.2

2) 对于先前获得的纹理,我们将使用高斯模糊对其进行处理。 (见图2.3)。

图2.3

3) 用模糊过的纹理减去原始的纹理,乘以颜色和亮度,就可以得到发光外轮廓。(见图2.4)

 原始图像的正方体是一个白色物体,颜色值为1,而模糊过的图像的颜色值是小于1的,模糊图像减去原始图片的话是<0的,所以中间部分显示的是黑色。因为高斯模糊是一个加权平均操作,每个像素的颜色值都是由其本身和相邻像素的颜色值进行加权平均得到,越靠近像素本身,权值越高,越偏离像素,权值越低。所以,正方体周围的像素也进行加权平均操作,得到淡一点的颜色。

图2.4

为此,我们将首先创建负责处理所需效果GlowOutlineEffect.cs的类(此脚本附加到主相机),还包括要渲染的每个纹理所必需的着色器。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Rendering; namespace FI
{
public class GlowOutlineEffect : MonoBehaviour
{
#region ENUMS
private enum Pass
{
PrePass,
BlurredPass,
FinalPass,
Result
} [SerializeField]
private Pass passToShow;
#endregion #region PRIVATE_VARS
[SerializeField, Range(0, 3)]
private int iterations; [SerializeField, Range(0, 10)]
private float intensity; [SerializeField]
private Color glowColor; [SerializeField]
private Renderer[] toRender; private Material unlitMat;
private Material gaussianBlurMat;
private Material finalPassMat;
private Material glowOutlineMat; private Material showPrePassMat;
private Material showBlurredMat;
private Material showFinalPassMat; private CommandBuffer buffer; private int prePassTexID;
private int blurredTexID;
private int finalTexID; private int tempText1ID;
#endregion #region UNITY_API void Start()
{ //初始化
InitializeBuffer();
InitializeMaterials();
InitializeProperties();
} // Update is called once per frame
void Update()
{
UpdateBuffer();
UpdateGlowProperties();
MouseButtonInput();
}
#endregion #region PRIVATE_METHODS private void MouseButtonInput()
{
if(Input.GetMouseButtonDown(0))
{
RaycastHit hit;
var ray = Camera.main.ScreenPointToRay(Input.mousePosition);
if(Physics.Raycast(ray,out hit))
{
if(hit.transform)
{
toRender[0] = hit.transform.GetComponentInChildren<Renderer>();
}
}
}
}
private void InitializeBuffer()
{
buffer = new CommandBuffer();
Camera.main.AddCommandBuffer(CameraEvent.BeforeImageEffects, buffer);
} private void InitializeMaterials()
{
unlitMat = new Material(Shader.Find("Unlit/Color"));
gaussianBlurMat = new Material(Shader.Find("FI/GaussianBlur"));
finalPassMat = new Material(Shader.Find("FI/FinalPass"));
glowOutlineMat = new Material(Shader.Find("FI/GlowOutline")); showPrePassMat = new Material(Shader.Find("FI/ShowPrePass"));
showBlurredMat = new Material(Shader.Find("FI/ShowBlurred"));
showFinalPassMat = new Material(Shader.Find("FI/ShowFinalPass"));
} private void InitializeProperties()
{
prePassTexID = Shader.PropertyToID("_PrePassTex");
blurredTexID = Shader.PropertyToID("_BlurredTex");
tempText1ID = Shader.PropertyToID("_TempTex1");
finalTexID = Shader.PropertyToID("_FinalTex");
} private void UpdateGlowProperties()
{
Shader.SetGlobalFloat("_Intensity", intensity);
Shader.SetGlobalColor("_GlowColor", glowColor);
} private void UpdateBuffer()
{
buffer.Clear(); UpdatePrePassTexture();
UpdateBlurredTexture();
UpdateFinalTexture();
} private void UpdatePrePassTexture()
{
//创建一个临时纹理,prePassTexID是纹理标识,
//第二和第三个参数为-1,表示该纹理的大小跟屏幕大小一样大。
//添加双线性过滤和抗锯齿的,以确保基本纹理尽可能清晰
buffer.GetTemporaryRT(prePassTexID, -1, -1,
0, FilterMode.Bilinear, RenderTextureFormat.ARGB32,
RenderTextureReadWrite.Default, QualitySettings.antiAliasing); buffer.SetRenderTarget(prePassTexID);
buffer.ClearRenderTarget(true, true, Color.clear); for (int i = 0; i < toRender.Length; i++)
{
if (toRender[i].gameObject.activeSelf)
{
buffer.DrawRenderer(toRender[i], unlitMat);
}
} } private void UpdateBlurredTexture()
{
/*创建临时纹理,第二和第三个参数为-2,代表该纹理的大小为屏幕大小的1/2
* 这样可以降低纹理的分辨率以获得更模糊的结果,并降低成本
*/
buffer.GetTemporaryRT(blurredTexID, -2, -2, 0, FilterMode.Bilinear);
buffer.GetTemporaryRT(tempText1ID, -2, -2, 0, FilterMode.Bilinear); //prePassTexID的纹理经过gaussianBlurMat材质的处理后,复制给blurredTexID
buffer.Blit(prePassTexID, blurredTexID, gaussianBlurMat); //循环迭代,迭代次数越多越模糊
for (int i = 0; i < iterations; i++)
{
//最后一个参数代表执行材质Shader里的哪一个Pass
//0代表执行该Shader的第一个Pass,即在水平方向进行模糊
//1代表执行该Shader的第二个Pass,即在垂直方向上进行模糊
buffer.Blit(blurredTexID, tempText1ID, gaussianBlurMat,0);
buffer.Blit(tempText1ID, blurredTexID, gaussianBlurMat,1);
}
} private void UpdateFinalTexture()
{
buffer.GetTemporaryRT(finalTexID, -1, -1, 0, FilterMode.Bilinear); buffer.Blit(null, finalTexID, finalPassMat);
} /// <summary>
/// 在所有的对象都渲染完后执行的方法,source是全屏幕的初始图像,
/// Blit()函数把source传给材质处理,在材质的Shader中用_MainTex
/// 去获取source的图像。
/// </summary>
/// <param name="source"></param>
/// <param name="destination"></param>
private void OnRenderImage(RenderTexture source, RenderTexture destination)
{
switch (passToShow)
{
case Pass.PrePass:
Graphics.Blit(null, destination, showPrePassMat);
break;
case Pass.BlurredPass:
Graphics.Blit(null, destination, showBlurredMat);
break;
case Pass.FinalPass:
Graphics.Blit(null, destination, showFinalPassMat);
break;
default:
Graphics.Blit(source, destination, glowOutlineMat);
break;
} }
#endregion
}
}

有了外发光轮廓的纹理后,剩下的就是将其添加到屏幕上渲染的纹理。

Shader "FI/GlowOutline"
{
Properties
{
_MainTex("Texture", 2D) = "white" {}
} SubShader
{
Pass
{
CGPROGRAM #pragma vertex vert
#pragma fragment frag #include "Common.cginc" sampler2D _FinalTex; fixed4 frag(v2f i) : SV_Target
{
return tex2D(_MainTex, i.uv) + tex2D(_FinalTex, i.uv);
} ENDCG
} }
}

Unity CommandBuffer物体轮廓的更多相关文章

  1. Unity 实现物体破碎效果(转)

    感谢网友分享,原文地址(How to Make an Object Shatter Into Smaller Fragments in Unity),中文翻译地址(Unity实现物体破碎效果) In ...

  2. Unity查找物体的子物体、孙物体

    Unity查找物体下的所有物体 本文提供全流程,中文翻译. Chinar 坚持将简单的生活方式,带给世人!(拥有更好的阅读体验 -- 高分辨率用户请根据需求调整网页缩放比例) Chinar -- 心分 ...

  3. opengl学习-利用模板测试勾画物体轮廓中出现的一个问题

    我在学习OpenGL模板测试勾画物体轮廓的时候,出现了这个问题: 这个出现的原因就是,改变摄像机的时候,每次绘制,上次绘制中模板缓冲区的数据没有清除的原因.也就是在while循环开始的时候,glCle ...

  4. Unity CommandBuffer的一些学习整理

    1.前言 近期在整理CommandBuffer这块资料,之前的了解一直较为混乱. 算不上新东西了,但个人觉得有些时候要比加一个摄像机再转RT廉价一些,至少省了深度排序这些操作. 本文使用两个例子讲解C ...

  5. unity 实现物体破碎效果的一些方法 - 细雨淅淅

    游戏越来越接近现实的感觉,如果有一个真是的 虚拟现实设备,可能我们真的会感觉是在真实世界.场景的逼真是在渲染效果.角色AI.游戏逻辑.物理效果等等一起导致的结果.现在游戏越来越大,除了渲染,物理估计是 ...

  6. unity 实现物体破碎效果的一些方法

    游戏越来越接近现实的感觉,如果有一个真是的 虚拟现实设备,可能我们真的会感觉是在真实世界.场景的逼真是在渲染效果.角色AI.游戏逻辑.物理效果等等一起导致的结果.现在游戏越来越大,除了渲染,物理估计是 ...

  7. 关于Unity动态物体无法向使用使用custom shader和lightmap的物体投射阴影

    最近在做unity shader forge和marmoset的优化,TA那边遇到了一个阴影显示的问题,具体如下:   在Forward Rendering状态下,静态场景使用了是shader for ...

  8. Unity 父物体与子物体位置

         酷跑片段本来想做三条轨道,然后通过切换轨道来做,后面发现一种巧妙的方法,利用物体的父级偏移来实现轨道的切换. 比如上图,实际运动的是Car对象,通过修改MineControler的左右位置( ...

  9. Unity 3D物体的点击事件响应以及NGUI坐标和世界坐标的互相转换

    Unity 版本:4.5 NGUI版本:3.6.5 参考链接:http://game.ceeger.com/Script/Camera/Camera.ScreenPointToRay.html,Uni ...

随机推荐

  1. centos8平台使用loginctl管理登录用户与session

    一,loginctl的用途: 控制 systemd 登录管理器 管理当前登录的用户和session 说明:刘宏缔的架构森林是一个专注架构的博客,地址:https://www.cnblogs.com/a ...

  2. selenium--数据填充

    from time import sleep from selenium import webdriver br = webdriver.Chrome() url = "https://ww ...

  3. Dubbo系列之 (七)网络层那些事(2)

    辅助链接 Dubbo系列之 (一)SPI扩展 Dubbo系列之 (二)Registry注册中心-注册(1) Dubbo系列之 (三)Registry注册中心-注册(2) Dubbo系列之 (四)服务订 ...

  4. PHP实现Bitmap的探索 - GMP扩展使用

    原文地址:https://blog.fanscore.cn/p/22/ 一.背景 公司当前有一个用户群的系统,核心功能是根据不同的条件组去不同的业务线中get符合条件的uid列表,然后存到redis中 ...

  5. python3异步爬虫 ——aiohttp模板使用

    一.简单使用和讲解 import aiohttp import asyncio async def fetch(client): async with client.get('http://httpb ...

  6. Go之NSQ简介,原理和使用

    NSQ简介 NSQ是Go语言编写的一个开源的实时分布式内存消息队列,其性能十分优异. NSQ 是实时的分布式消息处理平台,其设计的目的是用来大规模地处理每天数以十亿计级别的消息.它具有分布式和去中心化 ...

  7. Python&&Pip

    Pip简易使用 使用pip list命令就可以发现自己电脑里所安装库的名字.如图展示的出来的有package.Version.Location三列,package是下载的python库名,Versio ...

  8. F. Make It Connected 解析(思維、MST)

    Codeforce 1095 F. Make It Connected 解析(思維.MST) 今天我們來看看CF1095F 題目連結 題目 給你\(n\)個點,每個點\(u\)還有一個值\(a[u]\ ...

  9. SpringBoot + Swagger Demo

    Swagger是什么? Swagger 是一个规范且完整的框架,用于生成.描述.调用和可视化 RESTful 风格的 Web 服务.  Swagger 的目标是对 REST API 定义一个标准且和语 ...

  10. drf 认证校验及源码分析

    认证校验 认证校验是十分重要的,如用户如果不登陆就不能访问某些接口. 再比如用户不登陆就不能够对一个接口做哪些操作. drf中认证的写法流程如下: 1.写一个类,继承BaseAuthenticatio ...