# Cesium进阶学习四、shadertoy（着色器）

最新推荐文章于 2024-10-15 11:50:42 发布
Cesium进阶学习 最新推荐文章于 2024-10-15 11:50:42 发布
阅读量1.9k 收藏 12
点赞数 23
分类专栏： Cesium进阶学习 文章标签： 学习 着色器
版权声明：本文为博主原创文章，遵循 CC 4.0 BY-SA 版权协议，转载请附上原文出处链接和本声明。
本文链接：https://blog.csdn.net/weixin_43700915/article/details/142754373
版权

一、简介
shadertoy是一个基于webgl的分享Shader的开放平台，用户可以在上面根据既定规则分享自己编写的shader。在Cesium中要想实现一些酷炫的效果，唯一的一条路就是写shader，而shader的编写应该算是图形学中的难度天花板了。好在有很多shader大佬分享了自己编写的glsl效果，比如shadertoy网站上就是这些大牛们的作品，我们可以借鉴。

二、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、其次是iResolution，iResolution代表的是当前画布的宽高，即绘图区域的尺寸，所以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、最后我们按照上面说的替换掉fragCoord，fragCoord、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中如何使用参考上面体积云的示例，步骤相同。

确定要放弃本次机会？
福利倒计时
: :

立减 ¥

普通VIP年卡可用
立即使用
Cesium进阶学习
关注 关注

- 23
  点赞
- 踩
- 12
  收藏
  觉得还不错? 一键收藏
- 0
  评论
- 分享
  复制链接
  分享到 QQ
  分享到新浪微博
  扫一扫
- 打赏
  打赏
- 打赏 举报
  举报

专栏目录
0 条评论
写评论
博客
Cesium简介及发展历史
纸飞机的旅行的博客
02-20 8045
Cesium简介及发展历史
博客
Cesium 颜色过渡渲染 - Shader
TqbcAbap的博客
09-23 931
在Cesium中，我们可以使用着色器（Shader）来实现颜色的渐变效果。着色器是一种在图形渲染过程中控制颜色、纹理和光照等方面的工具。通过编写自定义的着色器代码，我们可以实现各种独特的视觉效果。为了实现颜色的渐变效果，我们需要编写一个简单的片段着色器（Fragment Shader）。通过使用该插值因子，我们可以根据垂直位置来计算颜色的过渡效果。在使用这段着色器代码之前，我们需要将其与Cesium的渲染管道进行关联。是传入的Uniform变量，用来指定起始颜色和结束颜色。函数来插值计算当前片段的颜色。
博客
Cesium学习笔记
最新发布
weixin_57468559的博客
10-15 2308
Cesium是便用JavaScript开发的基于WebGL的实现三维地球和地图可视化的JS库，Cesium支持海量的三维模型数据，影像数据，地形高程数据，失量数据等丰富的地理教据的加载。在交通，规划，城市管理，地形仿真等领城有非常广泛的应用。Cesium为三维的GIS提供了一个高效的数据可视化平台。中文文档官网例子Cesium的特点①、跨平台，跨浏览器，无插件②、强大的地理数据可视化能力。Cesium自定义的3D Tiles数据格式，支持海量数据渲染。
Cesium进阶学习 CSDN认证博客专家 CSDN认证企业博客

34
  原创

6万+
  周排名

4万+
  总排名

3万+
  访问

  等级

797
  积分

451
  粉丝

435
  获赞

0
  评论

448
  收藏

私信
关注

### 热门文章

- Cesium进阶学习四、shadertoy（着色器） 1911
- openlayers6.4.3+geoserver+vue 1890
- Cesium高阶学习十二、体积云 1697
- Cesium基础教程篇八 1629
- Cesium高阶学习五、离屏渲染 1402

### 分类专栏

- Cesium基础知识专栏 10篇
- Cesium进阶学习 9篇
- Cesium高阶学习 12篇
- 大疆机场 1篇
- openlayers+geoserver+vue 1篇

### 最新文章

- 大疆上云Demo注意事项
- Cesium高阶学习十二、体积云
- Cesium高阶学习十一、模型特效

2024年32篇
2020年1篇
2019年1篇

### 目录

### 分类专栏

- Cesium基础知识专栏 10篇
- Cesium进阶学习 9篇
- Cesium高阶学习 12篇
- 大疆机场 1篇
- openlayers+geoserver+vue 1篇

### 目录

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
添加红包
插入表情
代码片

- HTML/XML
- objective-c
- Ruby
- PHP
- C
- C++
- JavaScript
- Python
- Java
- CSS
- SQL
- 其它

条评论被折叠 查看
被折叠的 条评论 为什么被折叠? 到【灌水乐园】发言
查看更多评论
添加红包
祝福语

请填写红包祝福语或标题

红包数量
个

红包个数最小为10个

红包总金额
元

红包金额最低5元

余额支付
当前余额3.43元 前往充值 >
需支付：10.00元

打赏作者

Cesium进阶学习

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付：¥1
获取中
扫码支付

您的余额不足，请更换扫码支付或充值

打赏作者

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明：

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

余额充值