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. python 操作conf配置文件方法

    参考文章链接:https://blog.csdn.net/qq_23587541/article/details/85019610

  2. 反射(Reflection)

    Java学习笔记--反射(Reflection) 关于反射 能够分析类能力的程序称之为反射(Reflection) 反射机制可以用来: 在运行时分析类的能力 在运行时检查对象,例如:编写一个适合所有类 ...

  3. Helium文档3-WebUI自动化-click点击

    前言 click点击方法在web UI自动化中使用频率非常高,此方法就是模拟鼠标左键单击动作 click入参说明 1.首先我们来分析一下click方法的代码 click(element):   &qu ...

  4. ES5新增数组的方法

    ES5新增数组的方法 ES5新增数组常见方法(indexOf/forEach/map/filter/some/every) .indexOf( data , start)  检测数组中是否存在指定数据 ...

  5. pytest-pyppeteer:在pytest中运行pyppeteer

    pytest-pyppeteer pytest-pyppeteer是我写的一个 pytest 插件,支持在 pytest 中运行pyppeteer,起因是为了解决工作中的一个测试需求,现在将其开源并做 ...

  6. Retrofit学习

    ---恢复内容开始--- public class MainActivity extends AppCompatActivity { @Override protected void onCreate ...

  7. Kerberos与票据的爱情故事

    0x01.Kerberos认证原理 Kerberos是一种认证机制.目的是通过密钥系统为客户端/服务器应用程序提供强大的可信任的第三方认证服务: 保护服务器防止错误的用户使用,同时保护它的用户使用正确 ...

  8. APIO2008免费道路

    题目大意 给定一张n个点m条边的图,图上有两种边,求保证有k条第一种边的情况下的最小生成树 传送门 题解 考虑最小生成树kruskal算法 先找到不含限制的最小生成树,然后就可以知道哪些第一种边是必选 ...

  9. Luogu P4208 [JSOI2008]最小生成树计数

    题意 给定一个 \(n\) 个点 \(m\) 条边的图,求最小生成树的个数. \(\texttt{Data Range:}1\leq n\leq 100,1\leq m\leq 10^4\) 题解 一 ...

  10. NB-IOT关键技术分析

    NB-IOT(NarrowBand Internet of Things,窄带IoT)是一种基于蜂窝的窄带物联网技术,支持低功耗设备在广域网的蜂窝数据连接.NB-IOT在物联网应用广泛,许多领域都充分 ...