这段时间一直在看如何用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. [转载]springboot--常用注解--@configration、@Bean

    springboot--常用注解--@configration.@Bean @Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME ...

  2. VS2017 Nuget服务器搭建,简易版

    熟悉Visual Studio 的小伙伴肯定听说过“Nuget”这个词,关于Nuget是如何如何方便就不再赘述了,这里主要介绍一下如何搭建个人的Nuget服务器. 直奔主题 第一步:打开VS2017 ...

  3. rac 关于RACScheduler的一点学习

    RACScheduler  信号调度器,是一个线性执行队列,rac中的信号可以在RACScheduler上执行任务.发送结果,底层用GCD封装的. rac中提供生成线程的几个方法: 1:schedul ...

  4. 软raid实现

    几种raid类型,我就不在这里赘述了,企业一般都是用raid卡,所以一般使用软raid的非常少,但是也有用的,所以就写一个软raid的实验吧,其实用处不大. 实验环境:centos6.9 需要的硬件: ...

  5. windows 10安装mongodb数据库

    简介 MongoDB是一个基于分布式文件存储的数据库.由C++语言编写.旨在为WEB应用提供可扩展的高性能数据存储解决方案.MongoDB是一个介于关系数据库和非关系数据库之间的产品,是非关系数据库当 ...

  6. P5357 【模板】AC自动机(二次加强版)

    思路 这题可以同时作为AC自动机和SAM的模板啊喂 AC自动机 对T建出AC自动机,把S在上面匹配,然后记录每个点被经过的次数,最后统计一次即可(暴力跳fail的复杂度是不对的) SAM 对S建出SA ...

  7. BZOJ2208 [Jsoi2010]连通数[缩点/Floyd传递闭包+bitset优化]

    显然并不能直接dfs,因为$m$会非常大,复杂度就是$O(mn)$: 这题有三种做法,都用到了bitset的优化.第二种算是一个意外的收获,之前没想到竟然还有这种神仙操作.. 方法一:缩点+DAG上b ...

  8. C# 委托、lambda表达式和事件 (7) 持续更新

    引用方法 在C++,函数指针只不过是一个指向内存位置的指针,它不是类型安全的. C# 委托 定义了返回类型和参数的类型.委托类包含对方法的引用,还可以包含多个方法引用. 定义委托 public del ...

  9. Mysql-sql行转列

    原始数据如下图所示:(商品的销售明细)date=业务日期:Item=商品名称:saleqty=销售数量 -- 建立测试数据(表)create table test (Date varchar(10), ...

  10. 【SaltStack官方版】—— returners——返回器

    ETURNERS 返回器 By default the return values of the commands sent to the Salt minions are returned to t ...