Unity Shader GrabPass 使用注意的问题
文章目录
- 文档
- 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 使用注意的问题相关推荐
- Unity Shader - GrabPass 实现武器热扭曲拖尾效果
文章目录 先来看看效果 实现思路 Unity带的TrailRender组件 编写脚本实现 CSharp Shader 参数 注意性能 还可以优化 总结 Project 以前龙之谷喜欢选战士,帅气. 战 ...
- Unity Shader GrabPass 抓屏幕 截屏
Shader 抓屏 截屏 常用途径: 1.截取全屏作为截图储存 (常用). 2.截取全屏,模糊处理当作背景. 3.接入屏幕中某些指定的画面. 抓屏命令: GradPass{"Name&quo ...
- 【Unity Shader】Unity中利用GrabPass实现玻璃效果
<入门精要>中模拟玻璃是用了Unity里的一个特殊的Pass来实现的,这个Pass就是GrabPass,比起上一篇博客实现镜子的方法,这个方法我认为相对复杂,因此在实现之前需要对GrabP ...
- Unity Shader着色器优化
对游戏开发者而言,着色器长久以来就是游戏开发中的重要部分,在Unity中编写并实现着色器的过程直观且高效,优秀的着色器还可以创造非常精美的游戏画面,同时保证极高的性能.今天将由Unity的技术工程师张 ...
- Unity Shader之磨砂玻璃与水雾玻璃效果
导读 玻璃效果是游戏场景中常见的效果之一,除却普通的透明玻璃外,磨砂玻璃也是较为常见的效果.玻璃与场景中的其他物体也会有交互,例如,浴室中的玻璃.雨天的窗户会在水汽的作用下带有一定差别的雾效.本文以U ...
- Unity Shader 水多种元素的实现(反射、折射、菲涅尔、深浅、浪花/泡沫、水波、可交互)
综合效果 经过各元素叠加 和 程序的审美调参 后的综合效果 交互的水波与边缘浪花的合并需要优化一下 反射 两种方案: cubeMap 以水面对称设一个摄像机 cubeMap 实现:反射探针生成Cube ...
- 学习Shader Unity Shader 基础
1.如何充分利用 Unity Shader 来为我们的游戏增光添彩? 材质和 Unity Shader: 在Unity中,我们需要配合使用材质(Material)和 Unity Shader 才能达到 ...
- Unity Shader - 模仿RenderImage制作全屏Quad,可以制作自定义后处理的流程
文章目录 先尝试GL类来制作 Shader CSharp 画个三角型 画个全屏的Quad 发现GL没有RenderTarget之类的 使用CommandBuffer来绘制全屏的Quad GL渲染到目标 ...
- Unity Shader - 实现简单水体 - 浅水到深水颜色控制
文章目录 制作步骤 准备好水体网格 扰动水体网格 添加水体网格色调,纹理 放置海上放哨点(一些随便放的立方体) 添加水的深浅透视效果 添加水光效 重构水顶点法线 正交的相机的深度需要注意 改进 Pro ...
最新文章
- 互联网时代的云服务器四大功能
- Shell Scipt 命令行带参数,输出log
- 配置web监控及报警
- java 字符串 float_Java 字符串转float运算 float转字符串
- 如何编写高质量的代码二 - 类的设计
- 如何让Activiti-Explorer使用sql server数据库
- 计算机控制技术微课,课程名称:微型计算机控制技术课程
- nifi mysql to mysql_NiFi 实战
- 如何用Android Stuido 调用百度翻译的API
- Java中Method.invoke方法
- tpshop php版本,TPshop安装向导 - Powered by TPshop
- OpenCV中的模糊处理(python)
- 沈剑:技术核心管理者的时间,都只花在这 20% 的事情上
- webp转换gif动图的方法-批量转换并保留动画效果
- Vue3定义全局变量/方法
- nginx(项目部署)linux版
- Transformer:让ChatGPT站在肩膀上的巨人?
- iOS百度地图SDK之实时绘制轨迹(后台仍执行)
- spring RestTemplate 实例(NameValuePair)
- Cosmos系列-2. Cosmos SDK