2012-06-21 22 views
2

给定一个由三角形顶点组成的坚固的非纹理三维形状,以及用所有x,y,z点访问其顶点阵列,如何实现几个“磁场“来扭曲这种形状?说,每个磁场是一个随机的物体内或物体外位置x,y,z的点以及强度;如果它接近这个形状的顶点,它会将它拉向它,越靠近越强(也可以通过其强度值进行调整),从而导致平滑的颠簸和变化。用于顶点“磁变形”的3d算法

我现在正在使用ThreeJS(JavaScript/WebGL),所以在JavaScript,ThreeJS或伪代码中的帮助可能是完美的,但任何工作都可行!谢谢!

enter image description here

回答

1

你想要什么是隐性的表面或元球的行为。查看here(IMO)隐式曲面背后的基础知识。一旦你理解了这个概念,把它扩展到3D是很容易的。还要注意,你不必自己构建三角测量(因为你已经有了网格)。

+0

谢谢,这是一个良好的开端! metaballs的行为不会“合并”妨碍吗? –

+0

那么 - “磁铁”不会有任何几何形状,所以不用担心。并且,当网格中心的距离超过网格质心的设定距离时,可以通过限制变形来限制顶点拉伸效果。就像网格上的弹性极限。 – Ani

+0

谢谢,我会试试这个!在旁注中,ThreeJS中还有一个Metaball实现(http://ro.me/tech/metaball-playground)。 –

0

我对three.js一无所知,但我知道关于抛物线的东西。而且这似乎是你可以使用的东西。

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

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

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] 

顶点数次Mrange距离将只有最低限度的影响。 range内的顶点将被强烈吸引,但它们不会发疯。每个磁化顶点将位于M与其原始位置之间的线上,这将产生“褶皱”效应:磁化位置将在磁体附近变得更密集,这是以牺牲它们被吸引的区域为代价的。

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

+0

谢谢!不完全确定这在JavaScript中会是什么样子,但也许这是答案。 –

+0

如果你也可以看看我的尝试会很高兴http://stackoverflow.com/questions/11141139/3d-algorithm-for-magnetic-distortion-of-vertices/11145657#11145657 –

1

我得到了以下ThreeJS工作有些虽然它可能会在某些部分关闭,并且效果可能还没有看起来太令人信服,我不知道。该distortViaMagnets功能是问题的核心(完全跑步HTML and source here;运行demo here):

'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; 
} 
+0

为了使这个类似我的回答是,尝试用'1 /(1 + distance * distance /(magneticMaxValue * magneticMaxValue)')替换'magneticMaxValue/distance'。 – comingstorm