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. Spring官方都推荐使用的@Transactional事务,为啥我不建议使用!

    GitHub 17k Star 的Java工程师成神之路,不来了解一下吗! GitHub 17k Star 的Java工程师成神之路,真的不来了解一下吗! GitHub 17k Star 的Java工 ...

  2. Linux运维学习第二周记

    1 梨花淡白柳深青, 2 柳絮飞时花满城. 3 惆怅东栏一株雪, 4 人生看得几清明. 老牛望春满地绿! 第二周直接要起飞了! 仍然是最基础最基础的东西,但也是特别多,的别重要! 第二周学记 1.Li ...

  3. Linux系统部署WEB项目(2020最新最详细)

    2020最新Linux系统发行版ContOS7演示部署WEB项目 为防止操作权限不足,建议切换root用户,当然如果你对Linux命令熟悉,能够自主完成权限更新操作,可以不考虑此推荐. 更多命令学习推 ...

  4. unittest学习

    unittest的四大特点 TestCase:测试用例.所有的用例都是直接继承与UnitTest.TestCase类. TestFixture:测试固件.setUp和tearDown分别作为前置条件和 ...

  5. node的function函数和路由代码的小例子

    1.node事件循环 事件: const events=require("events"); emt=new events.EventEmitter(); function eve ...

  6. StringUtils工具类(Apache lang3 )

    引入依赖 <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons- ...

  7. 在PostgreSQL中CREATE STATISTICS

    如果你用Postgres做了一些性能调优,你可能用过EXPLAIN.EXPLAIN向你展示了PostgreSQL计划器为所提供的语句生成的执行计划,它显示了语句所引用的表如何被扫描(使用顺序扫描.索引 ...

  8. centos7安装oracle版本的jdk

    Hadoop机器上的JDK,最好是Oracle的Java JDK,不然会有一些问题,比如可能没有JPS命令. 如果安装了其他版本的JDK,卸载掉!!! 1,查看是否已经安装了jdk java -ver ...

  9. 感知机vs支持向量机

    感知机原理:二维空间中找到一条直线可以把所有二元类别分离开,三维或多维空间中,找到一个分离超平面把所有二元类别分离开.而可把所有二元类别分离开的超平面不止一个,哪个是最好的呢?损失函数:所有误分类的点 ...

  10. D. Alyona and Strings 解析(思維、DP)

    Codeforce 682 D. Alyona and Strings 解析(思維.DP) 今天我們來看看CF682D 題目連結 題目 略,請直接看原題. 前言 a @copyright petjel ...