当前位置: 首页 > 知识库问答 >
问题:

具有three.js的3D模型之间的过渡顶点

芮瑾瑜
2023-03-14

我试图实现多边形吹扫和重组效果,类似于:

  • http://na.leagueoflegends.com/en/featured/skins/project-2016
  • https://tkmh.me/

在这两个例子中,你可以看到它们是如何将顶点从一个3d模型变形/过渡到另一个模型的,从而产生非常酷的效果。我有类似的工作,但我无法理解它们是如何通过速度偏移过渡顶点的(请参考第一个链接,看看粒子如何不简单地映射和轻松到新位置,而是使用一些角偏移来完成):

所以,我三次导入两个模型。html" target="_blank">js,取一个顶点数较大的顶点,复制其几何图形,同时将第二个顶点的模型数据附加为属性:

class CustomGeometry extends THREE.BufferGeometry {
  constructor (geometry1, geometry2) {
    super()

    let { count } = geometry1.attributes.position

    // this will hold
    let targetArr = new Float32Array(count * 3)
    let morphFactorArr = new Float32Array(count)

    for (let i = 0; i < count; i += 3) {
      targetArr[i + 0] = geometry2.attributes.position.array[i + 0] || 0
      targetArr[i + 1] = geometry2.attributes.position.array[i + 1] || 0
      targetArr[i + 2] = geometry2.attributes.position.array[i + 2] || 0

      let rand = Math.random()
      morphFactorArr[i + 0] = rand
      morphFactorArr[i + 1] = rand
      morphFactorArr[i + 2] = rand
    }
    this.addAttribute('a_target',      new THREE.BufferAttribute(targetArr, 3))
    this.addAttribute('a_morphFactor', new THREE.BufferAttribute(morphFactorArr, 1))
    this.addAttribute('position', geometry1.attributes.position)
  }
}

然后在我的着色器中,我可以简单地在它们之间进行如下转换:

vec3 new_position = mix(position, a_targetPosition, a_morphFactor);

这种方法行得通,但是非常枯燥和无聊。顶点只是从一个模型映射到另一个模型,没有任何偏移,没有重力或任何你想加入的东西。

此外,由于如果顶点数不匹配,我会将0附加到一个位置,因此未使用的位置只会缩放到vec4(0.0、0.0、0.0、1.0),这同样会导致一种枯燥乏味的效果(这里是兔子和大象模型之间的变形)

(请注意,未使用的兔子顶点如何简单地缩小到0)

一个人如何处理这样的问题?

另外,在传奇联盟的链接中,他们是如何做到的

>

  • 当模型在屏幕上处于活动状态时,在模型内部设置顶点动画

    将粒子映射到下一个模型时(单击箭头并过渡时),对粒子应用不同的速度和重力?

    是通过传递布尔属性吗?他们在改变目标位置阵列吗?任何帮助都将不胜感激

  • 共有1个答案

    田马鲁
    2023-03-14

    这很管用,但真的很无聊。顶点只是从一个模型映射到另一个模型,没有任何偏移,没有重力或任何你想加入混合的东西。

    因此,您可以应用任何可以想象和编码的效果。这不是您问题的确切答案,但这是最简单的激励示例,说明您可以使用着色器做什么。剧透:答案的末尾是一个工作示例的链接。

    让我们改变这个

    变成这个

    在转变过程中有有趣的粒子群

    我们将使用3。BoxBufferGeometry()具有一些自定义属性:

    var sideLenght = 10;
    var sideDivision = 50;
    var cubeGeom = new THREE.BoxBufferGeometry(sideLenght, sideLenght, sideLenght, sideDivision, sideDivision, sideDivision);
    var attrPhi = new Float32Array( cubeGeom.attributes.position.count );
    var attrTheta = new Float32Array( cubeGeom.attributes.position.count );
    var attrSpeed = new Float32Array( cubeGeom.attributes.position.count );
    var attrAmplitude = new Float32Array( cubeGeom.attributes.position.count );
    var attrFrequency = new Float32Array( cubeGeom.attributes.position.count );
    for (var attr = 0; attr < cubeGeom.attributes.position.count; attr++){
        attrPhi[attr] = Math.random() * Math.PI * 2;
      attrTheta[attr] = Math.random() * Math.PI * 2;
      attrSpeed[attr] = THREE.Math.randFloatSpread(6);  
      attrAmplitude[attr] = Math.random() * 5;
      attrFrequency[attr] = Math.random() * 5;
    }
    cubeGeom.addAttribute( 'phi', new THREE.BufferAttribute( attrPhi, 1 ) );
    cubeGeom.addAttribute( 'theta', new THREE.BufferAttribute( attrTheta, 1 ) );
    cubeGeom.addAttribute( 'speed', new THREE.BufferAttribute( attrSpeed, 1 ) );
    cubeGeom.addAttribute( 'amplitude', new THREE.BufferAttribute( attrAmplitude, 1 ) );
    cubeGeom.addAttribute( 'frequency', new THREE.BufferAttribute( attrFrequency, 1 ) );
    

    三个。ShaderMaterial():

    var vertexShader = [
    "uniform float interpolation;",
    "uniform float radius;",
    "uniform float time;",
    "attribute float phi;",
    "attribute float theta;",
    "attribute float speed;",
    "attribute float amplitude;",
    "attribute float frequency;",
    
    "vec3 rtp2xyz(){ // the magic is here",
    " float tmpTheta = theta + time * speed;",
    " float tmpPhi = phi + time * speed;",
    " float r = sin(time * frequency) * amplitude * sin(interpolation * 3.1415926);",
    " float x = sin(tmpTheta) * cos(tmpPhi) * r;",
    " float y = sin(tmpTheta) * sin(tmpPhi) * r;",
    " float z = cos(tmpPhi) * r;",
    " return vec3(x, y, z);",
    "}",
    
    "void main(){",
    " vec3 newPosition = mix(position, normalize(position) * radius, interpolation);",
    " newPosition += rtp2xyz();",
    "   vec4 mvPosition = modelViewMatrix * vec4( newPosition, 1.0 );",
    "   gl_PointSize = 1. * ( 1. / length( mvPosition.xyz ) );",
    "   gl_Position = projectionMatrix * mvPosition;",
    "}"
    ].join("\n");
    
    var fragmentShader = [
    "uniform vec3 color;",
    "void main(){",
    "   gl_FragColor = vec4( color, 1.0 );",
    "}"
    ].join("\n");
    
    var uniforms = {
        interpolation: { value: slider.value},
      radius: { value: 7.5},
      color: { value: new THREE.Color(0x00ff00)},
      time: { value: 0 }
    }
    
    var shaderMat = new THREE.ShaderMaterial({
        uniforms: uniforms,
        vertexShader: vertexShader,
      fragmentShader: fragmentShader,
      //wireframe: true //just in case, if you want to use THREE.Mesh() instead of THREE.Points()
    });
    

    如您所见,所有的魔法都发生在顶点着色器及其rtp2xyz()函数中。

    最后,动画功能的代码:

    var clock = new THREE.Clock();
    var timeVal = 0;
    
    render();
    function render(){
        timeVal += clock.getDelta();
        requestAnimationFrame(render);
      uniforms.time.value = timeVal;
      uniforms.interpolation.value = slider.value;
      renderer.render(scene, camera);
    }
    

    哦,是的,我们的页面中有一个滑动控件:

    <input id="slider" type="range" min="0" max="1" step="0.01" value="0.5" style="position:absolute;width:300px;">
    

    这里有一个片段

    var scene = new THREE.Scene();
    var camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 1, 1000);
    camera.position.set(10, 10, 20);
    var renderer = new THREE.WebGLRenderer({antialias: true});
    renderer.setSize(window.innerWidth, window.innerHeight);
    document.body.appendChild(renderer.domElement);
    
    var controls = new THREE.OrbitControls(camera, renderer.domElement);
    
    var vertexShader = [
    "uniform float interpolation;",
    "uniform float radius;",
    "uniform float time;",
    "attribute float phi;",
    "attribute float theta;",
    "attribute float speed;",
    "attribute float amplitude;",
    "attribute float frequency;",
    
    "vec3 rtp2xyz(){ // the magic is here",
    " float tmpTheta = theta + time * speed;",
    " float tmpPhi = phi + time * speed;",
    " float r = sin(time * frequency) * amplitude * sin(interpolation * 3.1415926);",
    " float x = sin(tmpTheta) * cos(tmpPhi) * r;",
    " float y = sin(tmpTheta) * sin(tmpPhi) * r;",
    " float z = cos(tmpPhi) * r;",
    " return vec3(x, y, z);",
    "}",
    
    "void main(){",
    " vec3 newPosition = mix(position, normalize(position) * radius, interpolation);",
    " newPosition += rtp2xyz();",
    "	vec4 mvPosition = modelViewMatrix * vec4( newPosition, 1.0 );",
    "	gl_PointSize = 1. * ( 1. / length( mvPosition.xyz ) );",
    "	gl_Position = projectionMatrix * mvPosition;",
    "}"
    ].join("\n");
    
    var fragmentShader = [
    "uniform vec3 color;",
    "void main(){",
    "	gl_FragColor = vec4( color, 1.0 );",
    "}"
    ].join("\n");
    
    var uniforms = {
    	interpolation: { value: slider.value},
      radius: { value: 7.5},
      color: { value: new THREE.Color(0x00ff00)},
      time: { value: 0 }
    }
    
    var sideLenght = 10;
    var sideDivision = 50;
    var cubeGeom = new THREE.BoxBufferGeometry(sideLenght, sideLenght, sideLenght, sideDivision, sideDivision, sideDivision);
    var attrPhi = new Float32Array( cubeGeom.attributes.position.count );
    var attrTheta = new Float32Array( cubeGeom.attributes.position.count );
    var attrSpeed = new Float32Array( cubeGeom.attributes.position.count );
    var attrAmplitude = new Float32Array( cubeGeom.attributes.position.count );
    var attrFrequency = new Float32Array( cubeGeom.attributes.position.count );
    for (var attr = 0; attr < cubeGeom.attributes.position.count; attr++){
    	attrPhi[attr] = Math.random() * Math.PI * 2;
      attrTheta[attr] = Math.random() * Math.PI * 2;
      attrSpeed[attr] = THREE.Math.randFloatSpread(6);	
      attrAmplitude[attr] = Math.random() * 5;
      attrFrequency[attr] = Math.random() * 5;
    }
    cubeGeom.addAttribute( 'phi', new THREE.BufferAttribute( attrPhi, 1 ) );
    cubeGeom.addAttribute( 'theta', new THREE.BufferAttribute( attrTheta, 1 ) );
    cubeGeom.addAttribute( 'speed', new THREE.BufferAttribute( attrSpeed, 1 ) );
    cubeGeom.addAttribute( 'amplitude', new THREE.BufferAttribute( attrAmplitude, 1 ) );
    cubeGeom.addAttribute( 'frequency', new THREE.BufferAttribute( attrFrequency, 1 ) );
    
    var shaderMat = new THREE.ShaderMaterial({
    	uniforms: uniforms,
    	vertexShader: vertexShader,
      fragmentShader: fragmentShader,
      //wireframe: true
    });
    var points = new THREE.Points(cubeGeom, shaderMat);
    scene.add(points);
    
    var clock = new THREE.Clock();
    var timeVal = 0;
    
    render();
    function render(){
    	timeVal += clock.getDelta();
    	requestAnimationFrame(render);
      uniforms.time.value = timeVal;
      uniforms.interpolation.value = slider.value;
      renderer.render(scene, camera);
    }
    body{
      margin: 0;
    }
    <script src="https://threejs.org/build/three.min.js"></script>
    <script src="https://threejs.org/examples/js/controls/OrbitControls.js"></script>
    <input id="slider" type="range" min="0" max="1" step="0.01" value="0.5" style="position:absolute;width:300px;">
     类似资料:
    • 本文向大家介绍three.js实现3D模型展示的示例代码,包括了three.js实现3D模型展示的示例代码的使用技巧和注意事项,需要的朋友参考一下 由于项目需要展示3d模型,所以对three做了点研究,分享出来 希望能帮到大家 先看看效果:   three.js整体来说 不是很难 只要你静下心来研究研究 很快就会上手的 首先我们在页面上需要创建一个能够放置3D模型的画布 也可以说是初始化 Thre

    • 请看两张截图: 上面的部分好像是用了一个ViewPager。ViewPager上的当前屏幕显示上一个和下一个屏幕的一部分。 问题:我们如何实现这个动画?i、 e.在哪里可以看到部分上一个和下一个屏幕??

    • 问题内容: 我想在两个组件之间进行动画处理,第一个组件淡出并从DOM中删除,然后再将下一个组件添加到DOM中并淡入。否则,新组件将添加到DOM中并占用空间。旧组件已删除。您可以在这个小提琴中看到问题: http://jsfiddle.net/phepyezx/4 对于我来说,不可接受的解决方案是在转换为新组件之前用CSS隐藏原始组件,如下所示: http://jsfiddle.net/phepye

    • A-Frame提供了加载glTF, OBJ, COLLADA模型的组件。我们推荐尽可能使用glTF,因为glTF被采纳为通过web传输3d模型的标准。我们可以编写任何three.js支持的文件格式的加载组件:three.js loader。我们也可以在社区中尝试查找组件,找到用来处理其他的格式(比如,PLY, FBX, JSON)的现成组件。 纯文本文件格式的模型包含顶点,UVs,纹理、材质和动画

    • 问题内容: 什么是CSS3过渡的区别,等等? 问题答案: CSS3的过渡和动画支持缓动,正式称为“定时功能”。常见的有,,,,和,或者您可以使用自己指定。 将缓慢开始动画,并以全速结束。 将以全速开始动画,然后缓慢完成。 将缓慢开始,在动画中间最快,然后缓慢完成。 类似于,不同之处在于它的开始时间比结束时间略快。 不使用任何宽松措施。 最后,这里对语法做了很好的描述,但是除非您需要一些非常精确的效

    • 问题内容: 我似乎找不到具有多个属性的CSS过渡 速记 的正确语法。这没有做任何事情: 我用javascript添加了show类。元素变得更高且可见,它只是不过渡。在最新的Chrome,FF和Safari中进行测试。 我究竟做错了什么? 编辑:为了清楚起见,我正在寻找简化我的CSS的简写版本。使用所有供应商前缀已经足够enough肿。还扩展了示例代码。 问题答案: 句法: 请注意,如果指定了延迟,