一、自己封装水波纹效果
src/main/main01.js
import * as THREE from "three";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";
import gsap from "gsap";
import * as dat from "dat.gui";
import vertexShader from "../shaders/water/vertex.glsl";
import fragmentShader from "../shaders/water/fragment.glsl";
// 目标:设置云烟雾效果
//创建gui对象
const gui = new dat.GUI();
// console.log(THREE);
// 初始化场景
const scene = new THREE.Scene();
// 创建透视相机
const camera = new THREE.PerspectiveCamera(
90,
window.innerHeight / window.innerHeight,
0.1,
1000
);
// 设置相机位置
// object3d具有position,属性是1个3维的向量
camera.position.set(0, 0, 2);
// 更新摄像头
camera.aspect = window.innerWidth / window.innerHeight;
// 更新摄像机的投影矩阵
camera.updateProjectionMatrix();
scene.add(camera);
// 加入辅助轴,帮助我们查看3维坐标轴
const axesHelper = new THREE.AxesHelper(5);
scene.add(axesHelper);
const params = {
uWaresFrequency: 14,
uScale: 0.03,
uXzScale: 1.5,
uNoiseFrequency: 10,
uNoiseScale: 1.5,
uLowColor: "#ff0000",
uHighColor: "#ffff00",
uXspeed: 1,
uZspeed: 1,
uNoiseSpeed: 1,
uOpacity: 1,
};
const shaderMaterial = new THREE.ShaderMaterial({
vertexShader: vertexShader,
fragmentShader: fragmentShader,
side: THREE.DoubleSide,
uniforms: {
uWaresFrequency: {
value: params.uWaresFrequency,
},
uScale: {
value: params.uScale,
},
uNoiseFrequency: {
value: params.uNoiseFrequency,
},
uNoiseScale: {
value: params.uNoiseScale,
},
uXzScale: {
value: params.uXzScale,
},
uTime: {
value: params.uTime,
},
uLowColor: {
value: new THREE.Color(params.uLowColor),
},
uHighColor: {
value: new THREE.Color(params.uHighColor),
},
uXspeed: {
value: params.uXspeed,
},
uZspeed: {
value: params.uZspeed,
},
uNoiseSpeed: {
value: params.uNoiseSpeed,
},
uOpacity: {
value: params.uOpacity,
},
},
transparent: true,
});
gui
.add(params, "uWaresFrequency")
.min(1)
.max(100)
.step(0.1)
.onChange((value) => {
shaderMaterial.uniforms.uWaresFrequency.value = value;
});
gui
.add(params, "uScale")
.min(0)
.max(0.2)
.step(0.001)
.onChange((value) => {
shaderMaterial.uniforms.uScale.value = value;
});
gui
.add(params, "uNoiseFrequency")
.min(1)
.max(100)
.step(0.1)
.onChange((value) => {
shaderMaterial.uniforms.uNoiseFrequency.value = value;
});
gui
.add(params, "uNoiseScale")
.min(0)
.max(5)
.step(0.001)
.onChange((value) => {
shaderMaterial.uniforms.uNoiseScale.value = value;
});
gui
.add(params, "uXzScale")
.min(0)
.max(5)
.step(0.1)
.onChange((value) => {
shaderMaterial.uniforms.uXzScale.value = value;
});
gui.addColor(params, "uLowColor").onFinishChange((value) => {
shaderMaterial.uniforms.uLowColor.value = new THREE.Color(value);
});
gui.addColor(params, "uHighColor").onFinishChange((value) => {
shaderMaterial.uniforms.uHighColor.value = new THREE.Color(value);
});
gui
.add(params, "uXspeed")
.min(0)
.max(5)
.step(0.001)
.onChange((value) => {
shaderMaterial.uniforms.uXspeed.value = value;
});
gui
.add(params, "uZspeed")
.min(0)
.max(5)
.step(0.001)
.onChange((value) => {
shaderMaterial.uniforms.uZspeed.value = value;
});
gui
.add(params, "uNoiseSpeed")
.min(0)
.max(5)
.step(0.001)
.onChange((value) => {
shaderMaterial.uniforms.uNoiseSpeed.value = value;
});
gui
.add(params, "uOpacity")
.min(0)
.max(1)
.step(0.01)
.onChange((value) => {
shaderMaterial.uniforms.uOpacity.value = value;
});
const plane = new THREE.Mesh(
new THREE.PlaneBufferGeometry(1, 1, 1024, 1024),
shaderMaterial
);
plane.rotation.x = -Math.PI / 2;
scene.add(plane);
// 初始化渲染器
const renderer = new THREE.WebGLRenderer({ alpha: true });
// 设置渲染尺寸大小
renderer.setSize(window.innerWidth, window.innerHeight);
// 监听屏幕大小改变的变化,设置渲染的尺寸
window.addEventListener("resize", () => {
// console.log("resize");
// 更新摄像头
camera.aspect = window.innerWidth / window.innerHeight;
// 更新摄像机的投影矩阵
camera.updateProjectionMatrix();
// 更新渲染器
renderer.setSize(window.innerWidth, window.innerHeight);
// 设置渲染器的像素比例
renderer.setPixelRatio(window.devicePixelRatio);
});
// 将渲染器添加到body
document.body.appendChild(renderer.domElement);
// 初始化控制器
const controls = new OrbitControls(camera, renderer.domElement);
// 设置控制器阻尼
controls.enableDamping = true;
const clock = new THREE.Clock();
function animate(t) {
const elapsedTime = clock.getElapsedTime();
shaderMaterial.uniforms.uTime.value = elapsedTime;
requestAnimationFrame(animate);
// 使用渲染器渲染相机看这个场景的内容渲染出来
renderer.render(scene, camera);
}
animate();
src/shaders/water/fragment.glsl
precision lowp float;
uniform vec3 uHighColor;
uniform vec3 uLowColor;
varying float vElevation;
uniform float uOpacity;
void main(){
float a = (vElevation+1.0)/2.0;
vec3 color = mix(uLowColor,uHighColor,a);
gl_FragColor = vec4(color,uOpacity);
}
src/shaders/water/vertex.glsl
precision lowp float;
uniform float uWaresFrequency;
uniform float uScale;
uniform float uNoiseFrequency;
uniform float uNoiseScale;
uniform float uXzScale;
uniform float uTime;
uniform float uXspeed;
uniform float uZspeed;
uniform float uNoiseSpeed;
// 计算出的高度传递给片元着色器
varying float vElevation;
// 随机函数
float random (vec2 st) {
return fract(sin(dot(st.xy,vec2(12.9898,78.233)))*43758.5453123);
}
// 旋转函数
vec2 rotate(vec2 uv, float rotation, vec2 mid)
{
return vec2(
cos(rotation) * (uv.x - mid.x) + sin(rotation) * (uv.y - mid.y) + mid.x,
cos(rotation) * (uv.y - mid.y) - sin(rotation) * (uv.x - mid.x) + mid.y
);
}
// 噪声函数
float noise (in vec2 _st) {
vec2 i = floor(_st);
vec2 f = fract(_st);
// Four corners in 2D of a tile
float a = random(i);
float b = random(i + vec2(1.0, 0.0));
float c = random(i + vec2(0.0, 1.0));
float d = random(i + vec2(1.0, 1.0));
vec2 u = f * f * (3.0 - 2.0 * f);
return mix(a, b, u.x) +
(c - a)* u.y * (1.0 - u.x) +
(d - b) * u.x * u.y;
}
// Classic Perlin 2D Noise
// by Stefan Gustavson
//
vec4 permute(vec4 x)
{
return mod(((x*34.0)+1.0)*x, 289.0);
}
vec2 fade(vec2 t)
{
return t*t*t*(t*(t*6.0-15.0)+10.0);
}
float cnoise(vec2 P)
{
vec4 Pi = floor(P.xyxy) + vec4(0.0, 0.0, 1.0, 1.0);
vec4 Pf = fract(P.xyxy) - vec4(0.0, 0.0, 1.0, 1.0);
Pi = mod(Pi, 289.0); // To avoid truncation effects in permutation
vec4 ix = Pi.xzxz;
vec4 iy = Pi.yyww;
vec4 fx = Pf.xzxz;
vec4 fy = Pf.yyww;
vec4 i = permute(permute(ix) + iy);
vec4 gx = 2.0 * fract(i * 0.0243902439) - 1.0; // 1/41 = 0.024...
vec4 gy = abs(gx) - 0.5;
vec4 tx = floor(gx + 0.5);
gx = gx - tx;
vec2 g00 = vec2(gx.x,gy.x);
vec2 g10 = vec2(gx.y,gy.y);
vec2 g01 = vec2(gx.z,gy.z);
vec2 g11 = vec2(gx.w,gy.w);
vec4 norm = 1.79284291400159 - 0.85373472095314 * vec4(dot(g00, g00), dot(g01, g01), dot(g10, g10), dot(g11, g11));
g00 *= norm.x;
g01 *= norm.y;
g10 *= norm.z;
g11 *= norm.w;
float n00 = dot(g00, vec2(fx.x, fy.x));
float n10 = dot(g10, vec2(fx.y, fy.y));
float n01 = dot(g01, vec2(fx.z, fy.z));
float n11 = dot(g11, vec2(fx.w, fy.w));
vec2 fade_xy = fade(Pf.xy);
vec2 n_x = mix(vec2(n00, n01), vec2(n10, n11), fade_xy.x);
float n_xy = mix(n_x.x, n_x.y, fade_xy.y);
return 2.3 * n_xy;
}
void main(){
vec4 modelPosition = modelMatrix * vec4(position,1.0);
float elevation = sin(modelPosition.x*uWaresFrequency+uTime*uXspeed)*sin(modelPosition.z*uWaresFrequency*uXzScale+uTime*uZspeed);
elevation += -abs(cnoise(vec2(modelPosition.xz*uNoiseFrequency+uTime*uNoiseSpeed))) *uNoiseScale;
vElevation = elevation;
elevation *= uScale;
modelPosition.y += elevation;
gl_Position = projectionMatrix * viewMatrix *modelPosition;
}
二、使用官方提供的water方法
src/main/main.js
import * as THREE from "three";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";
import gsap from "gsap";
import * as dat from "dat.gui";
import { RGBELoader } from "three/examples/jsm/loaders/RGBELoader";
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader";
// 导入water
import { Water } from "three/examples/jsm/objects/Water2";
// 目标:认识shader
//创建gui对象
const gui = new dat.GUI();
// console.log(THREE);
// 初始化场景
const scene = new THREE.Scene();
// 创建透视相机
const camera = new THREE.PerspectiveCamera(
90,
window.innerHeight / window.innerHeight,
0.1,
1000
);
// 设置相机位置
// object3d具有position,属性是1个3维的向量
camera.position.set(5, 5, 5);
// 更新摄像头
camera.aspect = window.innerWidth / window.innerHeight;
// 更新摄像机的投影矩阵
camera.updateProjectionMatrix();
scene.add(camera);
// 加入辅助轴,帮助我们查看3维坐标轴
const axesHelper = new THREE.AxesHelper(5);
scene.add(axesHelper);
// const water = new Water(new THREE.PlaneBufferGeometry(1, 1, 1024, 1024), {
// color: "#ffffff",
// scale: 1,
// flowDirection: new THREE.Vector2(1, 1),
// textureHeight: 1024,
// textureWidth: 1024,
// });
// water.rotation.x = -Math.PI / 2;
// scene.add(water);
// 加载场景背景
const rgbeLoader = new RGBELoader();
rgbeLoader.loadAsync("./assets/050.hdr").then((texture) => {
texture.mapping = THREE.EquirectangularReflectionMapping;
scene.background = texture;
scene.environment = texture;
});
// 加载浴缸
const gltfLoader = new GLTFLoader();
gltfLoader.load("./assets/model/yugang.glb", (gltf) => {
console.log(gltf);
const yugang = gltf.scene.children[0];
yugang.material.side = THREE.DoubleSide;
const waterGeometry = gltf.scene.children[1].geometry;
const water = new Water(waterGeometry, {
color: "#ffffff",
scale: 1,
flowDirection: new THREE.Vector2(1, 1),
textureHeight: 1024,
textureWidth: 1024,
});
scene.add(water);
scene.add(yugang);
});
const light = new THREE.AmbientLight(0xffffff); // soft white light
light.intensity = 10;
scene.add(light);
const directionalLight = new THREE.DirectionalLight(0xffffff, 0.5);
scene.add(directionalLight);
// 初始化渲染器
const renderer = new THREE.WebGLRenderer({ alpha: true, antialias: true });
renderer.outputEncoding = THREE.sRGBEncoding;
renderer.toneMapping = THREE.ACESFilmicToneMapping;
// 设置渲染尺寸大小
renderer.setSize(window.innerWidth, window.innerHeight);
// 监听屏幕大小改变的变化,设置渲染的尺寸
window.addEventListener("resize", () => {
// console.log("resize");
// 更新摄像头
camera.aspect = window.innerWidth / window.innerHeight;
// 更新摄像机的投影矩阵
camera.updateProjectionMatrix();
// 更新渲染器
renderer.setSize(window.innerWidth, window.innerHeight);
// 设置渲染器的像素比例
renderer.setPixelRatio(window.devicePixelRatio);
});
// 将渲染器添加到body
document.body.appendChild(renderer.domElement);
// 初始化控制器
const controls = new OrbitControls(camera, renderer.domElement);
// 设置控制器阻尼
controls.enableDamping = true;
const clock = new THREE.Clock();
function animate(t) {
const elapsedTime = clock.getElapsedTime();
requestAnimationFrame(animate);
// 使用渲染器渲染相机看这个场景的内容渲染出来
renderer.render(scene, camera);
}
animate();