1.前言

近期在整理CommandBuffer这块资料,之前的了解一直较为混乱。

算不上新东西了,但个人觉得有些时候要比加一个摄像机再转RT廉价一些,至少省了深度排序这些操作。

本文使用两个例子讲解CommandBuffer如何使用,但在此之前稍稍总结一下官方CommandBuffer的案例。

2.官方案例

案例地址如下:

https://blogs.unity3d.com/cn/2015/02/06/extending-unity-5-rendering-pipeline-command-buffers/

文章尾部有Demo下载链接。

该demo包含3个例子。

第一个例子BlurryRefraction,和新建摄像机渲染RT类似,在渲染透明对象之前渲染屏幕,并做模糊处理。然后丢给shader转换到对应的UV空间,转换的代码和Grab一样不做赘述。

第二个例子DeferredCustomLights,这里灯光的容器模型和第三个例子的贴花容器模型差不多,都是为了空间剔除而建立的模型,灯光部分直接拿到GBuffer的数据进行绘制。

第三个例子DeferredDecals,和第二个差不多,容器模型直接是方块,而方块的投影方式又有点像地形的三方向投影。

3.学习案例

3.1 - 在延迟渲染环境下创建一个standard小球

总的来说坑还是蛮多的,unity的pbr这块本身和管线有所交互,所以commandBuffer要在光照和GBuffer两个阶段做插入。

其实最后光照还是有一些问题,所以暂时得出结论不要尝试直接绘制延迟光照的材质物体。

但如果是普通的vf shader没有太多问题,放在屏幕特效前做插入即可。

总之可以算作一次实践。

这是完成效果。

那么从头开始,首先按照常规思路是在GBuffer之后绘制一个球。

初始代码:

void OnEnable()
{
mCacheCommandBuffer = new CommandBuffer();
mCacheCommandBuffer.name = "TestCommandBuffer";
mCacheCommandBuffer.DrawRenderer(testRenderer, testMaterial, , -);
Camera.main.AddCommandBuffer(CameraEvent.AfterGBuffer, mCacheCommandBuffer);
}

释放:

void OnDisable()
{
Camera.main.RemoveCommandBuffer(CameraEvent.AfterGBuffer, mCacheCommandBuffer);
mCacheCommandBuffer.Dispose();
}

释放时Dispose要放在RemoveCommandBuffer之后调用。

2019/07/27补充:后来我又看了一下RenderingCommandBuffers的例子,释放时不需要手动调用Disposeh或是Release,只需要

RemoveCommandBuffer即可。

DrawRenderer比起DrawMesh多了很多自由度,但缺点是遇到多维子材质会比较棘手。

这里第四个参数是对应shader的pass,如果填写-1则所有pass都绘制

绘制效果如下

(直接画肯定是有问题的)

打开FrameDebugger看问题,把standard里所有的pass都绘制了出来这不是想要的。

这里看了下standard的pass,第三个pass针对的是延迟光照,后面都用pass 3来绘制。

而且还有个问题RT3的自发光信息不正确。

unity的GBuffer中四个RT分别是RT0-漫反射,RT1-高光,RT2-屏幕法线,RT3-自发光

翻阅了一下standard shader源码,发现可能是缺失了间接光照信息,而间接光照信息可能没有正确的初始化

不过发现了这么一个东西

void fragDeferred(
VertexOutputDeferred i,
out half4 outGBuffer0 : SV_Target0,
out half4 outGBuffer1 : SV_Target1,
out half4 outGBuffer2 : SV_Target2,
out half4 outEmission : SV_Target3 // RT3: emission (rgb), --unused-- (a)
#if defined(SHADOWS_SHADOWMASK) && (UNITY_ALLOWED_MRT_COUNT > 4)
, out half4 outShadowMask : SV_Target4 // RT4: shadowmask (rgba)
#endif
)
{
//...
}

也就是说可以自己定义输出的GBuffer,而且也可以指定只输出某一项GBuffer的值

void frag(
v2f i,
out half4 outEmission : SV_Target3
)
{
outEmission = ;
}

有时候这个还是蛮管用的,因为在CommandBuffer里Blit很多通道拷贝不了(应该是我技术不行)。

绘制多加一次:

mCacheCommandBuffer.DrawRenderer(testRenderer, testMaterial, , );
mCacheCommandBuffer.DrawRenderer(testRenderer, testMaterial_fix, , -);
Camera.main.AddCommandBuffer(CameraEvent.AfterGBuffer, mCacheCommandBuffer);

解决是解决了,但是和天空盒接触的地方就会没有光照。

查看了FrameDebugger,确认是光照部分出了问题,踩了一些坑之后发现在AfterLighting处再绘制一次即可。

void OnEnable()
{
mCacheCommandBuffer = new CommandBuffer();
mCacheCommandBuffer.name = "TestCommandBuffer"; mCacheCommandBuffer.DrawRenderer(testRenderer, testMaterial, , ); Camera.main.AddCommandBuffer(CameraEvent.AfterGBuffer, mCacheCommandBuffer); mCacheCommandBuffer2 = new CommandBuffer();
mCacheCommandBuffer2.name = "TestCommandBuffer2";
mCacheCommandBuffer2.DrawRenderer(testRenderer, testMaterial, , );
Camera.main.AddCommandBuffer(CameraEvent.AfterLighting, mCacheCommandBuffer2);
}

但这样依然有很多问题,例如只支持平行光,而且高光位置是错误的。应该是光照的初始化问题。

所以这个例子只能算作实践,总之不推荐这类对象的绘制。

3.2 - 使用CommandBuffer对挖洞模型进行模糊

之前别人做过,觉得这个案例有些意思,自己试了一下。

完成效果如下

这是完成效果,使用Stencil挖洞可以达到模拟半透明的效果,但如果渐变速度较慢时则会造成视觉上的不适。

而通过stencil来标记主角在屏幕中的位置,然后对主角在挖洞的基础上再做一次高斯模糊可以缓解这种视觉上的不适。

直接在原始模型上做Stencil会导致在shadow阶段Stencil数据被清除。

(2018/12/14补充: 在unity的camera中有这样一个接口:Camera.main.clearStencilAfterLightingPass,也可以尝试使用)

但是先不急着改变CommandBuffer的位置,先切换到正向渲染下看看Stencil不被清除的结果

可以看见即使Stencil生效,挖洞区域的Stencil也被挖掉了。所以必须想另外一个办法覆盖这个坏的Stencil。

我的思路是通过DrawRenderer在RenderSkybox之后绘制一个alpha为0的主角Renderer,并且带有正常Stencil,来覆盖掉旧的。

也就是需要两个CommandBuffer

mStencilFixCommandBuffer = new CommandBuffer();
mStencilFixCommandBuffer.name = "StencilFix"; for (int i = ; i < playerRenderers.Length; i++)
{
var item = playerRenderers[i];
mStencilFixCommandBuffer.DrawRenderer(item, playerReplaceMaterial, , -);
} Camera.main.AddCommandBuffer(CameraEvent.AfterSkybox, mStencilFixCommandBuffer);
Camera.main.AddCommandBuffer(CameraEvent.BeforeReflections, mBlurCommandBuffer);

可以看见这个正确的Stencil已经绘制上去了(当然也可以用这个方法修改深度,GBuffer)

然后就是模糊采样的问题,在CommandBuffer中你不能插入Lambda的CPU代码去执行异步内容

所以这里用几个RT来回切换做到重复采样,这里参考官方CommandBuffer里的第一个例子,也是需要用两个RT来回切换。

CommandBuffer里新建RT建议像下面这样,而不是用RenderTexture创建:

mBlurTempRT1 = Shader.PropertyToID("BlurTempRT1");
mBlurCommandBuffer.GetTemporaryRT(mBlurTempRT1, -, -, );

当然这么用也取不出RT对象,只能通过索引进行操作。

这里GetTemporaryRT方法第二和三个参数指定了分辨率,-1为默认值,-2为一半大小分辨率,-3为1/3以此类推

这时我遇到了第二个坑,Stencil信息不能通过通道单独拷贝出来,只有在和CameraTarget进行Blit操作时,才能读到Stencil信息

读Stencil信息是这样的,必须Blit的目标通道有Stencil才行(有待查证),比如下面这种就有问题:

mBlurCommandBuffer.Blit(BuiltinRenderTextureType.CameraTarget, mBlurTempRT1, mat);
mBlurCommandBuffer.Blit(mBlurTempRT1, BuiltinRenderTextureType.CameraTarget);

正确用法:

mBlurCommandBuffer.Blit(BuiltinRenderTextureType.CameraTarget, mBlurTempRT1);

for (int i = ; i < sampleNum - ; i++)
{
mBlurCommandBuffer.Blit(mBlurTempRT1, BuiltinRenderTextureType.CameraTarget, blurMaterial);
mBlurCommandBuffer.Blit(BuiltinRenderTextureType.CameraTarget, mBlurTempRT1);
} mBlurCommandBuffer.Blit(mBlurTempRT1, BuiltinRenderTextureType.CameraTarget, blurMaterial);

所以这里的模糊迭代这么做(这里理解的不太清晰,代码还可以优化)

最后效果也就达到了

就写到这里,本来还想做一个用到CommandBuffer的UI面板3D模型展示,但后来还是遇到了一些问题。

所以还是创建一个新相机吧。

测试工程地址:https://gitee.com/Hont/CommandBufferExample

(unity 2017.4)

Unity CommandBuffer的一些学习整理的更多相关文章

  1. js数组学习整理

    原文地址:js数组学习整理 常用的js数组操作方法及原理 1.声明数组的方式 var colors = new Array();//空的数组 var colors = new Array(3); // ...

  2. TweenMax学习整理--特有属性

    TweenMax学习整理--特有属性   构造函数:TweenMax(target:Object, duration:Number, vars:Object) target:Object -- 需要缓 ...

  3. HttpClient学习整理

    HttpClient简介HttpClient 功能介绍    1. 读取网页(HTTP/HTTPS)内容    2.使用POST方式提交数据(httpClient3)    3. 处理页面重定向    ...

  4. !!对python列表学习整理列表及数组详细介绍

    1.Python的数组分三种类型:(详细见 http://blog.sina.com.cn/s/blog_6b783cbd0100q2ba.html) (1) list 普通的链表,初始化后可以通过特 ...

  5. Java设计模式(学习整理)---命令模式

    设计模式之Command(学习整理) 1.Command定义 不少Command模式的代码都是针对图形界面的,它实际就是菜单命令,我们在一个下拉菜单选择一个命令时,然后会执行一些动作. 将这些命令封装 ...

  6. Wix学习整理(5)——安装时填写注册表

    原文:Wix学习整理(5)--安装时填写注册表 一 Microsoft操作系统的注册表 什么是注册表? 注册表是Mircrosoft Windows中的一个重要的数据库,用于存储系统和应用程序的设置信 ...

  7. Wix学习整理(6)——安装快捷方式

    原文:Wix学习整理(6)--安装快捷方式 一 为HelloWorld案例添加安装快捷方式 通常我们安装一个应用软件的时候,都喜欢在桌面或开始菜单中添加快捷方式以便我们快速访问.现在我们就在上篇添加注 ...

  8. Wix学习整理(7)——在开始菜单中为HelloWorld添加卸载快捷方式

    原文:Wix学习整理(7)--在开始菜单中为HelloWorld添加卸载快捷方式 通过前面的几篇随笔,我们已经给我们的HelloWorld提供了填写注册表信息,以及开始菜单快捷方式和桌面快捷方式.这些 ...

  9. Wix学习整理(4)——关于WiX文件格式和案例HelloWorld的分析

    原文:Wix学习整理(4)--关于WiX文件格式和案例HelloWorld的分析 关于WiX文件格式 .wxs是WiX的源文件扩展名..wxs文件以类XML文件的格式来指定了要构造Windows In ...

随机推荐

  1. 百度地图api的用法

    功能: 1.点击"江干区",地图自动定位到该区域,并且该区域出现overlay(红色) 2.点击"派出所"."社区"级别时,地图也自动定位同 ...

  2. markdown常用语法简记

    一级标题 二级标题 三级标题 ..... 无序列表 First Second Third 有序列表 第一条 第二条 第三条 链接 我的github主页 锚点 无序列表 代码块 var vm = new ...

  3. 学习React系列(九)——高阶函数

    定义:高阶组件就是一个函数,且该函数接收一个组件作为参数,并返回一个新的组件. (上一篇已经说过了高阶组件可以用来解决交叉问题) 一.不要改变原始组件,使用组合 class A extends Rea ...

  4. Resource 的 IsSealed 问题

    WFP 的 Generic.xaml ,App.xaml 等中的资源会被调用 Freezable. 在后台对该资源进行修改等操作会被提示.资源为密封对象. 如果,确定需要在后台对资源进行修改. 则需要 ...

  5. php Redis函数使用总结(string,hash,list, set , sort set )

    对于:string, set , sort set , hash 的增,改操作,是同一个命令,但是把它当改操作时,及时成功返回值依旧为0 对于:list结构来说,增删改查自有一套方法.   <? ...

  6. JS实现数组去重方法总结(六种方法)

    方法一: 双层循环,外层循环元素,内层循环时比较值 如果有相同的值则跳过,不相同则push进数组 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 Array. ...

  7. Apache 安装与配置(WIN10)

    本地坏境:windows 10 Pro 1709 Apache版本:httpd-2.4.32-Win64-VC15 Apache下载地址:https://www.apachelounge.com/do ...

  8. RPO(Relative Path Overwrite)

    Conception(Relative vs Absolute) Abosolute Path: "/etc/hosts"(in Linux), "C:\Windows\ ...

  9. Baidu音乐爬虫

    Baidu音乐歌曲爬虫: 1.分析Baidu音乐歌曲下载接口,组装参数 2.判断是否需要登录 a.使用cookie b.使用selenium 3.歌曲信息页面分析 4.数据表设计 歌曲类型表 歌曲表 ...

  10. spring mvc中的注解说明

    注解扫描 context:component-scan 包扫描 <context:component-scan base-package="org.bdp"> < ...