前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >shader实例(五)GrabPass捕捉屏幕纹理

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

作者头像
bering
发布2019-12-02 22:00:32
7910
发布2019-12-02 22:00:32
举报
文章被收录于专栏:游戏开发之旅游戏开发之旅

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

1.固定管线版本:

代码语言:javascript
复制
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捕捉屏幕纹理
shader实例(十六)GrabPass捕捉屏幕纹理

2.顶点,片段版本:

代码语言:javascript
复制
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捕捉屏幕纹理
shader实例(十六)GrabPass捕捉屏幕纹理

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

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

代码语言:javascript
复制
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"
}

注: 2中的确是将屏幕的纹理赋值到样本对象GrabTexture上,所以前面的模型显示整个屏幕的纹理是正常现象。 3中是计算该模型顶点在屏幕坐标的纹理信息,unity封装的UnityCG.cginc代码中有:

代码语言:javascript
复制
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赋值的代码是一样的。所以在顶点程序中可以这样写:

代码语言:javascript
复制
    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个对象进行测试,没发现太大差异,手机上没测过), 在手机上比较适合这种方式。实现代码如下:

代码语言:javascript
复制
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);
    }
}
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2017-10-21 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1.固定管线版本:
  • 2.顶点,片段版本:
  • 3.使用vf的方式,只获取物体后面的屏幕纹理,后面的扭曲效果会用到此方式,代码如下:
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档