shader/shadertoy移植/20241203_143947.md
严争鸣 36b5eb0abb init
2024-12-09 14:57:06 +08:00

30 KiB
Raw Permalink Blame History

**一、简介** [shadertoy](https://www.shadertoy.com/)是一个基于webgl的分享Shader的开放平台用户可以在上面根据既定规则分享自己编写的shader。在Cesium中要想实现一些酷炫的效果唯一的一条路就是写shader而shader的编写应该算是图形学中的难度天花板了。好在有很多shader大佬分享了自己编写的glsl效果比如shadertoy网站上就是这些大牛们的作品我们可以借鉴。 ![在这里插入图片描述](20241203_143947.assets/c1eeb7716326293e03113f99c49eda30.png)

二、shadertoy着色器基本结构

shadertoy上的shader是纯2d绘图没有几何顶点这些概念它的绘图方式和canvas绘图方式很像它将整个canvas作为绘图的画布所以输入参数fragCoord的x值范围是0画布的宽度fragCoord的y值范围是0画布的高度画布的宽高在定义的输入参数中是iResolution所以fragCoord.x范围就是(0,iResolution.x),fragCoord.y范围就是(0,iResolution.y)。
在这里插入图片描述

如图所示shadertoy上的shader示例最基本的着色器结构主要包括两个部分
a、输入参数的定义
b、着色器的入口函数

1、输入参数通过uniform来定义外部的输入值。

uniform vec3      iResolution;           // 视口分辨率,即画布的宽高
uniform float     iTime;                 // shader 的运行时间 秒
uniform float     iTimeDelta;            // 渲染时间 秒
uniform float     iFrameRate;            // shader 帧率
uniform int       iFrame;                // shader 帧率
uniform float     iChannelTime[4];       // 频道运行时间 不管
uniform vec3      iChannelResolution[4]; // 频道分辨率 不管
uniform vec4      iMouse;                // 鼠标坐标
uniform samplerXX iChannel0..3;          // 输入的纹理 比如我们从一张图片上采用颜色
uniform vec4      iDate;                 // 日期 年月日 不管
uniform float     iSampleRate;           // 声音采样 不管

2、入口函数mainImage

void mainImage(out vec4 fragColor, in vec2 fragCoord )
{
    fragColor = vec4(1.);
}

方法的第一个参数fragColor,是一个vec4类型的变量表示最后输出的颜色值。
方法的第二个参数fragCoord是一个vec2类型的变量表示输入的像素坐标。

三、在cesium中如何使用

shadertoy上的着色器是在一个canvas画布上进行工作的要移植到Cesium中我们需要找一个载体来替代canvas。我们知道Cesium绘制几何图形可以通过Entity和Primitive两种方式那么只有Primitive+Appearance比较合适了,关于Primitive的使用及介绍可以观看前面的章节。要将着色器移植到Cesium中我们先来重点看看shadertoy上的shader着色器需要用到的参数。以下面这个例子讲解https://www.shadertoy.com/view/XdlSDs
在这里插入图片描述

glsl代码

void mainImage(out vec4 fragColor, in vec2 fragCoord )
{
    vec2 p = (2.0*fragCoord.xy-iResolution.xy)/iResolution.y;
    float tau = 3.1415926535*2.0;
    float a = atan(p.x,p.y);
    float r = length(p)*0.75;
    vec2 uv = vec2(a/tau,r);

    //get the color
    float xCol = (uv.x - (iTime / 3.0)) * 3.0;
    xCol = mod(xCol, 3.0);
    vec3 horColour = vec3(0.25, 0.25, 0.25);

    if (xCol < 1.0) {

        horColour.r += 1.0 - xCol;
        horColour.g += xCol;
    }
    else if (xCol < 2.0) {

        xCol -= 1.0;
        horColour.g += 1.0 - xCol;
        horColour.b += xCol;
    }
    else {

        xCol -= 2.0;
        horColour.b += 1.0 - xCol;
        horColour.r += xCol;
    }

    // draw color beam
    uv = (2.0 * uv) - 1.0;
    float beamWidth = (0.7+0.5*cos(uv.x*10.0*tau*0.15*clamp(floor(5.0 + 10.0*cos(iTime)), 0.0, 10.0))) * abs(1.0 / (30.0 * uv.y));
    vec3 horBeam = vec3(beamWidth);
    fragColor = vec4((( horBeam) * horColour), 1.0);
}

a、首先是fragCoord前面介绍过fragCoord表示当前处理的像素坐标是一个vec2类型fragCoord.x范围为(0,画布宽度),fragCoord.y范围为(0,画布高度)。
b、其次是iResolutioniResolution代表的是当前画布的宽高即绘图区域的尺寸所以fragCoord.x范围就是(0,iResolution.x),fragCoord.y范围就是(0,iResolution.y)。
c、然后我们还在代码中看到有个iTime该参数代表当前运行的时间一般用来实现动画因为您会发现大多数shader的效果都是动态的。
d、最后是输出结果fragColor,代表最后计算的颜色输出值在Cesium中为out_FragColor。

接下来介绍在Cesium如何获取对应的参数

a、fragCoord在Cesium有个gl_FragCoord与之对应这是一个WebGL内置的变量。
b、iResolution在Cesium有个czm_viewport与之对应不过使用时采用zw分量即 czm_viewport.zw
c、iTime在Cesium中没有对应的变量我们可以通过变量的方式传递一个参数然后在渲染时不断修改该值不过这种方式略显麻烦在Cesium中我们可以用float iTime=czm_frameNumber/100.来模拟czm_frameNumber代表当前帧是一个自增长的数值所以可以用来模拟时间不断地增长。

接下来我们实操一下在Cesium中实现该shader的效果

1、首先我们创建一个Primitive并添加到sene中

let xMin = 115.894604, yMin = 39.516896, xMax = 117.431959, yMax = 40.630521;
let rect = new Cesium.Rectangle(Cesium.Math.toRadians(xMin), Cesium.Math.toRadians(yMin), Cesium.Math.toRadians(xMax), Cesium.Math.toRadians(yMax));
const rectangle = new Cesium.RectangleGeometry({
    rectangle: rect,
    height: 10000.0,
});
const geometry = Cesium.RectangleGeometry.createGeometry(rectangle);
viewer.scene.primitives.add(new Cesium.Primitive({
    geometryInstances: new Cesium.GeometryInstance({
        geometry: geometry
    }), 
}));

2、此时会发现什么也看不见这是因为没有设置外观我们创建一个默认的外观

let appearance = new Cesium.MaterialAppearance({
     material: new Cesium.Material({
         fabric: {
             type: "Color",
             uniforms: {
                 color: Cesium.Color.RED
             }
         }
     }), 
 })
 primitive.appearance = appearance;

在这里插入图片描述

3、接下来我们为外观添加片元着色器并将shadertoy上的shader赋值给外观的片元着色器属性。

shadertoy上glsl代码

 fragmentShaderSource: `  
  void mainImage( out vec4 fragColor, in vec2 fragCoord )
  {
       vec2 p = (2.0*fragCoord.xy-iResolution.xy)/iResolution.y;
      float tau = 3.1415926535*2.0;
      float a = atan(p.x,p.y);
      float r = length(p)*0.75;
      vec2 uv = vec2(a/tau,r);

      //get the color
      float xCol = (uv.x - (iTime / 3.0)) * 3.0;
      xCol = mod(xCol, 3.0);
      vec3 horColour = vec3(0.25, 0.25, 0.25);

      if (xCol < 1.0) {

          horColour.r += 1.0 - xCol;
          horColour.g += xCol;
      }
      else if (xCol < 2.0) {

          xCol -= 1.0;
          horColour.g += 1.0 - xCol;
          horColour.b += xCol;
      }
      else {

          xCol -= 2.0;
          horColour.b += 1.0 - xCol;
          horColour.r += xCol;
      }

      // draw color beam
      uv = (2.0 * uv) - 1.0;
      float beamWidth = (0.7+0.5*cos(uv.x*10.0*tau*0.15*clamp(floor(5.0 + 10.0*cos(iTime)), 0.0, 10.0))) * abs(1.0 / (30.0 * uv.y));
      vec3 horBeam = vec3(beamWidth);
      fragColor = vec4((( horBeam) * horColour), 1.0);
  }
   `

操作步骤:
a、首先我们需要修改着色器入口函数即将mainImage修改为main因为Appearance片元着色器的入口函数是main。
b、然后将输出结果的fragColor修改为out_FragColor。
c、最后我们按照上面说的替换掉fragCoordfragCoord、iTime

我们需要的代码:

fragmentShaderSource: ` 
   void main()
   {
       float iTime=czm_frameNumber/100.;
       vec2 p = (2.0 * gl_FragCoord.xy-czm_viewport.zw)/czm_viewport.w;

       float tau = 3.1415926535*2.0;
       float a = atan(p.x,p.y);
       float r = length(p)*0.75;
       vec2 uv = vec2(a/tau,r);

       //get the color
       float xCol = (uv.x - (iTime / 3.0)) * 3.0;
       xCol = mod(xCol, 3.0);
       vec3 horColour = vec3(0.25, 0.25, 0.25);

       if (xCol < 1.0) {

           horColour.r += 1.0 - xCol;
           horColour.g += xCol;
       }
       else if (xCol < 2.0) {

           xCol -= 1.0;
           horColour.g += 1.0 - xCol;
           horColour.b += xCol;
       }
       else {

           xCol -= 2.0;
           horColour.b += 1.0 - xCol;
           horColour.r += xCol;
       }

       // draw color beam
       uv = (2.0 * uv) - 1.0;
       float beamWidth = (0.7+0.5*cos(uv.x*10.0*tau*0.15*clamp(floor(5.0 + 10.0*cos(iTime)), 0.0, 10.0))) * abs(1.0 / (30.0 * uv.y));
       vec3 horBeam = vec3(beamWidth);
       out_FragColor = vec4((( horBeam) * horColour), 1.0);
   }
    `

在这里插入图片描述

但是移动地球我们会发现绘制的结果始终是在屏幕中心这其实是一个正确的结果因为这个示例在shadertoy上也是始终绘制在屏幕中心。现在我们需要将Primitive的几何体作为绘制的画布上面我们是使用gl_FragCoord坐标来获取当前应该处理的像素我们现在需要改变为Appearance的纹理坐标通过Appearence的纹理坐标计算当前Appearence上需要处理的像素。

vec2 p = (2.0 * gl_FragCoord.xy-czm_viewport.zw)/czm_viewport.w;

该行代码的意思是将绘图区的宽高转换到[-1,1]的一个区间中。而在Appearance的片元着色器中相对于该Primitive对应的Geometry而言绘图区的宽高已经被限制在了[0,1]的区间了这可以由片元着色器的st推断因为片元着色器的st一般就是[0,1]。现在我们改造一下代码将绘图区限定到Appearance的纹理区间中

 vec2 p = 2.0 * v_st - 1.;//(2.0*fragCoord.xy-iResolution.xy)/iResolution.y;

因为v_st区间是[0,1],所以我们的变换一下到[-1,1],运行结果如下
在这里插入图片描述

到这里我们已经成功将shadertoy上这个示例的shader移植到Cesium上的Primitve中

四、着色器使用技巧

上面示例的shader成功移植到Cesium中的Primitive上。分享2个在使用shadertoy上的shader时的技巧

1、在Cesium中如何选择对应的载体作为画布
因为shadertoy上的shader类型canvas的绘制原理是将canvas作为一个画布在Cesium中我们可以选择Entity或Primtive来作为载体。又因为要方便操作片元着色器所以我们选择了Primitive但是Primitive中又有很多Geometry类型那具体使用哪种Geometry呢根据经验最好选择像RectangleGeometry、PlaneGeomery这种比较规则的几何类型因为shader绘图其实是根据纹理坐标来实现的而这种规则的几何往往 它的纹理坐标也比较规则。

2、如何去除黑色背景
虽然我们已经成功将shader移植到Cesium中但是黑色的背景着实有点丑我们使用shader的初衷是为了好看、酷炫这效果好像有点违背了我们的初衷。那我们该如何去除这个黑色的背景呢我们可以分析一下这个黑色其实黑色的值就是vec3(0,0,0)rgb三个分量越接近于0就越黑当然这也代表着r+g+b约接近0所以我们可以这样去消除黑色背景。

out_FragColor = vec4(color,color.r+color.g+color.g);

在这里插入图片描述

这样就可以了。

五、难度加深,带有输入纹理数据的着色器示例(应用)

实现一个shader上带有输入纹理数据的例子
在这里插入图片描述

按照 “在Cesium中如何使用” 一节的思路我们先创建好Primitive、Appearance然后将着色器代码拷贝进来并修改相关参数。

let xMin = 115.894604, yMin = 39.516896, xMax = 117.431959, yMax = 40.630521;
const rectangle = new Cesium.RectangleGeometry({
    rectangle: rect,
    height: 10000.0,
});
const geometry = Cesium.RectangleGeometry.createGeometry(rectangle);
viewer.scene.primitives.add(new Cesium.Primitive({
    geometryInstances: new Cesium.GeometryInstance({
        geometry: geometry
    }),
    appearance: new Cesium.MaterialAppearance({
       material:new Cesium.Material({
        fabric: { 
                uniforms: {
                    image:"./texture.png"
                }
            }
       })
        ,
        fragmentShaderSource: ` 
        in vec3 v_positionEC; 
        in vec3 v_normalEC; 
        in vec2 v_st;

        // Maximum number of cells a ripple can cross.
        #define MAX_RADIUS 2

        // Set to 1 to hash twice. Slower, but less patterns.
        #define DOUBLE_HASH 0

        // Hash functions shamefully stolen from:
        // https://www.shadertoy.com/view/4djSRW
        #define HASHSCALE1 .1031
        #define HASHSCALE3 vec3(.1031, .1030, .0973)

        float hash12(vec2 p)
        {
            vec3 p3  = fract(vec3(p.xyx) * HASHSCALE1);
            p3 += dot(p3, p3.yzx + 19.19);
            return fract((p3.x + p3.y) * p3.z);
        }

        vec2 hash22(vec2 p)
        {
            vec3 p3 = fract(vec3(p.xyx) * HASHSCALE3);
            p3 += dot(p3, p3.yzx+19.19);
            return fract((p3.xx+p3.yz)*p3.zy);

        }

        void main(  )
        {

            //float resolution = 10. * exp2(-3.*iMouse.x/iResolution.x);
           // vec2 uv = fragCoord.xy / iResolution.y * resolution;
            float resolution =20.;
            vec2 uv = v_st * resolution;
            vec2 p0 = floor(uv);

            float iTime=czm_frameNumber/100.;

            vec2 circles = vec2(0.);
            for (int j = -MAX_RADIUS; j <= MAX_RADIUS; ++j)
            {
                for (int i = -MAX_RADIUS; i <= MAX_RADIUS; ++i)
                {
                    vec2 pi = p0 + vec2(i, j);
                    #if DOUBLE_HASH
                    vec2 hsh = hash22(pi);
                    #else
                    vec2 hsh = pi;
                    #endif
                    vec2 p = pi + hash22(hsh);

                    float t = fract(0.3*iTime + hash12(hsh));
                    vec2 v = p - uv;
                    float d = length(v) - (float(MAX_RADIUS) + 1.)*t;

                    float h = 1e-3;
                    float d1 = d - h;
                    float d2 = d + h;
                    float p1 = sin(31.*d1) * smoothstep(-0.6, -0.3, d1) * smoothstep(0., -0.3, d1);
                    float p2 = sin(31.*d2) * smoothstep(-0.6, -0.3, d2) * smoothstep(0., -0.3, d2);
                    circles += 0.5 * normalize(v) * ((p2 - p1) / (2. * h) * (1. - t) * (1. - t));
                }
            }
            circles /= float((MAX_RADIUS*2+1)*(MAX_RADIUS*2+1));

            float intensity = mix(0.01, 0.15, smoothstep(0.1, 0.6, abs(fract(0.05*iTime + 0.5)*2.-1.)));
            vec3 n = vec3(circles, sqrt(1. - dot(circles, circles)));
            vec3 color = texture(image_0, uv/resolution - intensity*n.xy).rgb + 5.*pow(clamp(dot(n, normalize(vec3(1., 0.7, 0.5))), 0., 1.), 6.);
            out_FragColor = vec4(color, 1.0);
        }
    `
    })
}));

分析代码:

float resolution = 10. * exp2(-3.*iMouse.x/iResolution.x);
vec2 uv = fragCoord.xy / iResolution.y * resolution;

意思是将uv转换到[0,x]的区间在Cesium中应该这样写我们先固定一个值

float resolution =20.;
vec2 uv = v_st * resolution;

本节的重点是

vec3 color = texture(iChannel0, uv/resolution - intensity*n.xy).rgb + 5.*pow(clamp(dot(n,normalize(vec3(1., 0.7, 0.5))), 0., 1.), 6.);
texture(iChannel0, uv/resolution - intensity*n.xy).rgb

从纹理中采样颜色值这个iChannel0是sampler2D在Cesium对应Cesium.Texture不过我们可以直接传一张图片Cesium会自动为我们封装成Cesium.Texture。将图片传递到着色器这里我们采用Materail的属性uniforms需要注意的是如果您在Appearance的片元着色器中使用Material中的uniforms参数值时你必须在参数名的后面加上一个序号比如你的第一个参数为在Material的uniforms中为image那在Appearance的片元着色器中必须使用image_0如下

texture(image_0, uv/resolution - intensity*n.xy).rgb

在这里插入图片描述

六、Perlin-Worley噪声实现云图

shadertoy示例地址https://www.shadertoy.com/view/3dVXDc

在这里插入图片描述

该示例使用了Perlin+Worley柏林+沃利噪声来实现云的效果并且是先将结果绘制到一张Texture中然后在主函数中根据纹理坐标读取纹理中的值。
1、首先是噪声函数定义在Common中
在这里插入图片描述

2、然后是绘制到Texture中过程在BufferA中
在这里插入图片描述

3、最后在主函数中调用
在这里插入图片描述

4、主函数中为了显示了各个噪声的结果将画布拆分为了5份我们可以注释掉一些不需要的结果显示比如修改代码如下

void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
    vec2 st = fragCoord / iResolution.xy;
    vec2 uv = fragCoord / iResolution.y;
    st.x *= 5.; // 5 columns for different noises
    uv -= .02 * iTime;

    vec3 col = vec3(0.);

    float perlinWorley = textureLod(iChannel0, uv * .5, 0.).x;

    // worley fbms with different frequencies
    vec3 worley = textureLod(iChannel0, uv, 0.).yzw;
    float wfbm = worley.x * .625 +
                 worley.y * .125 +
                 worley.z * .25; 

    // cloud shape modeled after the GPU Pro 7 chapter
    float cloud = remap(perlinWorley, wfbm - 1., 1., 0., 1.);
    cloud = remap(cloud, .85, 1., 0., 1.); // fake cloud coverage

    // if (st.x < 1.)
    //     col += perlinWorley;
    // else if(st.x < 2.)
    //     col += worley.x;
    // else if(st.x < 3.)
    //    col += worley.y;
    // else if(st.x < 4.)
    //     col += worley.z;
    // else if(st.x < 5.)
        col += cloud;

    // column dividers
    // float div = smoothstep(.01, 0., abs(st.x - 1.));
    // div += smoothstep(.01, 0., abs(st.x - 2.));
    // div += smoothstep(.01, 0., abs(st.x - 3.));
    // div += smoothstep(.01, 0., abs(st.x - 4.));

    // col = mix(col, vec3(0., 0., .866), div);

    fragColor = vec4(col,1.0);
}
因为我们不需要绘制到Texture的步骤所以需要合并BufferA和主函数的代码
  uv -= .02 * iTime;
    vec2 m = vec2(0.5);

    vec4 col = vec4(0.);

    float slices = 128.; // number of layers of the 3d texture
    float freq = 4.;

    float pfbm= mix(1., perlinfbm(vec3(uv, floor(m.y*slices)/slices), 4., 7), .5);
    pfbm = abs(pfbm * 2. - 1.); // billowy perlin noise

    col.g += worleyFbm(vec3(uv, floor(m.y*slices)/slices), freq);
    col.b += worleyFbm(vec3(uv, floor(m.y*slices)/slices), freq*2.);
    col.a += worleyFbm(vec3(uv, floor(m.y*slices)/slices), freq*4.);
    col.r += remap(pfbm, 0., 1., col.g, 1.); // perlin-worley 

    float perlinWorley =  col.r;

    // worley fbms with different frequencies
    vec3 worley = col.yzw;
    float wfbm = worley.x * .625 +
                 worley.y * .125 +
                 worley.z * .25; 

    // cloud shape modeled after the GPU Pro 7 chapter
    float cloud = remap(perlinWorley, wfbm - 1., 1., 0., 1.);
    cloud = remap(cloud, .65, 1., 0., 1.); // fake cloud coverage

    vec3 col_ = vec3(0.); 
    col_ += cloud;

5、在cesium中如何实现我们先创建好Primitive、Appearance然后将着色器代码拷贝进来并修改相关参数

let xMin = 115.894604, yMin = 39.516896, xMax = 117.431959, yMax = 40.630521;
const rectangle = new Cesium.RectangleGeometry({
    rectangle: rect,
    height: 10000.0,
});
const geometry = Cesium.RectangleGeometry.createGeometry(rectangle);
viewer.scene.primitives.add(new Cesium.Primitive({
    geometryInstances: new Cesium.GeometryInstance({
        geometry: geometry
    }),
    appearance: new Cesium.MaterialAppearance({
       material:new Cesium.Material({
        fabric: { 
                uniforms: {
                    image:"./texture.png"
                }
            }
       })
        ,
           fragmentShaderSource: ` 
            in vec3 v_positionEC;//顶点 相机(眼睛)坐标系 
            in vec3 v_normalEC;//顶点法线 相机(眼睛)坐标系 
            in vec2 v_st;//纹理坐标 uv
            /**
            This tab contains all the necessary noise functions required to model a cloud shape.
            */

            // Hash by David_Hoskins
            #define UI0 1597334673U
            #define UI1 3812015801U
            #define UI2 uvec2(UI0, UI1)
            #define UI3 uvec3(UI0, UI1, 2798796415U)
            #define UIF (1.0 / float(0xffffffffU))

            vec3 hash33(vec3 p)
            {
                uvec3 q = uvec3(ivec3(p)) * UI3;
                q = (q.x ^ q.y ^ q.z)*UI3;
                return -1. + 2. * vec3(q) * UIF;
            }

            float remap(float x, float a, float b, float c, float d)
            {
                return (((x - a) / (b - a)) * (d - c)) + c;
            }

            // Gradient noise by iq (modified to be tileable)
            float gradientNoise(vec3 x, float freq)
            {
                // grid
                vec3 p = floor(x);
                vec3 w = fract(x);

                // quintic interpolant
                vec3 u = w * w * w * (w * (w * 6. - 15.) + 10.);

                // gradients
                vec3 ga = hash33(mod(p + vec3(0., 0., 0.), freq));
                vec3 gb = hash33(mod(p + vec3(1., 0., 0.), freq));
                vec3 gc = hash33(mod(p + vec3(0., 1., 0.), freq));
                vec3 gd = hash33(mod(p + vec3(1., 1., 0.), freq));
                vec3 ge = hash33(mod(p + vec3(0., 0., 1.), freq));
                vec3 gf = hash33(mod(p + vec3(1., 0., 1.), freq));
                vec3 gg = hash33(mod(p + vec3(0., 1., 1.), freq));
                vec3 gh = hash33(mod(p + vec3(1., 1., 1.), freq));

                // projections
                float va = dot(ga, w - vec3(0., 0., 0.));
                float vb = dot(gb, w - vec3(1., 0., 0.));
                float vc = dot(gc, w - vec3(0., 1., 0.));
                float vd = dot(gd, w - vec3(1., 1., 0.));
                float ve = dot(ge, w - vec3(0., 0., 1.));
                float vf = dot(gf, w - vec3(1., 0., 1.));
                float vg = dot(gg, w - vec3(0., 1., 1.));
                float vh = dot(gh, w - vec3(1., 1., 1.));

                // interpolation
                return va + 
                       u.x * (vb - va) + 
                       u.y * (vc - va) + 
                       u.z * (ve - va) + 
                       u.x * u.y * (va - vb - vc + vd) + 
                       u.y * u.z * (va - vc - ve + vg) + 
                       u.z * u.x * (va - vb - ve + vf) + 
                       u.x * u.y * u.z * (-va + vb + vc - vd + ve - vf - vg + vh);
            }

            // Tileable 3D worley noise
            float worleyNoise(vec3 uv, float freq)
            {    
                vec3 id = floor(uv);
                vec3 p = fract(uv);

                float minDist = 10000.;
                for (float x = -1.; x <= 1.; ++x)
                {
                    for(float y = -1.; y <= 1.; ++y)
                    {
                        for(float z = -1.; z <= 1.; ++z)
                        {
                            vec3 offset = vec3(x, y, z);
                            vec3 h = hash33(mod(id + offset, vec3(freq))) * .5 + .5;
                            h += offset;
                            vec3 d = p - h;
                               minDist = min(minDist, dot(d, d));
                        }
                    }
                }

                // inverted worley noise
                return 1. - minDist;
            }

            // Fbm for Perlin noise based on iq's blog
            float perlinfbm(vec3 p, float freq, int octaves)
            {
                float G = exp2(-.85);
                float amp = 1.;
                float noise = 0.;
                for (int i = 0; i < octaves; ++i)
                {
                    noise += amp * gradientNoise(p * freq, freq);
                    freq *= 2.;
                    amp *= G;
                }

                return noise;
            }

            // Tileable Worley fbm inspired by Andrew Schneider's Real-Time Volumetric Cloudscapes
            // chapter in GPU Pro 7.
            float worleyFbm(vec3 p, float freq)
            {
                return worleyNoise(p*freq, freq) * .625 +
                         worleyNoise(p*freq*2., freq*2.) * .25 +
                         worleyNoise(p*freq*4., freq*4.) * .125;
            }

            void main(  )
            {
                vec2 uv =  v_st;
                float iTime=czm_frameNumber/100.;
                uv -= .02 * iTime;
                vec2 m = vec2(0.5);

                vec4 col = vec4(0.);

                float slices = 128.; // number of layers of the 3d texture
                float freq = 4.;

                float pfbm= mix(1., perlinfbm(vec3(uv, floor(m.y*slices)/slices), 4., 7), .5);
                pfbm = abs(pfbm * 2. - 1.); // billowy perlin noise

                col.g += worleyFbm(vec3(uv, floor(m.y*slices)/slices), freq);
                col.b += worleyFbm(vec3(uv, floor(m.y*slices)/slices), freq*2.);
                col.a += worleyFbm(vec3(uv, floor(m.y*slices)/slices), freq*4.);
                col.r += remap(pfbm, 0., 1., col.g, 1.); // perlin-worley 

                float perlinWorley =  col.r;

                // worley fbms with different frequencies
                vec3 worley = col.yzw;
                float wfbm = worley.x * .625 +
                             worley.y * .125 +
                             worley.z * .25; 

                // cloud shape modeled after the GPU Pro 7 chapter
                float cloud = remap(perlinWorley, wfbm - 1., 1., 0., 1.);
                cloud = remap(cloud, .65, 1., 0., 1.); // fake cloud coverage

                vec3 col_ = vec3(0.); 
                col_ += cloud;  

                out_FragColor = vec4(col_,col.r); `
    })
}));

这样就实现了体积云类似效果
在这里插入图片描述

七、流体水面
示例地址:https://www.shadertoy.com/view/4dd3Rl
在这里插入图片描述

取到代码在cesium中如何使用参考上面体积云的示例步骤相同。

            <link href="https://csdnimg.cn/release/blogv2/dist/mdeditor/css/editerView/markdown_views-f23dff6052.css" rel="stylesheet">
            <link href="https://csdnimg.cn/release/blogv2/dist/mdeditor/css/style-e504d6a974.css" rel="stylesheet">