2

给定一个由三角形顶点组成的实体、无纹理的 3D 形状,并使用所有 x、y、z 点访问其顶点数组,如何实现多个“磁场”来扭曲该形状?比如说,每个磁场都是一个随机的物体内部或外部位置 x、y、z 的点,以及一个强度;如果它靠近这种形状的顶点,它会将它“拉”向它,越靠近越强(也通过它的强度值进行调整)......导致平滑的颠簸和移动。

我目前正在使用 ThreeJS (JavaScript/WebGL),所以对 JavaScript、ThreeJS 或伪代码的帮助可能是完美的,但任何东西都可以!谢谢!

在此处输入图像描述

4

4 回答 4

1

如果您不想转换为隐式曲面(如@ananthonline 所述),您可以根据“磁铁”强度和位置调整所有顶点。请注意,以下不会创建任何新顶点,只会吸引现有顶点;这可能是优点或缺点,具体取决于您的应用程序。

给定一个磁铁位置M,您可以将每个顶点调整V到一个新位置V',如下所示:

point V =     [incoming vertex]
point M =     [magnet location]
float range = [chosen nominal range for the magnetic effect]

float range2 = range * range  [range squared, for comparison with squared distance]
vector MV = V - M             [vector from M to V]
float alpha = range2 / (range2 + dot(MV, MV))  [weighting factor]
point V' =  alpha * M + (1-alpha) * V          [new, "magnetized" position]

range距离数倍的顶点M只会受到最小的影响。内部的顶点range会被强烈吸引,但不会发疯。每个磁化顶点将位于其原始位置之间的线上M,这将产生“褶皱”效应:磁化位置在磁铁附近会更密集,但会牺牲它们被吸引的区域。

如果您不喜欢磁效应的确切形状(例如,如果您想确保超出一定距离的顶点根本不移动),您可以调整alpha. 只要确保alpha不超过1,并0随着dot(MV, MV)变大而接近。

于 2012-06-21T19:22:56.610 回答
1

您想要的是隐式曲面或类似元球的行为。在这里寻找(IMO)对隐式表面背后基础知识的非常好的解释。一旦理解了这个概念,就很容易将其扩展到 3D。另请注意,您不必自己构建三角剖分(因为您已经有一个网格)。

于 2012-06-21T15:45:32.830 回答
0

我对three.js一无所知,但我确实了解抛物线。这似乎是你可以使用的东西。

这似乎不是一个答案,但请看这个视频:http ://www.youtube.com/watch?v=8HvZHo-LSvQ 。该视频向您展示了如何绘制抛物线。在您的情况下,焦点是对象内部的一个点(strength of magnet = depth of focus point),轴的原点是磁铁,y 轴将是顶点的法线。

不幸的是,执行这种方法会给你带来切边,你肯定需要某种正弦曲线,但我希望这能给你一个如何继续前进的提示。我会进一步寻找你,但这是我的第一个直觉。

于 2012-06-21T16:02:16.503 回答
0

我让下面的 ThreeJS 工作了一些,尽管它在某些部分可能会关闭,而且效果可能看起来还不太令人信服,我不确定。distortViaMagnets 函数是问题的核心(此处为完整运行的HTML 和源代码;此处运行演示):

'use strict';
var app = null;

window.onload = function() {
    app = new App();
    app.init();
}

App.prototype.init = function() {
    this.scene = new THREE.Scene();
    this.addCamera();
    this.addLights();
    this.addSphere();
    this.addRenderer();
    this.render();
    app.animate();
};

App.prototype.distortViaMagnets = function() {
    var maxMagnets = this.magnets.length, maxVertices = this.sphere.geometry.vertices.length;
    var magneticMaxValue = this.getDistance3d( {x: 0, y: 0, z: 0}, {x: 1000, y: 1000, z: 1000} );
    var strength = 1000, factor = 3;
    for (var i = 0; i < maxMagnets; i++) {
        for (var vertexI = 0; vertexI < maxVertices; vertexI++) {
            var magnet = this.magnets[i];
            var vertex = this.sphere.geometry.vertices[vertexI];

            var distance = this.getDistance3d(magnet, vertex);
            var power = magneticMaxValue / distance / strength;

            vertex.x += ( (magnet.x - vertex.x) * power ) * factor;
            vertex.y += ( (magnet.y - vertex.y) * power ) * factor;
            vertex.z += ( (magnet.z - vertex.z) * power ) * factor;
        }
    }
}

App.prototype.animate = function() {
    app.mainGroup.rotation.y -= .003;

    requestAnimationFrame(app.animate);
    app.renderer.render(app.scene, app.camera);
};

App.prototype.addCamera = function() {
    this.camera = new THREE.CombinedCamera(this.width, this.height, 45, 1, 10000);
    this.camera.position.set(0, 0, 400 - 200);
    this.camera.lookAt( new THREE.Vector3(0, 0, 0) );
    this.scene.add(this.camera);
}

App.prototype.addSphere = function() {
    this.mainGroup = new THREE.Object3D();

    var radius = 50, segments = 30 * 3, rings = 30 * 3;
    var geometry = new THREE.SphereGeometry(radius, segments, rings);
    geometry.dynamic = true;

    var material = new THREE.MeshPhongMaterial( {color: 0xffffff, opacity: 1} );
    this.sphere = new THREE.Mesh(geometry, material);
    this.sphere.dynamic = true;
    this.sphere.position.set(0, 0, 0);
    this.sphere.doubleSided = true;

    this.addMagnets();
    this.distortViaMagnets();

    this.mainGroup.add(this.sphere);

    this.scene.add(this.mainGroup);
}

App.prototype.addMagnets = function(vertex) {
    var max = this.sphere.geometry.vertices.length, maxMagnets = 1;
    for (var i = 1; i <= 2; i++) {
        var index = Misc.getRandomInt(0, max - 1);
        var vertex = app.sphere.geometry.vertices[index];
        var magnetI = this.magnets.length;
        this.magnets[magnetI] = this.distortVertex( {x: vertex.x, y: vertex.y, z: vertex.z}, 10 );
        this.showMagnet(this.magnets[magnetI]);
    }

    var magnetI = 0;
    this.magnets[magnetI] = {x: 58, y: 0, z: 0};
    this.showMagnet(this.magnets[magnetI]);
}

App.prototype.getDistance3d = function(vertex1, vertex2) {
    var xfactor = vertex2.x - vertex1.x;
    var yfactor = vertex2.y - vertex1.y;
    var zfactor = vertex2.z - vertex1.z;
    return Math.sqrt( (xfactor*xfactor) + (yfactor*yfactor) + (zfactor*zfactor) );
}

App.prototype.showMagnet = function(vertex) {
    var radius = 1.5, segments = 10, rings = 10;
    var geometry = new THREE.SphereGeometry(radius, segments, rings);

    var material = new THREE.MeshPhongMaterial( {color: 0x11ee33, opacity: .6} );
    var sphere = new THREE.Mesh(geometry, material);
    sphere.position.set(vertex.x, vertex.y, vertex.z);

    this.mainGroup.add(sphere);
}

App.prototype.distortVertex = function(vertex, distortion) {
    vertex.x = Misc.distort(vertex.x, distortion);
    vertex.y = Misc.distort(vertex.y, distortion);
    vertex.z = Misc.distort(vertex.z, distortion);
    return vertex;
}

App.prototype.addRenderer = function() {
    this.renderer = new THREE.WebGLRenderer( {antialias: true} );
    this.renderer.setSize(this.width, this.height);
    var elmMain = document.getElementById('main');
    elmMain.appendChild(this.renderer.domElement);
}

App.prototype.render = function() {
    this.renderer.render(this.scene, this.camera);
};

App.prototype.addLights = function() {
    var light = new THREE.DirectionalLight(0xffffff, 1);
    light.position.set(-50, 250, 250);
    this.scene.add(light);
};

function App() {
    this.width = window.innerWidth;
    this.height = window.innerHeight;
    this.camera = null;
    this.sphere = null;
    this.controls = null;
    this.mainGroup = null;
    this.renderer = null;
    this.scene = null;
    this.magnets = [];
    this.debugElm = null;
}
于 2012-06-21T19:46:34.933 回答