用shader实现流动的水面(webgl)
这段时间一直在看如何用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)的更多相关文章
- Professional.WebGL.Programming-Chapter 2(高级WebGL编程——第二章)
(目前发现一些文章被盗用的情况,我们将在每篇文章前面添加原文地址,本文源地址:http://www.cnblogs.com/idealer3d/p/3508251.html) 这一章主要通过创建一个三 ...
- 教你用webgl快速创建一个小世界
收录待用,修改转载已取得腾讯云授权 作者:TAT.vorshen Webgl的魅力在于可以创造一个自己的3D世界,但相比较canvas2D来说,除了物体的移动旋转变换完全依赖矩阵增加了复杂度,就连生成 ...
- WebGL简易教程(二):向着色器传输数据
目录 1. 概述 2. 示例:绘制一个点(改进版) 1) attribute变量 2) uniform变量 3) varying变量 3. 结果 4. 参考 1. 概述 在上一篇教程<WebGL ...
- 网页万能排版布局插件,web视图定位布局创意技术演示页
html万能排版布局插件,是不是感觉很强大,原理其实很简单,不过功能很强大哈哈,大量节省排版布局时间啊! test.html <!doctype html> <html> &l ...
- Angle
1 What is Angle. The goal of ANGLE is to allow Windows users to seamlessly run WebGL and other OpenG ...
- JavaScript 移动和触摸框架
jQuery Mobile : 是 jQuery 在手机上和平板设备上的版本号. jQuery Mobile 不仅会给主流移动平台带来jQuery核心库.而且会公布一个完整统一的jQuery移动UI ...
- 从0开发3D引擎(九):实现最小的3D程序-“绘制三角形”
目录 上一篇博文 运行测试截图 需求分析 目标 特性 头脑风暴 确定需求 总体设计 具体实现 新建Engine3D项目 实现上下文 实现_init 实现"获得WebGL上下文" 实 ...
- Cesium参考资源
Reference resources cesium官网 cesium 下载 cesium官方文档 APIs cesium-workshop github cesium 官方示例 cesium git ...
- WebGL的shader
WebGL的shader(着色器)有2种:vertexShader(定点着色器)和 fragmentShader(片段着色器) 顶点着色器:定义点的位置.大小 片元着色器:定义画出来的物体的材质(颜色 ...
随机推荐
- CentOS7 内核优化 修改参数
一:内核简介 内核是操作系统最基本的部分.它是为众多应用程序提供对计算机硬件的安全访问的一部分软件,这种访问是有限的,并且内核决定一个程序在什么时候对某部分硬件操作多长时间. 内核的分类可分为单内核和 ...
- 开发中少不了的Fun -- js判断设备
判断是否是移动设备 var ua = window.navigator.userAgent.toLowerCase(); if(ua.indexOf('mobile')!== -1){ return ...
- UVALive 6858——分类讨论&&水题
题目 链接 题意:对于一个$n \times m$的矩阵的最外一圈,问是否能用$k \times 1$的方块填满 分析 考虑左右两边的情况,分类讨论,切记考虑所有可能的情形. #include< ...
- P3384——树链剖分&&模板
题目描述 链接 如题,已知一棵包含N个结点的树(连通且无环),每个节点上包含一个数值,需要支持以下操作: 操作1: 格式: 1 x y z 表示将树从x到y结点最短路径上所有节点的值都加上z 操作2: ...
- 【原创】洛谷 LUOGU P3372 【模板】线段树1
P3372 [模板]线段树 1 题目描述 如题,已知一个数列,你需要进行下面两种操作: 1.将某区间每一个数加上x 2.求出某区间每一个数的和 输入输出格式 输入格式: 第一行包含两个整数N.M,分别 ...
- 新西达电调初始化代码,使用nodejs ffi技术调用wiringpi,代码使用typescript编写
这是我设计的F450四轴飞行器飞控代码的一部分 运行在orangepi-zero上,操作系统是armbian,思路是使用node-ffi调用wiringpi的so库与GPIO通信,然后控制端逻辑代码使 ...
- Spring Cloud Eureka(三):认识Eureka Server 与 Eureka Client
Spring Cloud Netflix 是什么 This project provides Netflix OSS integrations for Spring Boot apps through ...
- matlab 计算灰度图像的一阶矩、二阶矩、三阶矩
一阶矩,定义了每个颜色分量的平均强度 二阶矩,反映待测区域颜色方差,即不均匀性 三阶矩,定义了颜色分量的偏斜度,即颜色的不对称性 close all;clear all;clc; ...
- centos7下面装fastdfs
首先我完全是按照这个链接上面来的,其中也算有点小错https://blog.csdn.net/ty5546/article/details/79245648 环境准备编译环境 yum install ...
- IPv4 地址分类-for what
怎么分的:IPV4 地址分类 A B C D E 分来做什么:IP地址为什要分类?就是a类,b类,c类...? - wuxinliulei的回答 - 知乎