您当前的位置: 首页 >  ar

Jave.Lin

暂无认证

  • 3浏览

    0关注

    704博文

    0收益

  • 0浏览

    0点赞

    0打赏

    0留言

私信
关注
热门博文

Unity Shader - Billboard火堆热扭曲

Jave.Lin 发布时间:2020-03-26 00:48:38 ,浏览量:3

文章目录
  • 先看看效果
  • 实现
    • 火堆的假泛光
      • 软粒子
    • 火堆的高温扭曲
      • 放一个Billboard面片
      • 但是我们Billboard会给其他的物体挡住
      • 添加`_OffsetToCam`调整参数模型空间坐标偏移,从而调整深度
      • 准备一张噪点图扭曲UV用
      • 但是扭曲的边缘太硬的过渡
      • 添加半径值过渡平滑
      • 太远是扭曲还是太强
      • 添加view space z深度判断控制扭曲响度
  • 另一种实现方式
    • 区别
      • ZTest不同
      • 扭曲方式
      • 网格不同
  • Project
  • Refernces
关于Billboard相关知识点,可参考:

  • OpenGL Tutorials - Billboards
  • Unity Shader - Billboard 广告板/广告牌

学习最好的方法就是:学以致用。

这段时间我报了一些数学相关的知识点课程,写完这篇博客后,后面过一段时间才有空继续写。 内功的东西,真的太缺了,太影响学习其他内容的速度,所以基础的东西千万千万别小看,往往越是简单的基础知识点,就越不能小看。在学习过程中,我将非常非常详尽的记录到我的:程序员的数学-私有,的专栏里,但只是供自己使用,加深、巩固理解,但是这些记录的知识点我不能公开,有大量的视频资源截图引用说明,所以会有版权的问题,当然也有大量自己的总结。

先看看效果

我用Unity Cube随意搭建的场景,下面的场景元素都是Cube缩放旋转平移来完成的(除了人物和火焰粒子):

  • 远处的山
  • 后面的Jave.Lin广告牌(这个就不是我们的功能上的Billboard了,它就真的一个广告牌)
  • 石凳
  • 火堆木材

火焰使用了Unity内置的Particle System。调整了一些属性可达成的效果,还是比较好用的。 在这里插入图片描述

实现

所以我们之前学习了Billboard之后,就可以用它来实现一些效果:

  • 火堆的假泛光
  • 火堆的高温扭曲
火堆的假泛光

在这里插入图片描述

软粒子

加泛光我我写了个软粒子处理的(手机端一般不开这个效果)

软粒子的意思是:当:片段可见时,片段对应深度缓存的深度与粒子的片段深度很近时,就alpha值很小,几乎看不到,然后随着距离大时,又慢慢的恢复alpha时,这样看起来就不会太硬的过渡边界。

具体Shader如下:

// jave.lin 2020.03.25 - 软粒子
Shader "Custom/SoftParticleAdditive" {
    Properties {
        _MainTex ("Texture", 2D) = "white" {}
        _MainColor ("Color", Color) = (1,1,1,1)
    }
    SubShader {
        Tags { "RenderType"="Transparent" "Queue"="Transparent" "IgnoreProjector"="True" }
        LOD 100 Cull Off Lighting Off Fog { Mode Off } ZWrite Off
        Blend One One 
        ZTest Always    // 一直测试通过都OK,因为已处理深度柔和处理了alpha
        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;
                float2 uv : TEXCOORD0;
                float4 projPos : TEXCOORD1;
            };
            sampler2D _MainTex;
            fixed4 _MainColor;
            sampler2D _CameraDepthTexture;
            v2f vert (appdata v) {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv = v.uv;
                o.projPos = ComputeScreenPos(o.vertex);
                COMPUTE_EYEDEPTH(o.projPos.z);      // eyeZ
                return o;
            }
            fixed4 frag (v2f i) : SV_Target {
                float fragZ = i.projPos.z;
                float buffZ = LinearEyeDepth(SAMPLE_DEPTH_TEXTURE_PROJ(_CameraDepthTexture, UNITY_PROJ_COORD(i.projPos)));
                // 只有fragZ < buffZ才叠加颜色,与buffZ差值越小,alpha越小
                float fade = saturate(max(0, buffZ - fragZ) * _MainColor.a);
                fixed4 col = tex2D(_MainTex, i.uv) * _MainColor * fade;
                return col;
            }
            ENDCG
        }
    }
}

注意我们这个软粒子的ZTest Always的,但处理了深度柔和alpha处理。

火堆的高温扭曲

实现比较简单,挂载一个:Billboard Quad面片,片面 GrabPass ColorBuffer后,对背景扭曲

放一个Billboard面片

在这里插入图片描述 这个Billboard如下代码:

         v2f vert(a2v v) {
            v2f o;
            float4 viewSpacePos = mul(UNITY_MATRIX_MV, float4(0,0,0,1));
            viewSpacePos.xyz += v.vertex.xyz * _BillboardSize.xyz;
            o.vertex = mul(UNITY_MATRIX_P, viewSpacePos);
            o.uv = v.uv;
            o.projPos = ComputeGrabScreenPos(o.vertex);
            return o;
         }

Billboard的思路是,将Billboard中心点转换到ViewSpace下,在用顶点本身的本地坐标值与_BillboardSize.syz来缩放顶点位置,然后再转换到clip space

但是我们Billboard会给其他的物体挡住

在这里插入图片描述

添加_OffsetToCam调整参数模型空间坐标偏移,从而调整深度

我们提供了一个参数_OffsetToCam可以对Billboard的模型空间坐标往相机这边偏移一些:float4 viewSpacePos = mul(UNITY_MATRIX_MV, float4(toCamDir * _OffsetToCam, 1.0));

         v2f vert(a2v v) {
            v2f o;
            float3 worldPos = mul(unity_ObjectToWorld, v.vertex);
            float3 toCamDir = normalize(_WorldSpaceCameraPos.xyz - worldPos.xyz);
            float4 viewSpacePos = mul(UNITY_MATRIX_MV, float4(toCamDir * _OffsetToCam, 1.0));
            viewSpacePos.xyz += v.vertex.xyz * _BillboardSize.xyz;
            o.vertex = mul(UNITY_MATRIX_P, viewSpacePos);
            o.uv = v.uv;
            o.projPos = ComputeGrabScreenPos(o.vertex);
            return o;
         }

如下效果: 在这里插入图片描述

然后我们使用GrabPass { "_bgTex" },这里我偷懒,其实可以不使用GrabPass,同样可以拿到ColorBuffer内容,而且消耗没有GrabPass的大,可参考我之前写的:Unity Shader - 实现武器热扭曲拖尾效果(不需要GrabPass)

准备一张噪点图扭曲UV用

拿到屏幕内容后,使用一张NoiseTex 在这里插入图片描述

然后在Fragment结对采样对UV偏移,在使用偏移后的UV来扭曲屏幕内容,效果如下:

float4 frag(v2f i) : SV_Target {
   i.projPos.xy /= i.projPos.w;
   float noise = tex2D(_NoiseTex, float4(i.uv - _Time.xy * _Speed, 0, 0)).r;
   i.projPos.y += noise * _Strength; // 这些地方都可以优化除法为乘法
   return tex2D(_BgTex, i.projPos.xy);
}

运行效果如下:

但是扭曲的边缘太硬的过渡

在这里插入图片描述 在这里插入图片描述

添加半径值过渡平滑

我们可以添加一个Fade的半径值_Radius,只让面片中间指定半径内的平滑过渡,我们直接将过渡的效果输出看看,代码如下:

float2 dist = i.uv - 0.5;
float fade = 1 - saturate(dot(dist, dist) / (_Radius * _Radius));
return fade;

运行看看: 在这里插入图片描述

黑色到白色之间过渡,就是处理扭曲的强弱程度的值,黑色不扭曲,白色扭曲。

将fragment代码调整一下:

float4frag(v2f i) : SV_Target {
   float2 dist = i.uv - 0.5;
   float fade = 1 - saturate(dot(dist, dist) / (_Radius * _Radius));
   i.projPos.xy /= i.projPos.w;
   float noise = tex2D(_NoiseTex, float4(i.uv - _Time.xy * _Speed, 0, 0)).r;
   i.projPos.y += noise * _Strength * fade; // 这些地方都可以优化除法为乘法
   return tex2D(_BgTex, i.projPos.xy);
}

在运行看看效果: 在这里插入图片描述

嗯,这时效果好很多了

太远是扭曲还是太强

但是,如果我们将镜头挪到比较远看效果时,发现扭曲太强了,如下效果: 在这里插入图片描述 在这里插入图片描述

添加view space z深度判断控制扭曲响度

所以我们还要对距离开控制扭曲强度,在vert函数再添加一个unity内置的宏处理:COMPUTE_EYEDEPTH计算view space下的Z值,赋值到o.projPos.z,并将o.projPos.z传到fragment阶段使用,在fragment下就可以使用其控制扭曲强度:

   o.projPos = ComputeGrabScreenPos(o.vertex);
   COMPUTE_EYEDEPTH(o.projPos.z);

然后我们在frag中直接输出距离的值与我们添加的一个:_FadeOutDist的参数,超过该距离就不扭曲(太远就看不到扭曲)

struct v2f {
   float4 vertex : SV_POSITION;
   float2 uv : TEXCOORD0;
   float4 projPos : TEXCOORD1;
};
v2f vert(a2v v) {
   v2f o;
   ...
   o.projPos = ComputeGrabScreenPos(o.vertex);
   COMPUTE_EYEDEPTH(o.projPos.z);
   return o;
}
float4 frag(v2f i) : SV_Target {
   return saturate(1 - i.projPos.z / _FadeOutDist);
}

运行效果如下: 在这里插入图片描述 同样也是,白色就是扭曲,黑色不扭曲,可以看到距离比较远时,几乎就原来越黑了,近时就很白。OK我们再将代码整理一下:

         struct v2f {
            float4 vertex : SV_POSITION;
            float2 uv : TEXCOORD0;
            float4 projPos : TEXCOORD1;
         };
         v2f vert(a2v v) {
            v2f o;
            float3 worldPos = mul(unity_ObjectToWorld, v.vertex);
            float3 toCamDir = normalize(_WorldSpaceCameraPos.xyz - worldPos.xyz);
            float4 viewSpacePos = mul(UNITY_MATRIX_MV, float4(toCamDir * _OffsetToCam, 1.0));
            viewSpacePos.xyz += v.vertex.xyz * _BillboardSize.xyz;
            o.vertex = mul(UNITY_MATRIX_P, viewSpacePos);
            o.uv = v.uv;
            o.projPos = ComputeGrabScreenPos(o.vertex);
            COMPUTE_EYEDEPTH(o.projPos.z);
            return o;
         }
         float4 frag(v2f i) : SV_Target {
            float2 dist = i.uv - 0.5;
            float fade = 1 - saturate(dot(dist, dist) / (_Radius * _Radius));
            i.projPos.xy /= i.projPos.w;
            float noise = tex2D(_NoiseTex, float4(i.uv - _Time.xy * _Speed, 0, 0)).r;
            i.projPos.y += noise * _Strength * fade * saturate(1 - i.projPos.z / _FadeOutDist); // 这些地方都可以优化除法为乘法
            return tex2D(_BgTex, i.projPos.xy);
         }

运行看看: 在这里插入图片描述 在这里插入图片描述

OK,可以看到远距离时,就不会扭曲太强而影响体验了,最后再将代码整理,运行效果如下: 在这里插入图片描述

OK,到这就完成了。

另一种实现方式

可以参考:Heat Distortion Shader Tutorial

它的实现方式与我上面的区别在于:

区别 ZTest不同

我的方式:默认:ZTest LessEqual 她的方式:ZTest Always

这就在交互效果上的差距会比较大。如:她的方式,可以会将比热扭曲更靠近镜头的不透明物体也扭曲了。 而我的方式 ZTest LessEqual,并且在世界坐标下,提供了一个:OffsetToCam来偏差调整,就问题的情况就会好很多。

扭曲方式

我的方式:在frag扭曲uv来采样 她的方式:在vert扭曲顶点坐标,uv不变,那么fragment阶段的片段坐标就会伸纹素,达到扭曲效果

网格不同

我的:一个Quad就好了,四个顶点 她的:一个Plane,121个顶点 在这里插入图片描述

当然她的在顶点阶段处理顶点位移来替代片段中扭曲uv的方式,性能会更好一些。

Project

backup : UnityShader_HeatDistortionTesting_2018.3.0f2

Refernces
  • OpenGL Tutorials - Billboards
  • Unity Shader - Billboard 广告板/广告牌
  • Heat Distortion Shader Tutorial
关注
打赏
1664331872
查看更多评论
立即登录/注册

微信扫码登录

0.0376s