javascript - 使用本轮和傅立叶变换绘制/渲染 3D 对象 [动画]

标签 javascript python algorithm matlab 3d

首先注意:他们不会让我嵌入图像,直到我有更多的声誉点(抱歉),但所有链接都是发布在 imgur 上的图像! :) 谢谢

我复制了一种使用傅立叶变换为任何单个路径(1 个闭合路径)设置动画的方法。这会创建一个epicylces 的动画(旋转的圆圈),它们围绕彼此旋转,并跟随估算的点,将路径跟踪为一个连续的循环/函数。

我想将此系统采用到 3D。我能想到的两种实现此目的的方法是使用球坐标系(两个复平面)或 3 个本轮 --> 每个轴 (x,y,z) 及其各自的参数方程。可能是最好的开始方式!!

2 个循环,一个用于 X,一个用于 Y:img

图片:一个循环 --> 复数 --> 对于 X 和 Y img

傅里叶变换背景!!!:

• 欧拉公式允许我们将复平面中的每个点分解为一个 Angular (指数函数的参数)和一个振幅(Cn 系数)

• 从这个意义上讲,将上述无限级数中的每一项成像为代表半径为 cn 的圆上的一个点,偏移 2πnt/T 弧度,这有关联

• 下图显示了根据相位/振幅的复数之和如何可视化为复平面中的一组串联圆。每条红线都是一个向量,表示和序列中的一项:cne2πi(nT)t

• 添加被加数对应于在复数空间中简单地连接这些红色向量中的每一个:

动画旋转圆圈:img

动画绘图的圆圈:

• 如果您在 2D (x-y) 空间中绘制线条,则可以在数学上将此路径描述为参数函数。 (两个独立的单变量函数,均以辅助变量(本例中为 T)表示:

enter image description here

• 例如,下面是一匹马的简单线条图,以及通过图像中黑色像素的参数化路径,然后将该路径分成 X 和 Y 分量:

Horse Path

• 此时,我们需要计算这两条路径的傅里叶近似值,并使用来自该近似值的系数来确定最终可视化所需的圆圈的相位和振幅。

Python代码: 可以在此处找到用于此示例的 python 代码 guithub

我已经在 2D 中成功地制作了这个过程的动画,但我想将其采用到 3D 中。

以下代码表示 2D 动画 --> 我已经在做的事情:

[使用 JavaScript 和 P5.js 库]

傅立叶算法 (fourier.js):

//  a + bi
class Complex {
  constructor(a, b) {
    this.re = a;
    this.im = b;
  }

  add(c) {
    this.re += c.re;
    this.im += c.im;
  }

  mult(c) {
    const re = this.re * c.re - this.im * c.im;
    const im = this.re * c.im + this.im * c.re;
    return new Complex(re, im);
  }
}



function dft(x) {
  const X = [];
  const Values = [];
  const N = x.length;
  for (let k = 0; k < N; k++) {
    let sum = new Complex(0, 0);
    for (let n = 0; n < N; n++) {
      const phi = (TWO_PI * k * n) / N;
      const c = new Complex(cos(phi), -sin(phi));
      sum.add(x[n].mult(c));
    }
    sum.re = sum.re / N;
    sum.im = sum.im / N;

    let freq = k;
    let amp = sqrt(sum.re * sum.re + sum.im * sum.im);
    let phase = atan2(sum.im, sum.re);
    X[k] = { re: sum.re, im: sum.im, freq, amp, phase };
Values[k] = {phase};
  console.log(Values[k]);

  }
  return X;
}

素描功能/动画 (Sketch.js):

let x = [];
let fourierX;
let time = 0;
let path = [];

function setup() {
  createCanvas(800, 600);
  const skip = 1;
  for (let i = 0; i < drawing.length; i += skip) {
    const c = new Complex(drawing[i].x, drawing[i].y);
    x.push(c);
  }
  fourierX = dft(x);
  fourierX.sort((a, b) => b.amp - a.amp);
}

function epicycles(x, y, rotation, fourier) {
  for (let i = 0; i < fourier.length; i++) {
    let prevx = x;
    let prevy = y;
    let freq = fourier[i].freq;
    let radius = fourier[i].amp;
    let phase = fourier[i].phase;
    x += radius * cos(freq * time + phase + rotation);
    y += radius * sin(freq * time + phase + rotation);

    stroke(255, 100);
    noFill();
    ellipse(prevx, prevy, radius * 2);
    stroke(255);
    line(prevx, prevy, x, y);
  }
  return createVector(x, y);
}

function draw() {
  background(0);

  let v = epicycles(width / 2, height / 2, 0, fourierX);
  path.unshift(v);

  beginShape();
  noFill();
  for (let i = 0; i < path.length; i++) {
    vertex(path[i].x, path[i].y);
  }
  endShape();

  const dt = TWO_PI / fourierX.length;
  time += dt;

最重要的是!路径/坐标: (这个是三 Angular 形)

let drawing = [

  { y:  -8.001009734    , x:    -50 },
  { y:  -7.680969345    , x:    -49 },
  { y:  -7.360928956    , x:    -48 },
  { y:  -7.040888566    , x:    -47 },
  { y:  -6.720848177    , x:    -46 },
  { y:  -6.400807788    , x:    -45 },
  { y:  -6.080767398    , x:    -44 },
  { y:  -5.760727009    , x:    -43 },
  { y:  -5.440686619    , x:    -42 },
  { y:  -5.12064623 , x:    -41 },
  { y:  -4.800605841    , x:    -40 },
...
...

  { y:  -8.001009734    , x:    -47 },
  { y:  -8.001009734    , x:    -48 },
  { y:  -8.001009734    , x:    -49 },


];

最佳答案

这个答案是为了回应:“你认为 [three.js] 可以复制我在 2D 中所拥有的但在 3D 中的东西吗?用旋转的圆圈和东西?”

我不确定您是想从头开始学习 3D 建模(即,创建您自己的矢量例程库、齐次坐标变换、渲染透视等),还是只是想制作最终产品。就后者而言,three.js 是一个基于 webGL 构建的强大图形库,据我估计,它对于初学者来说足够简单,但它的深度足以产生非常复杂的 3D 效果。 (仔细阅读 https://threejs.org/examples/ 中的示例,您会亲眼看到。)

我碰巧在做我自己的 three.js 项目,并快速创建了一个周转圈示例作为热身练习。这涉及从以下引用资料中提取零件......

结果是一个简单的场景,一个圆圈围绕另一个圆圈运行,允许鼠标控件围绕场景旋转,从不同的 Angular 和距离查看它。

<html>
  <head>
    <title>Epicyclic Circles</title>
    <style>
      body { margin: 0; }
      canvas { width: 100%; height: 100% }
    </style>
  </head>
  <body>

    <script src="https://rawgit.com/mrdoob/three.js/dev/build/three.js"></script>
    <script src="https://rawgit.com/mrdoob/three.js/dev/examples/js/controls/OrbitControls.js"></script>

    <script>

      // Set up the basic scene, camera, and lights.
      
      var scene = new THREE.Scene();
      scene.background = new THREE.Color( 0xf0f0f0 );
      
      var camera = new THREE.PerspectiveCamera( 75, window.innerWidth/window.innerHeight, 0.1, 1000 );
      scene.add(camera)
      
      var light = new THREE.PointLight( 0xffffff, 0.8 );
      camera.add( light );
      
      camera.position.z = 50;
        
      var renderer = new THREE.WebGLRenderer();
      renderer.setSize( window.innerWidth, window.innerHeight );
      document.body.appendChild( renderer.domElement );
      
      // Add the orbit controls to permit viewing the scene from different angles via the mouse.
      
      controls = new THREE.OrbitControls( camera, renderer.domElement );
      controls.enableDamping = true; // an animation loop is required when either damping or auto-rotation are enabled
      controls.dampingFactor = 0.25;
      controls.screenSpacePanning = false;
      controls.minDistance = 0;
      controls.maxDistance = 500;
      
      // Create center and epicyclic circles, extruding them to give them some depth.
      
      var extrudeSettings = { depth: 2, bevelEnabled: true, bevelSegments: 2, steps: 2, bevelSize: .25, bevelThickness: .25 };

      var arcShape1 = new THREE.Shape();
      arcShape1.moveTo( 0, 0 );
      arcShape1.absarc( 0, 0, 15, 0, Math.PI * 2, false );
      var holePath1 = new THREE.Path();
      holePath1.moveTo( 0, 10 );
      holePath1.absarc( 0, 10, 2, 0, Math.PI * 2, true );
      arcShape1.holes.push( holePath1 );
        
      var geometry1 = new THREE.ExtrudeBufferGeometry( arcShape1, extrudeSettings );
      var mesh1 = new THREE.Mesh( geometry1, new THREE.MeshPhongMaterial( { color: 0x804000 } ) );
      scene.add( mesh1 );
      
      var arcShape2 = new THREE.Shape();
      arcShape2.moveTo( 0, 0 );
      arcShape2.absarc( 0, 0, 15, 0, Math.PI * 2, false );
      var holePath2 = new THREE.Path();
      holePath2.moveTo( 0, 10 );
      holePath2.absarc( 0, 10, 2, 0, Math.PI * 2, true );
      arcShape2.holes.push( holePath2 );
          
      var geometry2 = new THREE.ExtrudeGeometry( arcShape2, extrudeSettings );
      var mesh2 = new THREE.Mesh( geometry2, new THREE.MeshPhongMaterial( { color: 0x00ff00 } ) );
      scene.add( mesh2 );

      // Define variables to hold the current epicyclic radius and current angle.
      var mesh2AxisRadius = 30;
      var mesh2AxisAngle = 0;

      var animate = function () {
        requestAnimationFrame( animate );

        // During each animation frame, let's rotate the objects on their center axis,  
        // and also set the position of the epicyclic circle.
        
        mesh1.rotation.z -= 0.02;

        mesh2.rotation.z += 0.02;
        mesh2AxisAngle += 0.01;
        mesh2.position.set ( mesh2AxisRadius * Math.cos(mesh2AxisAngle), mesh2AxisRadius * Math.sin(mesh2AxisAngle), 0 );

        renderer.render( scene, camera );
      };

      animate();
    </script>
  </body>
</html>

请注意,我在 animate 函数中使用了基本三 Angular 函数来围绕中心圆定位周转圆,并伪造了圆的旋转速率(而不是进行精确的数学计算),但可能有更好的“three.js”——通过矩阵或内置函数来做到这一点的方法。鉴于您显然具有很强的数学背景,我认为您在移植到 3D 时使用基本三 Angular 法转换多周转圆的 2D 模型不会有任何问题。

希望这有助于您决定如何继续制作 3D 版本的程序。

关于javascript - 使用本轮和傅立叶变换绘制/渲染 3D 对象 [动画],我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57117182/

相关文章:

javascript - 列出未使用 .val 函数显示的字段内容

c++ - 给定纬度/经度对 vector ,我如何创建一个 vector ,使点之间的距离小于或等于某个常数?

javascript - 制作一个可以使用 top 属性进行动画切换的菜单

javascript - img onClick 不起作用

python - 在 Python 中使用 FFprobe

Python 程序意外终止

algorithm - 空间聚类算法

c++ - 时间测量在 C++ 中不起作用

javascript - 是什么让这段代码一遍又一遍地执行

python - 如何使用 Django 和 South 创建父类(super class)(针对现有模型)