这段时间一直在看如何用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. git 判断路径是否是 git 仓库

    git 判断路径是否是 git 仓库 import subprocess repo_dir = "../path/to/check/" command = ['git', 'rev ...

  2. jQuery.fn.extend() 函数详解

    jQuery.fn.extend()函数用于为jQuery扩展一个或多个实例属性和方法(主要用于扩展方法). jQuery.fn是jQuery的原型对象,其extend()方法用于为jQuery的原型 ...

  3. <script language= "javascript " for= "window " event= "onload ">

    狼-志 EVENT   event   设置或获取脚本编写用于的事件   FOR   htmlFor   设置或获取绑定到事件脚本的对象. 可以这样理解. <script   language= ...

  4. BZOJ 2759 一个动态树好题 (LCT)

    PoPoQQQ 再一次orz-没看得特别明白的可以回来看看蒟蒻的补充口胡 我这里提一下关于splaysplaysplay维护的子树信息- 在原树上考虑,对于每一个点iii都有这样一个信息xi=ki∗x ...

  5. Elasticsearch7.1中文文档-第一章-入门

    安装openjdk wget --no-cookies --no-check-certificate --header "Cookie: oraclelicense=accept-secur ...

  6. luogu 3698 [CQOI2017]小Q的棋盘 树形dp

    Code: #include <bits/stdc++.h> #define N 107 #define setIO(s) freopen(s".in","r ...

  7. ec20 queclocator V1. 0 test

    AT+QICSGP=1,1,"UNIWAP","","",1 AT+QIACT=1 AT+QLOCCFG="contextid&q ...

  8. 分布式缓存Redis之Pipeline(管道)

    Redis的pipeline(管道)功能在命令行中没有,但redis是支持pipeline的,而且在各个语言版的client中都有相应的实现. 由于网络开销延迟,就算redis server端有很强的 ...

  9. java将PDF的前几页拆出来组成新pdf

    /** * 截取pdfFile的第from页至第end页,组成一个新的文件名 * @param pdfFile 需要分割的PDF * @param savepath 新PDF * @param fro ...

  10. Lombok Pojo默认初始值问题

    Lombok以注解形式来简化java代码,提高开发效率.比如我们常用的@Builder.@Data.@AllArgsConstructor.@NoArgsConstructor.@ToString等. ...