为什么薄膜干涉的厚度要很小_肥皂泡的渲染(薄膜干涉)

本文介绍了肥皂泡薄膜干涉的原理,通过Shader模拟了肥皂泡的色彩效果,包括菲涅尔现象、镜面反射、双面反射和表面流动导致的厚度变化。文章提供了项目下载,并详细解释了实现方法,如噪声图用于模拟厚度流动,中心对称反射的计算,以及基于菲涅尔和光照的透明度计算。最终展示了模拟的肥皂泡颜色效果,并探讨了其拓展应用。
摘要由CSDN通过智能技术生成

7562ee87c546f91bd611e6c839029aa9.png

序·薄膜干涉

前几天读了 @中国科普博览 @Luyao Zou 的一篇文章,里面讲了肥皂泡薄膜干涉的原理,并给出了定量测量的颜色图

觉得十分有趣,因此才起了用Shader实现一下肥皂泡的想法

为什么阳光下的泡沫是彩色的?​www.zhihu.com

94a2ce7ea380c2fe4af668671ed53b3d.png

一般渲染中只涉及几何光学,不会涉及干涉,但有定量测量结果后,我们可以只通过角度与薄膜厚度映射相关的色彩,从而模拟出干涉效果

项目下载

感兴趣的小伙伴可以在Github上下载这个小项目:

TYJia/Bubble​github.com
1068ce09ee53df9c4ac40abd393bf076.png

1397d6da8d8f8cf80c6efbc18746cdda.png
肥皂泡模拟

肥皂泡的属性分析

除了绚丽多彩,肥皂泡还有一些其他的属性,也是渲染的关键,仔细观察可以发现如下现象:

1. 菲涅尔现象

f82e6ec34be55ff041052b5e1e813b80.png
肥皂泡的菲涅尔现象

肥皂泡的菲涅尔现象是十分明显的,当视线与泡泡表面法线方向相同、相反时,反射率较低;当视线与泡泡表面法线垂直时,反射率较高

2. 镜面反射与光透明度

041e6b394c46a4518488963f995b7053.png
光滑,镜面反射,暗处透明

泡泡的表面都是极为光滑的,所以是镜面反射;不过在不反射光线或光线较弱时,它是全透明的,反射光线较强时,才会被我们看见

所以在进行透明度计算时,也要同时考虑反射明度

3. 双面反射(中心对称反射)

e03daf011f1c5939c8ea8cc81c781c12.png

与水晶球不同,泡泡的内外表面都是参与反射的,所以一个球形泡泡会形成中心对称的反射结果

4. 流动的表面

f7886bcf3d31ef7cfc89ba5af223f640.png
流动的表面

泡泡的表面是液体,再加上一些力的作用,所以会慢慢流动,也因此会产生表面厚度的变化

源码

Shader "TJia/BubbleShader" {
    Properties {

        _Color ("Main Color", Color) = (1,1,1,1)
        _MetalRef ("MetalRef", Range(0,1)) = 0

        _MainTex("Main Tex", 2D) = "white" {}
        _BumpMap ("Bumpmap (RGB)", 2D) = "bump" {}
        _BumpValue ("BumpValue", Range(0,3)) = 1

        _LightModle ("LightModleDiffuse (RGB)", 2D) = "white" {}
        _LightModleValue("LightModleValue", Range(0,3)) = 1     
        _LightModleSpec ("LightModleSpec (RGB)", 2D) = "black" {}
        _SpecValue ("SpecValue", Range(0,4)) = 0  
        
        _Bubble ("Bubble (RGB)", 2D) = "white" {} 
        _BubbleNoise("BubbleNoise",2D) = "white" {} 
        _BubblelValue ("Bubble Value", Range(0,2)) = 1
    }
    
    Subshader {
        Tags { "RenderType"="Transparent" "Queue" = "Transparent"}
        Fog { Color [_AddFog] }
        
        
        
        Pass {
            Name "BASE"
            Tags { "LightMode" = "Always" }
            Blend SrcAlpha OneMinusSrcAlpha
            
            CGPROGRAM
                #pragma vertex vert
                #pragma fragment frag
                #pragma fragmentoption ARB_fog_exp2
                #pragma fragmentoption ARB_precision_hint_fastest
                #include "UnityCG.cginc"

                //#define _Shadow 1
                //#define _ShadowRange 0.25
                
                struct v2f { 
                    float4 pos : SV_POSITION;
                    float4  uv : TEXCOORD0;
                    float3  TtoV0 : TEXCOORD1;
                    float3  TtoV1 : TEXCOORD2;
                    float3 visual : TEXCORRD3;
                    float3  normal : NORMAL;
                };
                
                uniform float4 _BumpMap_ST, _DetailGreyTex_ST, _DetailBumpTex_ST;
                uniform float4 _MainTex_ST;
                
                v2f vert (appdata_tan v)
                {
                    v2f o;
                    o.pos = UnityObjectToClipPos (v.vertex+_SinTime.y*v.normal*0.1);
                    o.uv.xy = TRANSFORM_TEX(v.texcoord,_MainTex);
                    o.uv.zw = TRANSFORM_TEX(v.texcoord,_BumpMap);
                    
                    
                    TANGENT_SPACE_ROTATION;
                    o.TtoV0 = normalize(mul(rotation, UNITY_MATRIX_IT_MV[0].xyz));
                    o.TtoV1 = normalize(mul(rotation, UNITY_MATRIX_IT_MV[1].xyz));

                    o.visual = normalize(mul(rotation, -ObjSpaceViewDir(v.vertex)));

                    return o;
                }
                
                uniform fixed4 _Color, _ShadowColor;
                uniform sampler2D _BumpMap, _Bubble, _BubbleNoise;
                uniform sampler2D _LightModle;
                uniform sampler2D _MainTex;
                uniform sampler2D _LightModleSpec;
                uniform fixed _SpecValue;
                uniform fixed _BumpValue;
                uniform fixed _LightModleValue, _BubblelValue;

                uniform fixed _MetalRef;

                float3 lum(fixed3 c)
                {
                    return c.r * 0.2 + c.g * 0.7 + c.b * 0.1;
                }
                
              
                float4 frag (v2f i) : COLOR
                {
                    fixed4 c = tex2D(_MainTex, i.uv.xy);
                    float3 normal = UnpackNormal(tex2D(_BumpMap, i.uv.zw));
                    normal.xy *= _BumpValue;
                    normal = normalize(normal);
                    
                    float v_n_angle = saturate(acos(dot(-i.visual,normal))*2/3.14);
                    float BubbleR = tex2D(_BubbleNoise, i.uv.xy+float2(0,_Time.x)).r;
                    float BubbleG = tex2D(_BubbleNoise, i.uv.xy+float2(BubbleR*0.5,_Time.x)*0.9+0.05*_SinTime.x).g;
                    float BubbleB = tex2D(_BubbleNoise, i.uv.xy+float2(BubbleG*0.5,_Time.x)*1.1-0.05*_SinTime.y).b;
                    float BubbleNoise = saturate(BubbleR * BubbleG * BubbleB * 4);
                    fixed4 Bubble = tex2D(_Bubble, fixed2(BubbleNoise, v_n_angle));

                    float3 correctiveNormal = normalize(reflect(i.visual, normal));
                    normal = normalize(lerp(normal, correctiveNormal, _MetalRef));
                    
                    
                    half2 vn;
                    vn.x = dot(i.TtoV0, normal);
                    vn.y = dot(i.TtoV1, normal);
                   
                    
                    fixed4 LightModleLookup = saturate(tex2D(_LightModle, vn * 0.495 + 0.505) * _Color * _LightModleValue);
                    //LightModleLookup.a = 1;
                    
                    fixed2x2 rotSpec =
                    {
                        -1, 0,
                        0, -1
                    };
                    half2 vnsp = mul(rotSpec, vn);
                    fixed4 LightModleSpec = tex2D(_LightModleSpec, vn*0.495 + 0.505);
                    fixed4 LightModleSpec2 = tex2D(_LightModleSpec, vnsp*0.495 + 0.505);
                    LightModleSpec.rgb = lum(saturate((LightModleSpec + LightModleSpec2)*0.5).rgb) * (LightModleSpec.rgb+LightModleSpec2.rgb)*0.5;
                 
                    LightModleSpec.a = 1;
                 

                    fixed4 diff = c * LightModleLookup ;

                    fixed4 finalColor = clamp(diff + LightModleSpec * _SpecValue, 0, 1) * lerp(fixed4(1, 1, 1, 1), Bubble * 1.5, _BubblelValue);
                    
                    float fresnel = saturate(pow(v_n_angle,1.6));
                    
                    finalColor.a = saturate(fresnel * 0.2 + 0.8 * (fresnel+0.1) * lum(LightModleSpec.rgb * _SpecValue + 0.1) + 0.05);
                     
                    return finalColor;
                     
                }
            ENDCG
        }
    }
    Fallback "VertexLit"
}

实现方法注释

1. 用以模拟泡泡表面液体流动的厚度噪声图

为了模拟肥皂泡厚度流动的变化,这里使用了一张噪声图,RGB通道都是PS随机生成的,厚度计算方法如下:

float BubbleR = tex2D(_BubbleNoise, i.uv.xy+float2(0,_Time.x)).r;
float BubbleG = tex2D(_BubbleNoise, i.uv.xy+float2(BubbleR*0.5,_Time.x)*0.9+0.05*_SinTime.x).g;
float BubbleB = tex2D(_BubbleNoise, i.uv.xy+float2(BubbleG*0.5,_Time.x)*1.1-0.05*_SinTime.y).b;
float BubbleNoise = saturate(BubbleR * BubbleG * BubbleB * 4);

f0a46d783eb701c21d75335b7eeb4815.png
厚度噪声图

2. 中心对称的反射

这里使用了旋转矩阵,将反射结果转了180°,并进行了如下的混合计算:

LightModleSpec.rgb = lum(saturate((LightModleSpec + LightModleSpec2)*0.5).rgb) * (LightModleSpec.rgb+LightModleSpec2.rgb)*0.5;

3. 基于菲涅尔和反光明度的透明度计算

菲涅尔通过视线、法线夹角得出

这里使用acos是为了薄膜干涉映射,在菲涅尔模拟中不必要

最终透明度通过菲涅尔和光照强度混合计算获得:

float v_n_angle = saturate(acos(dot(-i.visual,normal))*2/3.14);
float fresnel = saturate(pow(v_n_angle,1.6));
finalColor.a = saturate(fresnel * 0.2 + 0.8 * (fresnel+0.1) * lum(LightModleSpec.rgb * _SpecValue + 0.1) + 0.05);

最终结果如下:

a833c80a41710eed6f3e1095abc3c5fb.png

拓展

改改采样图,调调混合模式,就可以当流动的防护罩来用啦~

cacae097fff812de9e369aef8cf7402d.png
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值