three+webgl实现逼真微缩海水和帆船模拟交互三维动画代码
代码语言:html
所属分类:三维
代码描述:three+webgl实现逼真微缩海水和帆船模拟交互三维动画代码,可调节参数,可旋转,可鼠标拖动水面产生波纹,非常真实。
代码标签: three webgl 逼真 微缩 海水 帆船 模拟 交互 三维 动画 代码
下面为部分代码预览,完整代码请点击下载或在bfwstudio webide中打开
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1.0,maximum=1.0,minimum=1.0,user-scalable=0" />
<style>
* {
box-sizing: border-box;
}
html,
body {
margin: 0;
height: 100%;
overflow: hidden;
background: #05060a;
font-family:
ui-rounded,
"Segoe UI",
system-ui,
-apple-system,
sans-serif;
color: #e9edf6;
-webkit-tap-highlight-color: transparent;
}
#stage {
position: fixed;
inset: 0;
}
#stage canvas {
position: fixed;
inset: 0;
z-index: 0;
display: block;
width: 100%;
height: 100%;
touch-action: none;
cursor: grab;
}
#stage canvas:active {
cursor: grabbing;
}
/* ---- HUD ---- */
#hud {
position: fixed;
left: 22px;
top: 18px;
z-index: 3;
pointer-events: none;
text-shadow: 0 2px 18px rgba(0, 0, 0, 0.7);
}
#hud h1 {
margin: 0;
font-size: 23px;
font-weight: 700;
letter-spacing: 0.04em;
background: linear-gradient(180deg, #fff, #9fb8d8);
-webkit-background-clip: text;
background-clip: text;
color: transparent;
}
#hud p {
margin: 2px 0 0;
font-size: 12px;
letter-spacing: 0.22em;
text-transform: uppercase;
color: #7f8aa6;
}
#hint {
position: fixed;
right: 18px;
top: 20px;
z-index: 3;
pointer-events: none;
display: flex;
flex-direction: column;
align-items: flex-end;
gap: 6px;
}
#hint span {
font-size: 11px;
letter-spacing: 0.06em;
color: #aeb8d2;
background: rgba(20, 26, 44, 0.42);
border: 1px solid rgba(150, 180, 230, 0.14);
backdrop-filter: blur(8px);
-webkit-backdrop-filter: blur(8px);
padding: 5px 10px;
border-radius: 999px;
}
/* ---- control panel ---- */
#panel {
position: fixed;
left: 50%;
bottom: 20px;
transform: translateX(-50%);
z-index: 3;
display: flex;
gap: 18px;
align-items: center;
padding: 13px 18px;
border-radius: 16px;
background: rgba(14, 18, 32, 0.5);
border: 1px solid rgba(150, 180, 230, 0.16);
box-shadow:
0 12px 40px rgba(0, 0, 0, 0.45),
inset 0 1px 0 rgba(255, 255, 255, 0.05);
backdrop-filter: blur(14px);
-webkit-backdrop-filter: blur(14px);
}
.ctl {
display: flex;
flex-direction: column;
gap: 7px;
min-width: 150px;
}
.ctl span {
display: flex;
justify-content: space-between;
align-items: baseline;
font-size: 11px;
letter-spacing: 0.14em;
text-transform: uppercase;
color: #9fabc9;
}
.ctl i {
font-style: normal;
font-size: 10px;
color: #cfe0ff;
opacity: 0.8;
letter-spacing: 0.04em;
text-transform: none;
}
input[type="range"] {
-webkit-appearance: none;
appearance: none;
width: 100%;
height: 4px;
border-radius: 4px;
background: linear-gradient(90deg, #5b7cff, #7fd9ff);
outline: none;
cursor: pointer;
}
input[type="range"]::-webkit-slider-thumb {
-webkit-appearance: none;
width: 16px;
height: 16px;
border-radius: 50%;
background: #eaf2ff;
border: 2px solid #6f8dff;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.5);
cursor: grab;
}
input[type="range"]::-moz-range-thumb {
width: 16px;
height: 16px;
border-radius: 50%;
background: #eaf2ff;
border: 2px solid #6f8dff;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.5);
cursor: grab;
}
/* ---- loader ---- */
#loader {
position: fixed;
inset: 0;
z-index: 5;
display: flex;
gap: 14px;
align-items: center;
justify-content: center;
flex-direction: column;
background: radial-gradient(60% 60% at 50% 50%, #0a0d18, #05060b);
color: #8a96b4;
font-size: 13px;
letter-spacing: 0.2em;
text-transform: uppercase;
transition: opacity 0.6s ease;
}
#loader.hide {
opacity: 0;
pointer-events: none;
}
.spin {
width: 34px;
height: 34px;
border-radius: 50%;
border: 3px solid rgba(120, 150, 220, 0.18);
border-top-color: #7fd9ff;
animation: spin 1s linear infinite;
}
@keyframes spin {
to {
transform: rotate(360deg);
}
}
/* ---- sky knob (rotary dial: day → eclipse → night) ---- */
.knob-ctl {
display: flex;
flex-direction: column;
align-items: center;
gap: 7px;
}
.knob {
position: relative;
width: 44px;
height: 44px;
border-radius: 50%;
cursor: grab;
touch-action: none;
background: radial-gradient(circle at 50% 32%, #2b3655, #131a2b 70%);
border: 1px solid rgba(150, 180, 230, 0.22);
box-shadow:
0 5px 16px rgba(0, 0, 0, 0.5),
inset 0 1px 0 rgba(255, 255, 255, 0.1),
inset 0 -3px 8px rgba(0, 0, 0, 0.4);
}
.knob:active {
cursor: grabbing;
}
.knob-ind {
position: absolute;
left: 50%;
top: 5px;
width: 3px;
height: 13px;
margin-left: -1.5px;
border-radius: 2px;
background: linear-gradient(#ffe9a8, #ffbf4d);
box-shadow: 0 0 6px rgba(255, 200, 110, 0.7);
transform-origin: 50% 17px;
}
.knob-lbl {
display: flex;
gap: 6px;
font-size: 11px;
letter-spacing: 0.14em;
text-transform: uppercase;
color: #9fabc9;
}
.knob-lbl i {
font-style: normal;
font-size: 10px;
color: #cfe0ff;
opacity: 0.8;
letter-spacing: 0.04em;
text-transform: none;
}
/* ---- sound toggle (button is injected by script.js) ---- */
#sndBtn {
position: fixed;
right: 18px;
bottom: 20px;
z-index: 4;
width: 42px;
height: 42px;
border-radius: 12px;
border: 1px solid rgba(150, 180, 230, 0.18);
background: rgba(14, 18, 32, 0.5);
color: #dfe8ff;
font-size: 18px;
line-height: 1;
backdrop-filter: blur(14px);
-webkit-backdrop-filter: blur(14px);
box-shadow: 0 12px 40px rgba(0, 0, 0, 0.45);
cursor: pointer;
transition:
background 0.2s,
transform 0.1s;
}
#sndBtn:hover {
background: rgba(24, 30, 52, 0.6);
}
#sndBtn:active {
transform: scale(0.94);
}
#sndBtn.on {
background: rgba(46, 86, 150, 0.55);
border-color: rgba(127, 217, 255, 0.45);
}
@media (max-width: 560px) {
#hint {
display: none;
}
.ctl {
min-width: 120px;
}
#hud h1 {
font-size: 19px;
}
#sndBtn {
bottom: 78px;
}
}
</style>
</head>
<body>
<div id="stage">
<header id="hud">
<h1>Petri Dish</h1>
<p>a little sea you can rock</p>
</header>
<div id="hint">
<span>drag water to push</span>
<span>drag edge to orbit</span>
<span>spin the sky</span>
</div>
<div id="panel">
<label class="ctl">
<span>
Wind
<i id="vWind">breeze</i>
</span>
<input id="windStr" type="range" min="0" max="1000" value="460" />
</label>
<label class="ctl">
<span>
Direction
<i id="vDir">SE</i>
</span>
<input id="windDir" type="range" min="0" max="1000" value="95" />
</label>
<div class="knob-ctl">
<div id="skyKnob" class="knob" title="rotate: day → eclipse → night">
<i class="knob-ind"></i>
</div>
<span class="knob-lbl">
Sky
<i id="vSky">day</i>
</span>
</div>
</div>
<div id="loader">
<div class="spin"></div>
<span>filling the dish…</span>
</div>
</div>
<script>
(async () => {
"use strict";
let THREE;
try {
THREE =
await import("https://unpkg.com/three@0.160.0/build/three.module.js");
} catch (e) {
THREE =
await import("https://cdn.jsdelivr.net/npm/three@0.160.0/build/three.module.js");
}
const DISH_OUT = 4.95,
DISH_IN = 4.62,
RIM_Y = 1.95,
BASE_Y = 0.22;
const WATER_R = 4.55;
const WATER_Y = 1.0;
const SIM = 256;
const UV_R = 0.46;
const WALL_R = 0.476;
const CENTER = new THREE.Vector3(0, 0.85, 0);
const clamp = (x, a, b) => (x < a ? a : x > b ? b : x);
const lerp = (a, b, t) => a + (b - a) * t;
const norm2 = (x, z) => {
const l = Math.hypot(x, z) || 1;
return [x / l, z / l];
};
const COMMON = `
uniform vec3 uKeyDir, uKeyColor, uAmb;
vec3 aces(vec3 x){ return clamp((x*(2.51*x+0.03))/(x*(2.43*x+0.59)+0.14),0.0,1.0); }
float hash21(vec2 p){ return fract(sin(dot(p, vec2(127.1,311.7)))*43758.5453); }
float vnoise(vec2 p){
vec2 i=floor(p), f=fract(p); f=f*f*(3.0-2.0*f);
float a=hash21(i), b=hash21(i+vec2(1,0)), c=hash21(i+vec2(0,1)), d=hash21(i+vec2(1,1));
return mix(mix(a,b,f.x), mix(c,d,f.x), f.y);
}
float fbm(vec2 p){ float s=0.0,a=0.5; for(int i=0;i<4;i++){ s+=a*vnoise(p); p*=2.02; a*=0.5; } return s; }
vec3 envColor(vec3 dir){
float up = dir.y;
vec3 c = mix(vec3(0.012,0.018,0.03), vec3(0.07,0.10,0.15), smoothstep(-0.1,0.95,up));
c += vec3(0.16,0.19,0.26) * smoothstep(0.62,1.0,up) * 0.35;
float k = max(dot(normalize(dir), normalize(uKeyDir)), 0.0);
c += uKeyColor * (pow(k,500.0)*2.6 + pow(k,80.0)*0.35);
return c;
}
`;
const stage = document.getElementById("stage");
const renderer = new THREE.WebGLRenderer({
antialias: true,
alpha: false,
powerPreference: "high-performance"
});
const isCoarse = matchMedia && matchMedia("(pointer:coarse)").matches;
renderer.setPixelRatio(
Math.min(window.devicePixelRatio || 1, isCoarse ? 1.0 : 1.6)
);
renderer.setClearColor(0x05060a, 1);
renderer.outputColorSpace = THR.........完整代码请登录后点击上方下载按钮下载查看















网友评论0