(转)GrabPass捕捉屏幕纹理

转自:http://blog.sina.com.cn/s/blog_89d90b7c0102va4m.html

最近在看热扭曲效果,里面用到了GrabPass。

之前看过官方翻译版本的说明http://blog.sina.com.cn/s/blog_89d90b7c01019run.html

但是还是无法理解GrabPass{}是捕捉物体后面的屏幕纹理,还是整个屏幕的纹理,至于为什么,可以看下面的例子,如果你知道原因求分享。。。

1.固定管线版本:

Shader "Custom/Grab" {
 Properties {
  //_MainTex ("Base (RGB)", 2D) = "white" {}
 }
 SubShader {
  // 在所有不透明对象之后绘制自己,更加靠近屏幕
  Tags { "Queue" = "Transparent" }
  // 通道1:捕捉对象之后的屏幕内容放到_GrabTexture纹理中
  GrabPass{}
  // 通道2:设置材质
  Pass{
   // 使用上面产生的纹理,进行颜色反相(1-原材质色)
   SetTexture[_GrabTexture]{combine one-texture}
  }
 }
 FallBack "Diffuse"
}

效果如下,它是取模型背后的屏幕纹理:

2.顶点,片段版本:

Shader "Custom/GrabAllVF" {
 Properties {
  //_MainTex ("Base (RGB)", 2D) = "white" {}
 }
 SubShader {
  // 在所有不透明对象之后绘制自己,更加靠近屏幕
  Tags{"Queue"="Transparent"}
  // 通道1:捕捉屏幕内容放到_GrabTexture纹理中
  GrabPass{}
  // 通道2:设置材质
  Pass{
   Name "pass2"
   CGPROGRAM
   #pragma vertex vert
            #pragma fragment frag
   #include "UnityCG.cginc"

   sampler2D _GrabTexture;
   float4 _GrabTexture_ST;
   // 片段程序的输入
   struct v2f {
                float4  pos : POSITION;
                float2  uv : TEXCOORD0;
            };
   v2f vert (appdata_base v)
            {
                v2f o;
                o.pos = mul(UNITY_MATRIX_MVP,v.vertex);
    o.uv = TRANSFORM_TEX(v.texcoord, _GrabTexture);
                return o;
            }
   float4 frag (v2f i) : COLOR
            {
    half4 texCol = tex2D(_GrabTexture, float2(1-i.uv.x , 1-i.uv.y));
    // 颜色反相,便于观察效果
                return 1 - texCol;
            }
   ENDCG
  }
 }
 FallBack "Diffuse"
}

效果如下:取到的是全屏的纹理:

1和2为什么取到的屏幕纹理不一样呢?

3.使用vf的方式,只获取物体后面的屏幕纹理,后面的扭曲效果会用到此方式,代码如下:

Shader "Custom/GrabVF" {
 Properties {
  //_MainTex ("Base (RGB)", 2D) = "white" {}
 }
 SubShader {
  // 在所有不透明对象之后绘制自己,更加靠近屏幕
  Tags{"Queue"="Transparent"}
  // 通道1:捕捉对象之后的屏幕内容放到_GrabTexture纹理中
  GrabPass{}
  // 通道2:设置材质
  Pass{
   Name "pass2"
   CGPROGRAM
   #pragma vertex vert
            #pragma fragment frag
   #include "UnityCG.cginc"

   sampler2D _GrabTexture;
   float4 _GrabTexture_ST;
   struct v2f {
                float4  pos : POSITION; // 输入的模型空间中,顶点坐标信息
                float4  uv : TEXCOORD0; // 材质信息也包含了xyzw,通常只用xy,但是这里由顶点生成
            };
   v2f vert (appdata_base v)
            {
                v2f o;
    // 从模型坐标-世界坐标-视坐标-(视觉平截体乘以投影矩阵并进行透视除法)-剪裁坐标
                o.pos = mul(UNITY_MATRIX_MVP,v.vertex);
    // 【自动生成纹理】通过输出的pos计算的纹理信息
    // 【解决平台差异】D3D原点在顶部(本机需要让y缩放乘以-1),openGL在底部
                #if UNITY_UV_STARTS_AT_TOP
    float scale = -1.0;
    #else
    float scale = 1.0;
    #endif
    // pos的范围是【-1,1】+1为【0,2】,乘以0.5变成uv的范围【0,1】
    // 不清楚为什么这样写,但是标准的写法就是这样
    o.uv.xy = (float2(o.pos.x, o.pos.y*scale) + o.pos.w) * 0.5;
    o.uv.zw = o.pos.zw;
                return o;
            }
   float4 frag (v2f i) : COLOR
            {
    // 对_GrabTexture纹理进行取样,进行2D纹理映射查找,后面传入的一定要四元纹理坐标。
    // UNITY_PROJ_COORD传入四元纹理坐标用于给tex2Dproj读取,但是多数平台上,返回一样的值。
    // 【自动生成的纹理UV】类型是float4,使用如下方式进行2D纹理映射查找
    //half4 texCol = tex2Dproj(_GrabTexture, UNITY_PROJ_COORD(i.uv));

    // 也可以使用tex2D进行采样,但是【自动生成的纹理UV】时必须要除以w转为齐次坐标
    float last_x = i.uv.x / i.uv.w;
    float last_y = i.uv.y / i.uv.w;
    half4 texCol = tex2D(_GrabTexture, float2(last_x, last_y));
    // 颜色反相,便于观察效果
                return 1 - texCol;
            }
   ENDCG
  }
 }
 FallBack "Diffuse"
}

没法一口吃个大胖子,学到这里才发现底层渲染原理很多都不了解,还的再仔细看看基础知识才行啊。

注:

补充于2015年1月4日,来自一位网友的提示。

2中的确是将屏幕的纹理赋值到样本对象GrabTexture上,所以前面的模型显示整个屏幕的纹理是正常现象。

3中是计算该模型顶点在屏幕坐标的纹理信息,unity封装的UnityCG.cginc代码中有:

inline float4 ComputeGrabScreenPos (float4 pos) {
      #if UNITY_UV_STARTS_AT_TOP
      float scale = -1.0;
      #else
      float scale = 1.0;
      #endif
      float4 o = pos * 0.5f;
      o.xy = float2(o.x, o.y*scale) + o.w;
      o.zw = pos.zw;
      return o;
    }

与3中给o.uv赋值的代码是一样的。所以在顶点程序中可以这样写:

 v2f vert (appdata_base v)
    {
     v2f o;
     // 从模型坐标-世界坐标-视坐标-(视觉平截体乘以投影矩阵并进行透视除法)-剪裁坐标
     o.pos = mul(UNITY_MATRIX_MVP,v.vertex);
     //o.uv = TRANSFORM_TEX(v.texcoord, _GrabTexture);// UV纹理坐标集信息来自屏幕样本对象

     float4 screenUV = ComputeGrabScreenPos(o.pos);//计算该模型顶点在屏幕坐标的纹理信息
     o.uv = screenUV.xy/screenUV.w;
     return o;
    }

嘿嘿,以后我们就可以不用再写这段代码了,直接用unity提供的函数ComputeGrabScreenPos,方便!

获取屏幕的纹理,还可以通过摄像机,将渲染的内容写到RenderTexture中,这样就可以不使用grabpass,一样达到获取屏幕纹理的目标,grabpass比较耗(官方说的,不过我在pc上创建了5000个对象进行测试,没发现太大差异,手机上没测过),在手机上比较适合这种方式。实现代码如下:

public class ScreenTexture : MonoBehaviour
{
    public Camera m_camera;          // 和主摄像机参数一样的拍照摄像机
    private RenderTexture m_tex;    // 摄像机渲染的材质
    public Material mat;            // 要控制的材质

    void Start()
    {
        m_tex = new RenderTexture(Screen.width, Screen.height, 16);
        m_camera.targetTexture = m_tex;
    }

    void OnPreCull()
    {
        mat.SetTexture("_MainTex", m_tex);   // 给shader的主材质赋值,为屏幕纹理
    }
    void OnPostRender()
    {
        mat.SetTexture("_MainTex", null);
    }
}
时间: 2024-11-05 13:36:49

(转)GrabPass捕捉屏幕纹理的相关文章

【浅墨Unity3D Shader编程】之三 光之城堡篇:子着色器、通道与标签的写法 & 纹理混合

本系列文章由@浅墨_毛星云 出品,转载请注明出处. 文章链接:http://blog.csdn.net/poem_qianmo/article/details/41175585 作者:毛星云(浅墨)    微博:http://weibo.com/u/1723155442 邮箱: [email protected] 本文介绍了Unity中子着色器.通道和标签相关的详细概念与写法,以及纹理的设置方法,基本的纹理混合写法,写了5个Shader作为本文Shader讲解的实战内容,最后创建了一个梦幻的光之

Unity3d之Shader编程:子着色器、通道与标签的写法 & 纹理混合

一.子着色器 Unity中的每一个着色器都包含一个subshader的列表,当Unity需要显示一个网格时,它能发现使用的着色器,并提取第一个能运行在当前用户的显示卡上的子着色器. 我们知道,子着色器定义了一个渲染通道的列表,并可选是否为所有通道初始化所需要的通用状态.子着色器的写法如下: Subshader{ [Tags] [CommonState] Passdef [Passdef ...] } 也就是通过可选标签,通用状态 和 一个Pass 定义的列表构成了子着色器. 当Unity选择用于

Unity3D教程宝典之Shader篇:第二十三讲GrabPass

转载自风宇冲Unity3D教程学院 GrabPass是一种特殊的pass类型.当物体将要被绘制时,它抓取屏幕内容并绘制到一张texture里.总体来说GrabPass开销较大,不如 AlphaBlend等指令.故能用AlphaBlend等指令实现的效果尽量用指令,不得不用GrabPass时才用GrabPass. (1)GrabPass{} 抓取当前屏幕内容,每次使用的开销都非常昂贵. texture用_GrabTexture获取(2)GrabPass{"TextureName"}将抓取

【Unity Shader】(九) ------ 高级纹理之渲染纹理及镜子与玻璃效果的实现

笔者使用的是 Unity 2018.2.0f2 + VS2017,建议读者使用与 Unity 2018 相近的版本,避免一些因为版本不一致而出现的问题. [Unity Shader](三) ------ 光照模型原理及漫反射和高光反射的实现 [Unity Shader](四) ------ 纹理之法线纹理.单张纹理及遮罩纹理的实现 [Unity Shader](五) ------ 透明效果之半透明效果的实现及原理 [Unity Shader](六) ------ 复杂的光照(上) [Unity

OpenGL蓝宝书第六章代码疑虑:为什么使用了GL_TEXTURE_1D??纹理和顶点间的映射是如何实现?

遇到一个大问题,明明加载纹理绑定的是GL_TEXTURE_2D glUniform1i(locCloudTex, 1); //明明默认为0,参数却不是0. 可是原书中代码确是 glGenTextures(1, &uiTexture); glBindTexture(GL_TEXTURE_1D, uiTexture); // LoadTGATexture("Clouds.tga", GL_LINEAR, GL_LINEAR, GL_CLAMP_TO_EDGE); glUniform

CUDA学习日志:常量内存和纹理内存

接触CUDA的时间并不长,最开始是在cuda-convnet的代码中接触CUDA代码,当时确实看的比较痛苦.最近得空,在图书馆借了本<GPU高性能编程 CUDA实战>来看看,同时也整理一些博客来加强学习效果. Jeremy Lin 在上一篇博文中,我们谈到了如何利用共享内存来实现线程协作的问题.本篇博文我们主要来谈谈如何利用常量内存和纹理内存来提高程序性能. 常量内存 所谓的常量内存,从它的名字我们就可以知道,它是用来保存在核函数执行期间不会发生变化的数据.NVIDIA硬件提供了64KB的常量

DXVA2解码数据用texture纹理渲染

FFmpeg DXVA2解码得到的数据使用surface来承载的,surface限制很多,如果能用纹理来渲染的话,那我们就可以充分开发D3D,比如可以用坐标变换来实现电子放大的功能,还可以用坐标变换来实现视频图像任意角度的旋转等功能.而对于我来说,最重要的是纹理渲染可以使得解码后的数据能够用像素着色器来做简单的视频图像处理,如果是用的是D3D11,对于更为复杂的视频图像处理算法也是有望可以用Compute Shader实现,以便充分利用显卡来加速和释放CPU. DXVA2解码数据用纹理渲染的方法

Halcon编程-基于纹理的mara检测

表面瑕疵检测是机器视觉领域非常重要的一个应用.机器视觉是集光学.机电和计算机三个领域的一门不算新的技术.但目前表面瑕疵检测在学界主要是计算机专业或者控制专业瞄准图像处理方向在做,而视觉光学系统这一块主要是光学工程专业在做.很少有研究者把这三块都结合的很好,而国内做这机器视觉(注意是机器视觉 不是计算机视觉)基本上都是小公司. 软件这一块就不说了,国内的整体软件环境不好.据我所知,日本.德国和美国在机器视觉方面有很多相对成熟的软件.中国农业大学的陈兵旗教授在留日期间弄过很多机器视觉方面的农业机器人

Cg入门21:Fragment shader - 2D纹理采样

体纹理:是啥? tex2D 以前只能在Fragment程序中纹理采样 UV坐标系:其实点为左下角,范围为[0,1],U为x轴,V为y轴 Texture.wrapMode 循环模式: TextureWrapMode.Clamp:设置纹理充满拉伸使用 TextureWrapMode.Repeat:纹理重复平铺使用 如果采用Repeat,那么等于U>=1的情况就会用纹理图在右边在平铺一张图 Texture.filterMode 过滤模式: Point:像素过滤(不进行过滤 ) Bilinear:双线性