用three.js实现边缘浪花和退潮湿岸特效

在three.js的官网上有一个国外大佬写的Webgl网页,名叫coastalworld

可以看到在海平面和其他物体接触时,会有一条颜色带,这个就是简易版的边缘浪花效果,如何实现呢?

1.深度纹理

由于篇幅的原因,深度纹理的这种实现方式这里先不阐述了,感兴趣的话将来可能还会再出一片文章来解析一下这种实现方式(实际上是懒癌犯了)

2.修改被影响的物体的着色器

这种方式也是coastalworld 所采用的,即在h的这段高度(也可以称为浪花的厚度)内的顶点的颜色是白色, 需要用到onBeforeCompile这个函数,我们需要去修改他片元着色器中的代码,在此之前我们还需要做一些准备工作,获取海平面在世界空间坐标系下的position.y,可以通过getWorldPosition这个函数获取 并且作为uniform传入到着色器中

js 复制代码
const localToWorld = new THREE.Vector3()
mesh.getWorldPosition(localToWorld)

通过localToWorld.y即可取到世界空间坐标系下的y值了,到此位置我们的准备工作算是做完了,接下来开始修改片元着色器的代码

由于我们无法在外界访问到onBeforeCompile回调函数中的参数,所以我们可以利用js的特性去修改shader中的数据

js 复制代码
const params = {
uPosY:{value:0
}

同时也不要忘了在帧动画函数中更新数据

js 复制代码
params.uPosY.value = ocean.PosY                           

要想在规定的厚度(高度内)让顶点颜色变成白色我们可以使用glsl的内置函数step,同岁为了让边缘不是那么锐利,可以使用smoothstep来进行处理

step(edge,x):阶跃函数。当x < edge时返回0,否则返回1

smoothstemp(edge0, edge1, x):平滑阶跃函数。当x <= edge0,返回0,当x >= edge1时,返回1。否则在[0, 1]区间执行Hermite插值(edge0 < x < edge1)

js 复制代码
 m.onBeforeCompile = (shader) => {
                    shader.uniforms.uPosY = params.uPosY
                   shader.vertexShader = shader.vertexShader.replace(
                        /* glsl */ `#include <common>`,
                        /* glsl */ `#include <common>
                        varying vec4 world_pos;
                        `
                    ).replace(
                        /* glsl */ `#include <project_vertex>`,
                        /* glsl */ `#include <project_vertex>
                        //获取世界空间坐标系下的顶点位置
                        vec4 modelPosition = modelMatrix *  vec4( transformed, 1.0 );
                        world_pos = modelPosition;
                        `
                    );
                    shader.fragmentShader = shader.fragmentShader.replace(
                        /* glsl */ `#include <common>`,
                        /* glsl */ `#include <common>
                        uniform float uPosY 
                        varying vec4 world_pos;
                    ).replace(
                        /* glsl */ `#include <opaque_fragment>`,
                        /* glsl */ `#include <opaque_fragment>
                        /* 边缘浪花 */
                        float thicknesses = .05; //厚度
                        float edge = 0.01;//边缘
                        float c = step(uPosY,world_pos.y) - smoothstep(uPosY+thicknesses,uPosY+thicknesses+edge,world_pos.y);
                        gl_FragColor = vec4(vec3(c) ,gl_FragColor.a);
                       `
                    )

                }

注:在three.js的最近几个版本中片元着色器需要替换的位置为#include <opaque_fragment> 至此,我们的边缘浪花特效算是完成了

接下来就是边缘浪花特效的实现了,这里我只提供一种我自己的思路,如果有更好的思路还请大佬补充

由于该项目里海平面涨潮和退潮的高度都是固定的,所以我们实现退潮湿岸的效果就相对容易一些,我们需要做一些准备工作,获取涨潮时的最大高度退潮的总时间是否退潮

在帧动画函数中

js 复制代码
  lastPosY = PosY //需要世界空间坐标系下的position.y
  
  //海平面 退/涨潮的函数
  mesh.position.y = Power1.easeInOut(Math.abs((this._uniform.uTime.value % 1) - 0.5) * 2) * 0.5 + SEA_LEVEL;

  if (lastPosY > PosY) {
      maxPosY = Math.max(lastPosY, maxPosY)
      ebbTime += dt;
      // 退潮
      Ebb = true
    } else {
      Ebb = false
      ebbTime = 0
         }

我们需要让h这一段内的顶点颜色暗一点,使用一个系数乘以原本的颜色即可,但是这样子会让沙滩整体变暗,要怎么做呢,依然是利用step 函数,算出一个需要减去的rgb 值,让原本在这一段内的顶点的原本颜色减去我们算出来的这个值 就可以实现了,通过计算当前时间和总退潮时间的百分比,应用到需要减去的这个值上,就实现湿岸慢慢褪去的效果

代码如下:

glsl 复制代码
 float d = 0.;
    //是否进行退潮
  if(uEbb){
  //利用smoothstep函数让边缘不是那么锐利,利用clamp函数限制变暗的程度
    d=clamp(step(uPosY,world_pos.y) - smoothstep(uMaxPosY,uMaxPosY+.05,world_pos.y),0.0,0.2);
    }else{
    d=0.0;
     }
     
    //计算退潮的当前时间和总时间的比值,并取反
     float progress = 1.-smoothstep(0.,1.,uEbbTime / 5.);
    //计算出当前需要减去的颜色
     vec3 reduceColor = vec3(gl_FragColor.xyz)*d*progress;
    //最终的颜色
     vec3 finColor = gl_FragColor.xyz +vec3(c)-reduceColor;

 gl_FragColor = vec4(finColor ,gl_FragColor.a);

总结

到此为止,我们已经实现了一个简易版的边缘浪花和退潮的特效了,其实还有很多很多可以优化的地方,希望大家可以从中学到思路,并且优化自己的算法,当然如果你有更好的思路也可以和我分享,谢谢!

相关推荐
ycgg18 小时前
深入理解 AbortSignal:前端异步操作取消的原生方案
前端
妮妮喔妮18 小时前
前端字节面试大纲
前端·面试·职场和发展
白兰地空瓶18 小时前
告别“千里传荔枝”:React useContext 打造跨层级通信“任意门”
前端·react.js
恋猫de小郭18 小时前
Flutter 小技巧之帮网友理解 SliverConstraints overlap
android·前端·flutter
小oo呆18 小时前
【自然语言处理与大模型】LangChainV1.0入门指南:核心组件Structured Output
前端·javascript·easyui
Mapmost18 小时前
【高斯泼溅】3DGS城市模型从“硬盘杀手”到“轻盈舞者”?看我们如何实现14倍压缩
前端
AC赳赳老秦18 小时前
农业智能化:DeepSeek赋能土壤与气象数据分析,精准预测病虫害,守护丰收希望
java·前端·mongodb·elasticsearch·html·memcache·deepseek
囊中之锥.18 小时前
《HTML 网页构造指南:从基础结构到实用标签》
前端·html
饼饼饼18 小时前
从 0 到 1:前端 CI/CD 实战(第二篇:用Docker 部署 GitLab)
前端·自动化运维
qq_4061761418 小时前
JavaScript的同步与异步
前端·网络·tcp/ip·ajax·okhttp