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

这篇具有很好参考价值的文章主要介绍了【Unity Shader】Unity中利用GrabPass实现玻璃效果。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

《入门精要》中模拟玻璃是用了Unity里的一个特殊的Pass来实现的,这个Pass就是GrabPass,比起上一篇博客实现镜子的方法,这个方法我认为相对复杂,因此在实现之前需要对GrabPass及实现原理做一个更加详细的介绍。


1 效果及代码

1.1 效果

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

1.2 Shader完整代码

Shader "Unity Shaders Book/Chapter 10/GlassRefraction"
{
    //Properties
    Properties {
        _MainTex ("Main Tex", 2D) = "white" {}  //玻璃材质纹理
        _Cubemap ("EM", Cube) = "_Skybox" {}
        _BumpMap ("Bump Map", 2D) = "bump" {}  //玻璃法线纹理
        //control the distortion of refraction
        _Distortion ("Distortion", range(0, 100)) = 10
        _RefractAmount ("Refract Amount", Range(0.0, 1.0)) = 1.0
    }
    
    SubShader {
        //Queue must be transparent, opaque objects will be drawn before
        Tags { "Queue"="Transparent" "RenderType"="Opaque" }

        //define a pass to grab the screen behind the object,
        //see the result by using "_RefractionTex"
        GrabPass {"_RefractionTex"}

        Pass {
            CGPROGRAM
            
            #pragma vertex vert
            #pragma fragment frag
            #include "Lighting.cginc"
            #include "AutoLight.cginc"

            //Properties
            sampler2D _MainTex;
            float4 _MainTex_ST;
            samplerCUBE _Cubemap;
            sampler2D _BumpMap;
            float4 _BumpMap_ST;
            float _Distortion;
            fixed _RefractAmount;
            //remember to add:
            sampler2D _RefractionTex;
            //get the texel size:
            float4 _RefractionTex_TexelSize; 
            
            struct a2v {
                float4 vertex : POSITION;
                float3 normal : NORMAL;
                float4 texcoord : TEXCOORD0;
                float4 tangent : TANGENT;
            };

            struct v2f {
                float4 pos : SV_POSITION;
                float4 uv : TEXCOORD0;
                float4 TtoW0 : TEXCOORD1;
                float4 TtoW1 : TEXCOORD2;
                float4 TtoW2 : TEXCOORD3;
                float4 srcPos : TEXCOORD4;
            };

            v2f vert(a2v v) {
                v2f o;
                o.pos = UnityObjectToClipPos(v.vertex);

                //抓取屏幕图像的采样坐标
                o.srcPos = ComputeGrabScreenPos(o.pos);
                o.uv.xy = TRANSFORM_TEX(v.texcoord, _MainTex);
                o.uv.zw = TRANSFORM_TEX(v.texcoord, _BumpMap);

                float3 worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
                float3 worldNormal = UnityObjectToWorldNormal(v.normal).xyz;
                float3 worldTangent = UnityObjectToWorldNormal(v.tangent).xyz;
                float3 worldBinormal = cross(worldNormal, worldTangent) * v.tangent.w;

                //计算切线空间 -> 世界空间的矩阵,只需要3x3
                //按列摆放
                o.TtoW0 = float4(worldTangent.x, worldBinormal.x, worldNormal.x, worldPos.x);
                o.TtoW1 = float4(worldTangent.y, worldBinormal.y, worldNormal.y, worldPos.y);
                o.TtoW2 = float4(worldTangent.z, worldBinormal.z, worldNormal.z, worldPos.z);

                return o;
            }

            fixed4 frag(v2f i) :SV_Target {
                float3 worldPos = float3(i.TtoW0.w, i.TtoW1.w, i.TtoW2.w);
                //计算光照需要参数:
                fixed3 worldlightDir = normalize(UnityWorldSpaceLightDir(worldPos));
                fixed3 worldviewDir = normalize(UnityWorldSpaceViewDir(worldPos));
                //对纹理采样+解码,得到法线方向
                fixed3 bump = UnpackNormal(tex2D(_BumpMap, i.uv.zw));
                
                //由于这里需要模拟的是玻璃的折射效果,因此不能是这种常规的法线纹理的偏移:
                //bump.xy *= _BumpScale;
                //bump.z = sqrt(1.0 - saturate(dot(bump.xy, bump.xy)));

                //开始实现折射效果
                float2 offset = bump.xy * _Distortion * _RefractionTex_TexelSize.xy;
                i.srcPos.xy = offset + i.srcPos.xy;
                //采样得到“折射”颜色:
                fixed3 refractColor = tex2D(_RefractionTex, i.srcPos.xy/i.srcPos.w).rgb;

                bump = normalize(half3(dot(i.TtoW0.xyz, bump), dot(i.TtoW1.xyz, bump), dot(i.TtoW2.xyz, bump)));

                //开始环境映射:
                fixed3 reflectDir = reflect(-worldviewDir, bump);
                fixed4 texColor = tex2D(_MainTex, i.uv.xy);
                fixed3 reflectColor = texCUBE(_Cubemap, reflectDir).rgb * texColor.rgb;

                fixed3 finalColor = reflectColor * (1 - _RefractAmount) + refractColor * _RefractAmount;
                return fixed4(finalColor, 1.0);
            }
            ENDCG
        }
    }
    FallBack  "Diffuse"
}

2 一些重点过程

2.1 制作场景

场景物体拜访和贴图完全参考《入门精要》:

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

以及当前场景的Cubemap的创建:由于相同项目下之前已经在Assets -> Edit加了一个可以获取场景中某个GameObject角度的Cubemap,直接按照相似的方法创建了创建过程可以参考【Unity Shader】Unity中如何创建Cubemap?

2.2 关于渲染队列设置

乍一看,SubShader的标签设置好像是前后矛盾的, 渲染队列Queue是Transparent透明的,而当前shader的渲染类型RenderType确是不透明:

Tags { "Queue"="Transparent" "RenderType"="Opaque" }
GrabPass {"_RefractionTex"}

设置Queue的作用

我们上效果,如果不加上"Queue"="Transparent",效果如下:

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

 当前的Shader是挂在外面的Cube上的,从上图效果和1.1的效果对比可以看出,Queue设置为Transparent是为了保证当前屏幕空间画面里的比Cube深度大但是是不透明的物体(默认的Queue就是不透明)也能渲染并呈现出来,达到“透过玻璃观察”的效果。

设置RenderType的作用

还跟上面一样,假设把"RenderType"="Opaque"去掉,会发现效果跟1.1的没有任何变化。这是因为!RenderType其实是提前给当前的Shader归类了,为了方便之后使用着色器替换(Shader Relacement)的时候,当前Shader能被正确的使用。至于什么是着色器替换,后面会涉及到,这里就先不解释了,挖个坑以后填。

2.3 2种GrabPass的使用方法

参考ShaderLab:GrabPass - Unity 手册,从官方文档中可知GrabPass是ShaderLab语法中的一员,是包含在SubShader内的一种特殊的通道类型,它直接定义了一个额外的抓取屏幕图像的Pass把即将绘制对象时的屏幕内容抓取到某个纹理中,这个纹理可以在后面的Pass中被使用去做一些效果。它的使用方式通常有两种:

GrabPass {}

即直接在Pass语义块前添加GrabPass {},{}里啥也不写,那么后续抓取屏幕图像的Pass会使用_GrabTexture来访问屏幕图像。这种方法看似方便,省去了定义一个新texture的麻烦。但当场景中多个物体都需要这种形式来抓取屏幕时(我理解的是有多个物体需要做出类似“玻璃”的效果),Unity都会为每个物体单独执行一次这个Pass的抓取操作每个物体都会生成属于自己的_GrabTexture,这样的效果虽好,但代价是很大的。

GrabPass {"TextureName"}

就像上述代码中的:

GrabPass {"_RefractionTex"}

给我们Pass抓取屏幕图像定义一个专属的、名为"_RefractionTex"的纹理,后续的Pass中如果需要使用,就可以通过这个名称来访问抓取屏幕图像的纹理啦!比如上述代码中的:

fixed3 refractColor = tex2D(_RefractionTex, i.srcPos.xy/i.srcPos.w).rgb;

就是直接使用了定义的纹理名称来访问这个纹理。这样使用方法的好处是,同一个屏幕下多个需要GrabPass的物体都使用同一次渲染出的纹理,也就是仅进行一次GrabPass的抓取图像操作,这样就可以大大节省消耗!而且大部分情况下,都使用一张抓取的图像已经能满足效果需求了。

二者的对比

 这里我们还是用到了Unity提供的Frame Debug,同时为了更好的对比效果,我在场景中多添加了一个想实现透明效果的Cube,这里仅看透明物体的渲染步骤。

使用GrabPass {"_RefractionTex"}时,可以发现步骤中两个Cube是共用同一张Texture的,仅Grab了一次:

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

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

而当使用GrabPass {}时,Grab了两次:

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

两次的RenderTexuter分别是:

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

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

二者的消耗对比(左GrabPass {"_RefractionTex"};右GrabPass {}),可以发现右边消耗明显比左边大:

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

2.4 获取纹素大小:_TexelSize

这里是为了提一提Shader中定义的:

//get the texel size:
float4 _RefractionTex_TexelSize;

以后的使用中如果想要获取某张纹理的纹素大小,就可以在纹理名称后加上_TexelSize啦!这个有点类似_MainTex_ST,都是Unity Shader的内置属性~

2.5 ComputerGrabScreenPos函数

这是一个Unity Shader的内置函数,ComputerGrabScreenPos()括号中输入裁剪空间下的顶点位置坐标,可以得到当前被抓取的屏幕图像的屏幕坐标。关于屏幕坐标获得好像有另一个函数?——ComputerScreenPos,那么问题来了:为什么不用ComputerScreenPos?关于这个问题,可以先保留着,我将在接下来的博客中仔细说明(又给自己挖了一个坑。。。)这里仅需要知道ComputerGrabScreenPos()的作用就行!

2.6 如何实现折射效果?

关于折射,我们好像真的学过并使用过一个折射相关的Unity内置函数——Refract(i, n, ri),但这里实现折射并不是真的要实现折射光的效果(太消耗啦!),而是采用GrabPass+给屏幕坐标一个偏移的方式实现玻璃的折射效果。

GrabPass在前面已经介绍过了,这里过一遍如何给屏幕坐标偏移,主要体现在如下代码:

fixed3 bump = UnpackNormal(tex2D(_BumpMap, i.uv.zw));
//由于这里需要模拟的是玻璃的折射效果,因此不能是这种常规的法线纹理的偏移:
//bump.xy *= _BumpScale;
//bump.z = sqrt(1.0 - saturate(dot(bump.xy, bump.xy)));

//开始实现折射效果
float2 offset = bump.xy * _Distortion * _RefractionTex_TexelSize.xy;
i.srcPos.xy = offset + i.srcPos.xy;
//采样得到“折射”颜色:
fixed3 refractColor = tex2D(_RefractionTex, i.srcPos.xy/i.srcPos.w).rgb;

对bump偏移

如果不记得如何在世界空间使用法线纹理了,可以先去看看【Unity Shader】纹理实践5.0:世界空间下使用法线纹理,如果仅实现法线纹理但并不考虑玻璃效果,直接给bump一个传统的变化就行,但这里需要加上玻璃的折射效果,因此还需要结合定义的_Distortion变量和_RefractionTex_TexelSize变量对bump“做手脚”。

  • _Distorion——控制折射的扭曲程度,其实它的道理跟传统应用中的“_BumpScale”是一样的
  • _RefractionTex_TexelSize——偏移量的大小,当然是根据纹理坐标而偏移

获取折射颜色

tex2D(_RefractionTex, i.srcPos.xy/i.srcPos.w)这里用了一个透视除法!这里涉及到了如何在Unity中获取片元在屏幕上的像素位置,这一点之前在学习基础理论时忽略了,后期会再补上,这里就不赘述(好家伙,又挖了一个坑。。。)。

最终呈现的颜色

fixed3 finalColor = reflectColor * (1 - _RefractAmount) + refractColor * _RefractAmount;

这里用了一个_RefractAmount巧妙地控制了折射和反射的占比(跟之前的环境映射中实现折射效果一样的操作),其实就是一个自行给定的菲涅尔项。


关于实现玻璃效果的过程梳理到这就结束啦!

后面会再出一个关于Unity中GrabPass的使用、包括GrabPass和AlphaBlend的区别、URP下的GrabPass实现方案等等,以及《入门精要》中提到的一个Command Buffers这个新概念,这些点真的有太多太多可以学习的内容了。文章来源地址https://www.toymoban.com/news/detail-488969.html

到了这里,关于【Unity Shader】Unity中利用GrabPass实现玻璃效果的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Unity中Shader抓取屏幕并实现扭曲效果(优化)

    对上一篇中实现的shader进行优化 1、定义结构体用于传入顶点坐标系 struct appdata { float4 vertex : POSITION; //从应用程序阶段的输入,多加一个uv,用于对扭曲纹理的采样 float2 uv : TEXCOORD; }; 2、因为UnityObjectToClipPos是从本地空间转换到裁剪空间,但是没有进行透视除法,所以需要对其

    2024年02月03日
    浏览(39)
  • Unity Shader学习记录(11) ——透明效果的实现方式

    1 透明效果的两种方法 透明是游戏中经常要使用的一种效果。在实时渲染中要实现透明效果,通常会在渲染模型时控制它的透明通道(Alpha Channel)。当开启透明混合后,当一个物体被渲染到屏幕上时,每个片元除了颜色值和深度值之外,它还有另一个属性一一透明度。 当透明度

    2024年02月07日
    浏览(35)
  • unity shader 入门 全透明与半透明效果实现

    片元函数的fixed4类型的返回值的第4位即为阿尔法值,0代表完全不显示(透明),1代表完全显示。中间的数值代表半透明。但只修改这个值是不能直接修改透明度的,因为还要对队列等进行修改。 本文介绍透明度测试与透明度混合,前者只能制作全透明效果,后者可制作半透明

    2024年02月14日
    浏览(33)
  • 【unity实战】使用shader和shader Graph实现2d图片描边效果(附源码)

    最近在学习shader Graph相关内容,其实关于实现2d图片描边效果,网上可以看到很多教程,但是我发现大多数都是基于比较老旧的2018unity版本,可是我们实际开发使用可能是比较新的2021及以上版本,差别还是有的,实际在升级或者使用过程中,会遇到诸多问题,而且也很少有人

    2024年02月16日
    浏览(31)
  • 【实现100个unity特效】shader实现3D物品闪光和描边效果

    线状映射图片 配置,按该shader新增材质 Outline Width控制描边 Line Speed控制闪光速度 当然,还可以修改对应的颜色 效果演示 赠人玫瑰,手有余香!如果文章内容对你有所帮助,请不要吝啬你的 点赞评论和关注 ,以便我第一时间收到反馈,你的每一次 支持 都是我不断创作的最

    2024年01月25日
    浏览(41)
  • 【实现100个unity特效之2】使用shader和shader Graph实现2d图片描边效果(附源码)

    最近在学习shader Graph相关内容,其实关于实现2d图片描边效果,网上可以看到很多教程,但是我发现大多数都是基于比较老旧的2018unity版本,可是我们实际开发使用可能是比较新的2021及以上版本,差别还是有的,实际在升级或者使用过程中,会遇到诸多问题,而且也很少有人

    2024年01月21日
    浏览(46)
  • 【unity插件】Shader实现UGUI的特效——UIEffect为 Unity UI 提供视觉效果组件

    一般的shader无法直接使用在UI上,需要在shader中定义特定的面板参数,今天就来推荐github上大佬做的一套开源的一系列UGUI,Shader实现的特效——UIEffect 为 Unity UI 提供视觉效果组件。 https://github.com/Ankh4396/UIEffect 让我们用效果来装饰你的UI!您可以根据需要从脚本和检查器中控

    2024年02月04日
    浏览(35)
  • CSS实现磨砂玻璃(毛玻璃)效果样式

    要实现磨砂玻璃背景,可以使用 CSS3 中的 ::before 伪元素和 backdrop-filter 属性,结合 opacity 属性和 blur() 函数来实现。 具体实现步骤如下: 创建一个具有背景的元素,例如一个 div 元素。 使用 ::before 伪元素为元素添加一个半透明的背景层。 在这个示例中,设置了 ::before 伪元素

    2024年02月05日
    浏览(29)
  • Unity Shader学习3:透明效果

    Unity中的透明效果由透明通道控制(RGBA中的A),其值为0是完全透明,为1时完全不透明。有两种方法可以实现透明效果: 透明度测试(Alpha Test) 和 透明度混合(Alpha Blend) 。 透明度测试是指通过特定的条件(通常是Alpha通道的值是否超过某个阈值)来判断片元是否透明,只

    2024年01月19日
    浏览(49)
  • unity 利用Scroll View实现滑动翻页效果

    1.在Hierarchy视图右键创建UI-Scroll View。 Scrollbar可根据自己需求选择是否删除,我这里制作的翻页日历用不上我就删除了。 connect节点挂上Grid Layout Group组件,参数属性可参考unity API。 下面是具体实现代码  onLeft和onRight绑定左右翻页按钮事件  

    2024年01月25日
    浏览(27)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包