• (转)GrabPass捕捉屏幕纹理


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

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

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

    但是还是无法理解GrabPass{}是捕捉物体后面的屏幕纹理,还是整个屏幕的纹理,至于为什么,可以看下面的例子,如果你知道原因求分享。。。shader实例(十六)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"
    }

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

    shader实例(十六)GrabPass捕捉屏幕纹理

    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"
    }

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

    shader实例(十六)GrabPass捕捉屏幕纹理 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"
    }

    没法一口吃个大胖子,学到这里才发现底层渲染原理很多都不了解,还的再仔细看看基础知识才行啊。shader实例(十六)GrabPass捕捉屏幕纹理

    注:

    补充于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);
        }
    }
  • 相关阅读:
    es3的语法来模拟es5的bind方法
    js判断变量的类型(使用闭包来玩一把)
    获取一组时间中的最近的日期
    可以设置超时版的的fetch
    错误排查
    如何查看Linux的CPU负载
    shell脚本操作mysql数据库,使用mysql的-e参数可以执行各种sql的(创建,删除,增,删,改、查)等各种操作
    Swoole server函数列表(转载)
    php的反射
    PHP共享内存
  • 原文地址:https://www.cnblogs.com/wonderKK/p/4644895.html
Copyright © 2020-2023  润新知