如何使用Threejs使物体像浮力一样漂浮在水上

问题描述 投票:0回答:1

[我使用THREE.WATER创建了水效果,并向场景中添加了一些gltf对象,现在我正试图使添加的对象像小船一样漂浮在水上。

这里是附带的代码。

var scene, camera, renderer, ambient, directional, controls, stats, clock, imageLoader;
var terrain, water, mixer, model,modelL;

var lillyCoordPosition = [
    [3.5, -6.1, -18],
    [8.5, -6.1, -18],
    [13.3, -6.1, -18],
    [3.5, -8.1, -16],
    [8.5, -8.1, -16],
    [13.3, -8.1, -16],
    [3.5, -7.1, -17],
    [8.5, -7.1, -17],
    [13.3, -7.1, -17]
];

init();
animate();
resize();

function resize() {
    window.addEventListener("resize", function () {
        let width = window.innerWidth;
        let height = window.innerHeight;
        renderer.setSize(width, height);
        camera.aspect = width / height;
        camera.updateProjectionMatrix();
    });
}

function init() {
    clock = new THREE.Clock();

    imageLoader = new THREE.TextureLoader();
    scene = new THREE.Scene();

    camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 20000);
    camera.position.y = 6.5;
    camera.position.z = 2;

    ambient = new THREE.AmbientLight(0xe8eb34);
    scene.add(ambient);

    directional = new THREE.DirectionalLight(0xe8eb34, 1);
    directional.position.set(30, 80, 20);
    scene.add(directional);

    renderer = new THREE.WebGLRenderer({ antialias: true });
    renderer.setClearColor(0x70c8ff, 1);
    renderer.setPixelRatio(window.devicePixelRatio);
    renderer.setSize(window.innerWidth, window.innerHeight);

    //   controls = new THREE.OrbitControls( camera, renderer.domElement );    

    //   controls.target.set(0,120,-500);
    //   controls.update();

    document.body.appendChild(renderer.domElement);
    scene.fog = new THREE.FogExp2(0xEDC9AF, 0.003);

    loadWater();
    /*lillyCoordPosition.map( position => {
        loadLilly(position);
    })*/
}

function loadBar() {
    var texture = new THREE.Texture(generateTexture());
    texture.needsUpdate = true;

    var geometry = new THREE.PlaneGeometry(0.060, 10);
    var material = new THREE.MeshBasicMaterial({
        map: texture,
        // color: 0xffffff,
        side: THREE.DoubleSide
    });
    var plane = new THREE.Mesh(geometry, material);
    plane.position.set(1.500, 6.390, 0.000);
    plane.scale.set(1, 0.110, 1);

    //border
    var geo = new THREE.EdgesGeometry(plane.geometry);
    var mat = new THREE.LineBasicMaterial({ color: 0xffffff, linewidth: 4 });
    var wireframe = new THREE.LineSegments(geo, mat);
    wireframe.renderOrder = 1; // make sure wireframes are rendered 2nd
    plane.add(wireframe);

    scene.add(plane);

    //indicator
    var indicatorGeo = new THREE.PlaneGeometry(0.110, 0.300);
    var indMaterial = new THREE.MeshBasicMaterial({ color: 0xffffff, side: THREE.DoubleSide });
    var indicator = new THREE.Mesh(indicatorGeo, indMaterial);

    indicator.position.set(1.500, 6.000, 0.000);
    indicator.scale.set(1, 0.110, 1);

    scene.add(indicator);
}

function generateTexture() {
    var size = 25;

    canvas = document.createElement('canvas');
    canvas.width = size;
    canvas.height = size;

    var context = canvas.getContext('2d');

    var gradient = context.createLinearGradient(0, size, 0, 0);
    gradient.addColorStop(0, '#ff0000');
    gradient.addColorStop(1, '#00ff00');

    context.fillStyle = gradient;
    context.fillRect(0, 0, size, size);

    return canvas;
}

function loadLilly(coord){
    var loader = new THREE.GLTFLoader();
        loader.load('model/lillypad/scene.gltf', function(lilly){
            modelL = lilly.scene;
            modelL.scale.set(3,1.5,1.5);
            modelL.position.z = coord[2];
            modelL.position.y = coord[1];
            modelL.position.x = coord[0];
            // modelL.position.x = i*1.6;
            scene.add(modelL);
        });
}

function loadFrog() {
    var loader = new THREE.GLTFLoader();
    loader.load(
        'model/frog/scene.gltf', function (gltf) {
            //console.log(gltf.animations[0]); 
            model = gltf.scene;
            //console.log(model);
            model.scale.set(0.7, 0.7, 1.1);
            model.position.z = -30;
            scene.add(model);
            mixer = new THREE.AnimationMixer(model);
            mixer.clipAction(gltf.animations[0]).play();
        });
}

function loadPlant() {
    var loader = new THREE.GLTFLoader();
    loader.load('model/forest_2/scene.gltf', function (plant) {
        var modelP = plant.scene;
        modelP.scale.set(46, 40, 40);
        modelP.position.z = -80;
        modelP.position.y = 10;
        modelP.position.x = 4;
        modelP.rotation.y = 20;
        scene.add(modelP);
    });
}

function loadWater() {

    var geo = new THREE.PlaneBufferGeometry(15000, 15000, 10, 10);
    var c = directional.position.clone();

    var normal = imageLoader.load('https://www.titansoftime.com/textures/water/waternormals.jpg');

    normal.wrapS = THREE.RepeatWrapping;
    normal.wrapT = THREE.RepeatWrapping;
    water = new THREE.Water(geo, {
        textureWidth: 204,
        textureHeight: 204,
        waterNormals: normal,
        alpha: 0.01,
        fog: true,
        distortionScale: 10.0,
        sunDirection: c.normalize(),
        sunColor: 0x73a9ff,
        waterColor: 0x73a9ff,
        side: THREE.DoubleSide
    });

    water.rotation.x = - Math.PI * 0.5;

    water.matrixAutoUpdate = false;
    water.rotationAutoUpdate = false;
    water.updateMatrix();

    water.name = 'water';

    scene.add(water);

}

function animate() {
    requestAnimationFrame(animate);
    var delta = clock.getDelta();
    if (water) {
        water.material.uniforms.time.value += 0.5 * delta;
    }
    if (mixer != null) mixer.update(delta);

    renderer.render(scene, camera);
}
<body>
    <link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
    <style>
        body {
            overflow: hidden;
            /* background:url('https://www.publicdomainpictures.net/pictures/30000/velka/blue-sky-1330598792xLu.jpg'); */
        }

        canvas {
            width: 100vw;
            height: 100vh;
            position: absolute;
            left: 0;
        }

        #question {
            width: fit-content;
            height: 90px;
            background-color: rgba(0, 0, 0, 0.2);
            position: absolute;
            top: 50px;
            left: 30%;
            border-radius: 20px;
            color: white;
            text-align: center;
            z-index: 1;
            box-shadow: 0 10px 10px rgba(0, 1, 0, 0.3);
            padding: 8px;
        }

        .score,
        .pause {
            width: 90px;
            height: 90px;
            border-radius: 20px;
            background-color: rgba(0, 0, 0, 0.2);
            box-shadow: 0 10px 10px rgba(0, 1, 0, 0.3);
            position: absolute;
            top: 50px;
            z-index: 1;
            right: 10;
            text-align: center;
            color: white;
        }
    </style>
    <div id="question">
    </div>
    <span class="score">
        <h1>23</h1>
    </span>
    <script src="https://threejs.org/build/three.js"></script>
    <script src="https://cdn.rawgit.com/mrdoob/three.js/master/examples/js/loaders/GLTFLoader.js"></script>
    <script src="https://rawgit.com/mrdoob/three.js/master/examples/js/controls/OrbitControls.js"></script>
    <script src="https://rawgit.com/mrdoob/three.js/master/examples/js/objects/Water.js"></script>
    <script src="index.js"></script>
</body>

在上面的代码中,loadLilly()是将lillypads加载到水上的函数,但是我希望lillypads漂浮在水上。

lillypad对象:https://sketchfab.com/3d-models/lilypad-8c8285abc29f4d6cb5836f23037b741b

[请帮助我解决问题,我尝试使用obje.position.y,但没有用。

谢谢。

javascript three.js game-physics gltf
1个回答
0
投票

您可以一次加载该莉莉模型,然后在循环中重新使用其几何形状和材质,为每个莉莉创建一个新的网格。

出于示例目的,我只是在代码中创建了莉莉的几何图形和材质。

使用sin函数产生浮动效果,将其应用于y坐标。您可以使用相同的原理使莉莉丝在任何坐标轴上移动。

var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(60, innerWidth / innerHeight, 1, 100);
camera.position.set(0, 2.5, 5);
camera.lookAt(scene.position);
var renderer = new THREE.WebGLRenderer();
renderer.setSize(innerWidth, innerHeight);
document.body.appendChild(renderer.domElement);

var controls = new THREE.OrbitControls(camera, renderer.domElement);

var light = new THREE.DirectionalLight(0xffffff, 1);
light.position.setScalar(1);
scene.add(light);
scene.add(new THREE.AmbientLight(0xffffff, 0.5));

scene.add(new THREE.GridHelper(10, 100, "aqua", "aqua"));

// lilly geom 
let lCurve = new THREE.EllipseCurve(0, 0, 1, 1, 0, 1.9 * Math.PI);
let lPts = lCurve.getPoints(32);
lPts.push(new THREE.Vector2());
let lShape = new THREE.Shape(lPts);
let lGeom = new THREE.ExtrudeBufferGeometry(lShape, {
  depth: 0.25,
  bevelEnabled: false
});
lGeom.rotateX(-Math.PI * 0.5);
lGeom.translate(0, -0.125, 0);
let lMat = new THREE.MeshLambertMaterial({
  color: "green"
});

// lillies
var lillies = [];
var lillyCoordPosition = [
  [-2.5, -2.5],
  [0, -2.5],
  [2.5, -2.5],
  [-2.5, 0],
  [0, 0],
  [2.5, 0],
  [-2.5, 2.5],
  [0, 2.5],
  [2.5, 2.5]
].forEach(p => {
  let l = new THREE.Mesh(lGeom, lMat);
  l.position.set(p[0], 0, p[1]);
  l.rotation.y = Math.random() * Math.PI * 2; // random rotation
  let s = Math.random() * 0.1 + 0.9; // slightly different size of each one
  l.scale.set(s, 1, s);
  l.userData.initFloating = Math.random() * Math.PI * 2; // initial value for start of floating
  lillies.push(l);
  scene.add(l);
});

var clock = new THREE.Clock();

renderer.setAnimationLoop(al);

function al() {
  let t = clock.getElapsedTime();
  lillies.forEach(lil => {
    lil.position.y = Math.sin(lil.userData.initFloating + t) * 0.05;
  });
  renderer.render(scene, camera);
}
body {
  overflow: hidden;
  margin: 0;
}
<script src="https://threejs.org/build/three.min.js"></script>
<script src="https://threejs.org/examples/js/controls/OrbitControls.js"></script>
© www.soinside.com 2019 - 2024. All rights reserved.