文章目录

  • 文档
  • GrabPass有两种写法
  • 区别
    • 使用GrabPass { } 的方式
      • 应用
      • Profiler > Frame Debugger
    • 使用GrabPass { "TheGrabTextureName" }方式
      • 两图对比一下
  • 不同Shader中使用了一样的GrabPass {"Name"}
  • 注意性能
    • CSharp
    • Shader

文档

关于Unity Shader中的GrabPass说明文档:

  • 官方的ShaderLab: GrabPass
  • CSDN其他博主翻译的ShaderLab: GrabPass

GrabPass有两种写法

  • GrapPass { }
  • GrabPass { “TheGrabTextureName” }

两种写法的去写在哪呢。
文档中有说明,但是可能说的还是不够清楚。

我用自己总结的:

  • GrabPass { }每次Drawcall中的Shader的GrabPass使用时都会中屏幕内容抓取一次绘制的内容,并保存在默认的命为_GrabTexture的纹理中
  • GrabPass { "TheGrabTextureName" }每一帧Drawcall中的Shader的GrabPass中,第一次调用该GrabPass抓取的内容,保存在TheGrabTextureName的纹理中,后面Drawcall或是pass调用的GrabPass { "TheGrabTextureName" }只要TheGrabTextureName纹理名字与之前GrabPass { "TheGrabTextureName" }中的TheGrabTextureName相同,都不会再执行GrabPass的过程,而直接使用之前Grab好的纹理对象内容。

下面我在实际Unity测试项目中测试结果写下来。

区别

我写了个测试用的,类似毛玻璃的模糊效果的Shader,如下:

使用GrabPass { } 的方式

// jave.lin 2020.03.04
Shader "Custom/GrabTexBlur" {Properties {_Blur ("_Blur", Range(0, 1)) = 0}SubShader {Tags { "Queue"="Transparent" "RenderType"="Transparent" }GrabPass { }Pass {CGPROGRAM#pragma vertex vert#pragma fragment frag#include "UnityCG.cginc"struct appdata {float4 vertex : POSITION;float2 uv : TEXCOORD0;};struct v2f {float4 vertex : SV_POSITION;float4 grabPos : TEXCOORD1;float4 worldPos : TEXCOORD2;};sampler2D _GrabTexture;float4 _GrabTexture_TexelSize;fixed _Blur;fixed3 blur(float2 uv) {fixed3 sum = 0;const int blurSize = 4;const int initV = blurSize / 2;const int maxV = initV + 1;for (int i = -initV; i < maxV; i++) {for (int j = -initV; j < maxV; j++) {sum += tex2D(_GrabTexture, uv + float2(i * _GrabTexture_TexelSize.x, j * _GrabTexture_TexelSize.y));}}// sum /= (blurSize + 1) * (blurSize + 1); // 这句时正确的效果sum /= blurSize * blurSize; // 这句时为了测试两个Sphere交集部分更亮的效果return sum;}v2f vert (appdata v) {v2f o;o.worldPos = mul(unity_ObjectToWorld, v.vertex);o.vertex = UnityObjectToClipPos(v.vertex);o.grabPos = ComputeGrabScreenPos(o.vertex);return o;}fixed4 frag (v2f i) : SV_Target {i.grabPos.xy /= i.grabPos.w;fixed4 col = tex2D(_GrabTexture, i.grabPos.xy);col.rgb = lerp(col.rgb, blur(i.grabPos.xy), _Blur);return col;}ENDCG}}
}

应用

  • 场景中新建一个Sphere球体。
  • 创建Material材质,设置使用的Shader。
  • 给球体设置材质。

效果如下图

Profiler > Frame Debugger

打开Frame Debugger查看绘制过程,因为shader指定在transparent的queue绘制队列,所以直接看TransparentGeometry下绘制的内容就好

可以看到有一个Grab RenderTexture的绘制

这时再复制一个毛玻璃的Sphere,然后再看看绘制过程。

这次看到有两次Grab RenderTexture的绘制

这是GrabPass { } 的绘制过程。

看看运行效果,记住这个效果,与下面的其他方式是不一样的

使用GrabPass { “TheGrabTextureName” }方式

shader还是和上面的一样,都是毛玻璃的测试shader。
只不过,这次我们给GrabPass出来的屏幕内容的纹理定义了一个名字。

GrabPass { "_GrabTexture" }

注意,如果你起的名字与默认的_GrabTexture相同也是没问题的,只要用了这种给GrabPass出来的纹理起个名字,就和没使用名字的处理流程是不一样的

然后再看看绘制过程

可以看到,这次虽然绘制了两个Sphere(黄色框那),但是Grab RenderTexture只执行了一次,因为都使用了GrabPass {“Name”}的方式,并且Name是一样的。那么后续的GrabPass就直接使用之前的纹理。

效果上会也和没起名字的不一样

两图对比一下

首先,shader中有这么一句故意使用的:

                // sum /= (blurSize + 1) * (blurSize + 1); // 这句时正确的效果sum /= blurSize * blurSize; // 这句时为了测试两个Sphere交集部分更亮的效果

因为少除了一些采样数据均值权重,所以整体权重比之前大,就会更亮。

  • GrabPass{ }的方式,在两球体交集部分会比较亮,原因是:GrabPass { } 每次Drawcall时的都重新先取拿一次当前绘制屏幕内容的内容到一个纹理中,所以第一个球体对Grab出来的屏幕像素内容处理逻辑了,导致某些像素比较亮了。但是第二个球体再次Grab出来的屏幕像素时,有些与第一个球体的像素集合有交集的本身有处理过,所以亮度本身比较高了,那么再次处理亮度,就会亮上加亮。
  • 而GrabPass { “Name” } 的方式,区别在于,第二个球再次GrabPass { “Name” }时发现这个"Name"的纹理之前有了,就不再重新抓取当前屏幕内容了。所以使用的还是第一个球体执行毛玻璃+亮度之前的原始屏幕内容,所以交集出的部分不会更亮。第一次球体绘制的与第二次球体绘制有交集的那部分内容,都给第二次球体绘制的覆盖了。

不同Shader中使用了一样的GrabPass {“Name”}

再添加另一个shader,此shader作用就是添加亮度的,如下

// jave.lin 2020.03.04
Shader "Custom/GrabTexBrightness" {Properties {_Brightness ("_Brightness", Range(1, 2)) = 1}SubShader {Tags { "Queue"="Transparent" "RenderType"="Transparent" }GrabPass { "_GrabTexture" }Pass {CGPROGRAM#pragma vertex vert#pragma fragment frag#include "UnityCG.cginc"struct appdata {float4 vertex : POSITION;};struct v2f {float4 vertex : SV_POSITION;float4 grabPos : TEXCOORD1;};sampler2D _GrabTexture;half _Brightness;v2f vert (appdata v) {v2f o;o.vertex = UnityObjectToClipPos(v.vertex);o.grabPos = ComputeGrabScreenPos(o.vertex);return o;}fixed4 frag (v2f i) : SV_Target {return tex2Dproj(_GrabTexture, i.grabPos) * _Brightness;}ENDCG}}
}

可以看到使用了GrabPass { “_GrabTexture” },名字与之前的毛玻璃+亮度方式的shader的"_GrabTexture"是一直的名字。

那么看看Frame Debugger的绘制过程

三次Draw Mesh,但只有一次Grab RenderTexxture,而且Draw Mesh BrightnessSphere与后面两次BlurSphere的Shader是不一样的,只不过他们使用的GrabPass {“Name”}中的Name是一样的。

注意性能

从Frame Debugger中的GrabPass的处理过程,显示的是:Grab RenderTexture,注意后面是RenderTexture

那么大概也能到Unity引擎在底层遍历每个Shader->SubShader->pass中,之要是GrabPass的Pass,都会判断使用的格式,是否有名字,然后有名字的会将Grab出来的纹理放到一个hashTable中,类似的字典中[name] = grabTexture的方式存着,方便下次使用。

而RenderTexture就是我们说的RT,我们在Project视图下也可以手动创建RT。指定分辨率,滤波方式,是否记录深度,模板,像素格式,等属性。

那就是说,GrabPass其实类似下面的代码处理的过程

注意都是猜测,具体调用底层的渲染API这个需要使用一个分析工具才能确定

CSharp

RenderTexture grabRT = ...;
Camera c= ...;
c.targetTexture = grabRT;
c.Render();// 单个材质shader的
Material mat = ...;
mat.SetTexture("_GrabTexture", grabRT");// 所有shader的
Shader.SetGlobalTexture("_GrabTexture", grabRT);

Shader

sampler2D _GrabTexture;

而这个过程是很费(耗费性能)的

所以我们尽量能用GrabPass {“Name”} 就不用GrabPass{ }


刚开始我以为是RT的方式,后来发现一篇文章,虽然不是将GrabPass的,是将一般后效使用的MonoBehaviour类的回调OnRenderImage的方法下的Graphics.Blit,也是挺卡的,在一般的手机上。

这篇文章中有讲解:在使用Mali系列的Android手机。他用Mali Graphics Debugger看到底层渲染API的调用。

图中可以看到Unity的Profiler中,有显示调用的是RenderTexture.GrabPixels

中MGD(Mali Graphics Debugger)中查看底层API

在glReadPixels的最后个参数不为空,则表示数据从显存传输到系统内存,从CPU到GPU的逆向传输,这是非常缓慢的过程,并且是阻塞模式。

2020.03.15 更新,在看到unity 的SIGGRAPH2011的某个文档有说明:
GrabPass {“name”}
• new in 3.4: only copies the color buffer once per frame (_Grab is shared)

所以确定,调用的就是glReadPixel来读取ColorBuffer的像素的。

在FORCE FIELD EFFECT那部分有说明,文档:SIGGRAPH2011 Special Effect with Depth.pdf
如果多年后,下载不了,链接无效了,可以点击这里(Passworld:cmte)下载(我收藏到网盘了)

Unity Shader GrabPass 使用注意的问题相关推荐

  1. Unity Shader - GrabPass 实现武器热扭曲拖尾效果

    文章目录 先来看看效果 实现思路 Unity带的TrailRender组件 编写脚本实现 CSharp Shader 参数 注意性能 还可以优化 总结 Project 以前龙之谷喜欢选战士,帅气. 战 ...

  2. Unity Shader GrabPass 抓屏幕 截屏

    Shader 抓屏 截屏 常用途径: 1.截取全屏作为截图储存 (常用). 2.截取全屏,模糊处理当作背景. 3.接入屏幕中某些指定的画面. 抓屏命令: GradPass{"Name&quo ...

  3. 【Unity Shader】Unity中利用GrabPass实现玻璃效果

    <入门精要>中模拟玻璃是用了Unity里的一个特殊的Pass来实现的,这个Pass就是GrabPass,比起上一篇博客实现镜子的方法,这个方法我认为相对复杂,因此在实现之前需要对GrabP ...

  4. Unity Shader着色器优化

    对游戏开发者而言,着色器长久以来就是游戏开发中的重要部分,在Unity中编写并实现着色器的过程直观且高效,优秀的着色器还可以创造非常精美的游戏画面,同时保证极高的性能.今天将由Unity的技术工程师张 ...

  5. Unity Shader之磨砂玻璃与水雾玻璃效果

    导读 玻璃效果是游戏场景中常见的效果之一,除却普通的透明玻璃外,磨砂玻璃也是较为常见的效果.玻璃与场景中的其他物体也会有交互,例如,浴室中的玻璃.雨天的窗户会在水汽的作用下带有一定差别的雾效.本文以U ...

  6. Unity Shader 水多种元素的实现(反射、折射、菲涅尔、深浅、浪花/泡沫、水波、可交互)

    综合效果 经过各元素叠加 和 程序的审美调参 后的综合效果 交互的水波与边缘浪花的合并需要优化一下 反射 两种方案: cubeMap 以水面对称设一个摄像机 cubeMap 实现:反射探针生成Cube ...

  7. 学习Shader Unity Shader 基础

    1.如何充分利用 Unity Shader 来为我们的游戏增光添彩? 材质和 Unity Shader: 在Unity中,我们需要配合使用材质(Material)和 Unity Shader 才能达到 ...

  8. Unity Shader - 模仿RenderImage制作全屏Quad,可以制作自定义后处理的流程

    文章目录 先尝试GL类来制作 Shader CSharp 画个三角型 画个全屏的Quad 发现GL没有RenderTarget之类的 使用CommandBuffer来绘制全屏的Quad GL渲染到目标 ...

  9. Unity Shader - 实现简单水体 - 浅水到深水颜色控制

    文章目录 制作步骤 准备好水体网格 扰动水体网格 添加水体网格色调,纹理 放置海上放哨点(一些随便放的立方体) 添加水的深浅透视效果 添加水光效 重构水顶点法线 正交的相机的深度需要注意 改进 Pro ...

最新文章

  1. 互联网时代的云服务器四大功能
  2. Shell Scipt 命令行带参数,输出log
  3. 配置web监控及报警
  4. java 字符串 float_Java 字符串转float运算 float转字符串
  5. 如何编写高质量的代码二 - 类的设计
  6. 如何让Activiti-Explorer使用sql server数据库
  7. 计算机控制技术微课,课程名称:微型计算机控制技术课程
  8. nifi mysql to mysql_NiFi 实战
  9. 如何用Android Stuido 调用百度翻译的API
  10. Java中Method.invoke方法
  11. tpshop php版本,TPshop安装向导 - Powered by TPshop
  12. OpenCV中的模糊处理(python)
  13. 沈剑:技术核心管理者的时间,都只花在这 20% 的事情上
  14. webp转换gif动图的方法-批量转换并保留动画效果
  15. Vue3定义全局变量/方法
  16. nginx(项目部署)linux版
  17. Transformer:让ChatGPT站在肩膀上的巨人?
  18. iOS百度地图SDK之实时绘制轨迹(后台仍执行)
  19. spring RestTemplate 实例(NameValuePair)
  20. Cosmos系列-2. Cosmos SDK

热门文章

  1. Printing 1 to 1000 without loop or conditionals
  2. Pt100转RS-485,热电阻温度Modbus数据采集模块 WJ125
  3. 虚拟光驱文件bin/cue到iso的转换
  4. 路由器 dhcp服务器的作用,你可知道在路由器中DHCP是什么吗
  5. EFS】NV65633-LTE搜网能力的NV值、格式、配置
  6. python二维散点图绘画详解
  7. ipqc的工作流程图_品质部各人员工作流程图
  8. PS无法打开png和jpg
  9. 全景图的种类及opencv实现
  10. ansible使用之——网络设备自动巡检