这段时间一直在看如何用shader绘制一个流动的水面,直接用贴图(高度图、法向贴图)实现的方法,这里就不讨论了。

搜了一大波博客资料,感觉存在如下一些问题

1⃣️大多数资料都是基于opengl实现(或者是unity里的shader),过多关注点在渲染上面而不是水波的mesh实现上,让人没有看下去的欲望

2⃣️有的就直接是照搬别人的博客,公式大段大段地搬,却没有自己的一丝见解,太过敷衍

3⃣️代码不加注释,对前来学习者不太友好

4⃣️针对webgl的实现,网上的资料太少(虽然已经有了opengl的实现)

所以在查阅了资料之后,决定写一个webgl版本的实现(three.js + shader)

nvidia官方提供的水波实现方程(其实网上大多数博客里的方程式应该都是源于此处):传送门

对应的,知乎有一篇文章,基本上就是上面网站的中文版,但是作者加入了一点自己思考后的想法,个人觉得很好,推荐一下:GPU Gems 基于物理模型的水面模拟

-------------------------------------------------------------------华丽的分割线-------------------------------------------------------------------------

一、PlaneGeometry + ShaderMaterial + 正弦波方程式

    //1.PlaneGeometry
this.seaMaterial = new THREE.ShaderMaterial({
uniforms: {
time:{type:'f',value:},
},
vertexShader: seashader.vs,
fragmentShader: seashader.fs,
side:THREE.DoubleSide,
wireframe: true
});
this.geometry = new THREE.PlaneGeometry( ,,,);
var plane = new THREE.Mesh( this.geometry, this.seaMaterial );
plane.rotation.x= -Math.PI/;
this.scene.add( plane );
const seashader = {
vs:`
uniform float time; void main(){
float x = position.x;
float y = position.y;
float PI = 3.141592653589; float sz = 0.0;
float ti = 0.06;
float index = 1.0;
vec2 dir;//波的方向
//四条正弦波相加
for(int i = ;i<;i++){
ti = ti + 0.0005;
index = index + 0.1;
if(mod(index,2.0)==0.0){
dir = vec2(1.0,ti);
}else{
dir = vec2(-1.0,ti);
}
float l1 = 2.0 * PI / (0.5);//波长
float s1 = 10.0 * 2.0 / l1;//速度
float z1 = 1.0 * sin(dot(normalize(dir),vec2(x,y)) * l1 + time * s1);//正弦波方程式
sz +=z1;
}
gl_Position = projectionMatrix * modelViewMatrix * vec4(x,y,sin(sz) * 10.0,1.0);
}
`,
fs:`
void main(){
gl_FragColor = vec4(./.,./.,./.,1.0);
}
`,
}
//animation
if(this.seaMaterial){
this.seaMaterial.uniforms.time.value += 0.01;
}

参考的水波方程式:

效果如下:

温馨提示:

调参很重要,水波方向、波长、波的叠加数量,如果取值不当,生成的波将会很诡异(和plane尺寸以及分割的分数有关,因为这些参数将会直接影响vs接收的顶点坐标)。

可以看到上述代码vs中顶点z:sin(sz) * 10.0,在测试中,我发现对sz进行sin处理,得到的水波细节会更多一点,就是凹凸的感觉会多一点。

正弦波具有圆润的外观-这可能正是我们想要一个平静的田园池塘所需要的。

二、Gerstner波方程式

接下来只贴shader部分

const gerstnershader = {
vs:`
uniform float time; void main(){
float x = position.x;
float y = position.y;
float PI = 3.141592653589; float sx = 0.0;
float sy = 0.0;
float sz = 0.0; float ti = 0.0;
float index = 1.0;
vec2 dir;//水波方向
for(int i = ;i<;i++){
ti = ti + 0.0005;
index +=1.0;
if(mod(index,2.0)==0.0){
dir = vec2(1.0,ti);
}else{
dir = vec2(-1.0,ti);
}
float l1 = 2.0 * PI / (0.5 + ti);//波长
float s1 = 20.0 * 2.0 / l1;//速度
float x1 = 1.0 * dir.x * sin(dot(normalize(dir),vec2(x,y)) * l1 + time * s1);
float y1 = 1.0 * dir.y * sin(dot(normalize(dir),vec2(x,y)) * l1 + time * s1);
float z1 = 1.0 * sin(dot(normalize(dir),vec2(x,y)) * l1 + time * s1);
sx +=x1;
sy +=y1;
sz +=z1;
}
sx = x + sx;
sy = y + sy;
gl_Position = projectionMatrix * modelViewMatrix * vec4(sx,sy,sin(sz) * 10.0,1.0);
}
`,
fs:`
void main(){
gl_FragColor = vec4(./.,./.,./.,1.0);
}
`,
}

参考的水波方程式:

gerstner波相较正弦波的特点是:波峰陡峭、波谷平坦。

效果如下:

用shader实现流动的水面(webgl)的更多相关文章

  1. Professional.WebGL.Programming-Chapter 2(高级WebGL编程——第二章)

    (目前发现一些文章被盗用的情况,我们将在每篇文章前面添加原文地址,本文源地址:http://www.cnblogs.com/idealer3d/p/3508251.html) 这一章主要通过创建一个三 ...

  2. 教你用webgl快速创建一个小世界

    收录待用,修改转载已取得腾讯云授权 作者:TAT.vorshen Webgl的魅力在于可以创造一个自己的3D世界,但相比较canvas2D来说,除了物体的移动旋转变换完全依赖矩阵增加了复杂度,就连生成 ...

  3. WebGL简易教程(二):向着色器传输数据

    目录 1. 概述 2. 示例:绘制一个点(改进版) 1) attribute变量 2) uniform变量 3) varying变量 3. 结果 4. 参考 1. 概述 在上一篇教程<WebGL ...

  4. 网页万能排版布局插件,web视图定位布局创意技术演示页

    html万能排版布局插件,是不是感觉很强大,原理其实很简单,不过功能很强大哈哈,大量节省排版布局时间啊! test.html <!doctype html> <html> &l ...

  5. Angle

    1 What is Angle. The goal of ANGLE is to allow Windows users to seamlessly run WebGL and other OpenG ...

  6. JavaScript 移动和触摸框架

     jQuery Mobile : 是 jQuery 在手机上和平板设备上的版本号. jQuery Mobile 不仅会给主流移动平台带来jQuery核心库.而且会公布一个完整统一的jQuery移动UI ...

  7. 从0开发3D引擎(九):实现最小的3D程序-“绘制三角形”

    目录 上一篇博文 运行测试截图 需求分析 目标 特性 头脑风暴 确定需求 总体设计 具体实现 新建Engine3D项目 实现上下文 实现_init 实现"获得WebGL上下文" 实 ...

  8. Cesium参考资源

    Reference resources cesium官网 cesium 下载 cesium官方文档 APIs cesium-workshop github cesium 官方示例 cesium git ...

  9. WebGL的shader

    WebGL的shader(着色器)有2种:vertexShader(定点着色器)和 fragmentShader(片段着色器) 顶点着色器:定义点的位置.大小 片元着色器:定义画出来的物体的材质(颜色 ...

随机推荐

  1. docker化canal与canal-adapter

    今日公司需求,需要将mysql更新实时同步到kafka中,后来又要将数据库中的一张表的变化实时同步到另一台mysql中,于是乎canal与canal-adapter紧急解决,其中踩了不少坑,下面为总结 ...

  2. url传参特殊字符问题(+、%、#等)

    这样的话,你传的大多数带特殊符号的参数,都能在后台拿到,但是, url中可能用到的特殊字符及在url中的经过编码后的值:(此表格借鉴) 字符   特殊字符的含义 URL编码 #   用来标志特定的文档 ...

  3. 题解 [51nod1607] 卷积和

    题面 解析 神仙LZF随机找出的毒瘤题. 一开始读题过于草率导致\(naive\)了. step 1 看上去特别像数位DP(实际上也有一点). 先预处理出有\(i\)位的数(最高位不为\(0\))的数 ...

  4. Java xml和map,list格式的转换-摘抄

    import java.io.ByteArrayOutputStream; import java.util.ArrayList; import java.util.HashMap; import j ...

  5. 移动端H5开发问题记录

    1. 当弹出键盘时,会改变页面高度,影响页面样式 通过window.onsize事件可以控制键盘弹出或消失的时候的样式 var h = document.body.scrollHeight // 用o ...

  6. toggle([speed],[easing],[fn]) 用于绑定两个或多个事件处理器函数,以响应被选元素的轮流的 click 事件。

    toggle([speed],[easing],[fn]) 概述 用于绑定两个或多个事件处理器函数,以响应被选元素的轮流的 click 事件. 如果元素是可见的,切换为隐藏的:如果元素是隐藏的,切换为 ...

  7. Linux+CLion+树莓派远程编译时,Cmake编译出现undefined reference to `vtable for MainWindow'的解决办法

    在win+CLion上进行远程qt开发时碰到以下错误: 错误提示: undefined reference to `vtable for MainWindow' 原因:源文件的目录结构有问题?? 解决 ...

  8. java实现自定义同步组件的过程

    实现同步组件twinsLock:可以允许两个线程同时获取到锁,多出的其它线程将被阻塞. 以下是自定义的同步组件类,一般我们将自定义同步器Sync定义为同步组件TwinsLock的静态内部类. 实现同步 ...

  9. webpack4 打包 library 遇到的坑

    output: { publicPath: '/', path: path.join(__dirname, 'lib'), filename: 'chart.js', library: 'tchart ...

  10. 树及其衍生算法(Trees and tree algorithms)

    1,二叉树(Binary tree) 二叉树:每一个节点最多两个子节点,如下图所示: 相关概念:节点Node,路径path,根节点root,边edge,子节点 children,父节点parent,兄 ...