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. gti 常用命令

    git add 文件 : 追踪指定文件git add . :追踪所有的文件git commit -m "注释" : 提交报本地仓库git push : 推送远程仓库git pull ...

  2. Linux用户和组管理命令-用户属性修改usermod

    用户属性修改 usermod 命令可以修改用户属性 格式: usermod [OPTION] login 常见选项: -u UID: 新UID -g GID: 新主组 -G GROUP1[,GROUP ...

  3. dubbo-config-spring自定义xml标签扩展

    要实现自定义自定义标签扩展,需要有如下步骤(在spring中定义了两个接口NamespaceHandler.BeanDefinitionParser,用来实现扩展) 1.设计配置属性和JavaBean ...

  4. Linux入门到放弃之一《在VMware虚拟机中安装Linux系统(RedHat)》

    1.启动VMware: 2.新建虚拟机: 3.自定义配置(1安装客户机操作系统点击"稍后安装操作系统"2选择客户机操作系统为Linux,版本为Red Hat Enterprise ...

  5. Java网关服务-AIO(三)

    Java网关服务-AIO(三) 概述 前两节中,我们已经获取了body的总长度,剩下的就是读出body,处理请求 ChannelServerHandler ChannelServerHandler即从 ...

  6. CTF-misc:老板,再来几道misc玩玩

    [BJDCTF 2nd]最简单的misc-y1ng 得到一个图片,提示格式损坏,修补一下文件头 然后得到一张图片 直接python16进制转字符串 >>> string = &quo ...

  7. B. Two Arrays 解析(思維)

    Codeforce 1417 B. Two Arrays 解析(思維) 今天我們來看看CF1417B 題目連結 題目 略,請直接看原題. 前言 a @copyright petjelinux 版權所有 ...

  8. linux系统软件启动sh脚本

    在系统维护中,编写脚本会帮助运维提高效率,现记录一个通用的软件启动脚本.脚本内容如下: #!/bin/bash # 软件启动程序包名称 APP_NAME=datadog-4.2.0.jar # 软件名 ...

  9. Docker composer搭建Spring Cloud Alibaba 运行环境(二)

    " Spring Cloud Alibaba要用到的组件很多,注册中心nacos, 限流sentinel, 数据库,网关等等.由于用到的组件相对较多,部署会很繁琐,最关键的是没有资源服务器, ...

  10. python 作业 日报模板输出

    1 #!/usr/bin/env python 2 # coding: utf-8 3 4 import numpy as np 5 import pandas as pd 6 7 path='C:/ ...