0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

3DCGのBend機能を実装する

Last updated at Posted at 2025-03-03

loop2.gif

はじめに

3DCGにおいて、変形させる機能をdeformerと呼んだりします。その中で、「曲げ」Bendツールをプログラムした話です。chatGPT利用。

image.png

環境

Processing 4

アルゴリズム

点P(x,y)をbend関数でθ曲げることを考えます。

Bend変形のカギとなるのは、x座標を円周上の角度にマッピングすることです。

Bend適用前のグリッド全体の長さをL、bend角度をθとしたとき、円弧上での対応する角度θxは次式であらわすことができます。

\theta_x=\frac{\theta}{L}x

次に、yを円弧の半径へマッピングします。円の基準半径をRとし、点Pの半径をrとし次式を定義します。

r=R+y

bend変形後の座標をP(x',y')とすると、

x'=r\sin(\theta_x)
y'=R-r\cos(\theta_x)

となる。
このとき、

\sin(\theta_x)は円周方向の移動を表し、
\cos(\theta_x)は円の高さの変化を表す。

最終的な変換式は、以下となる。

x'=(R+y)\sin(\frac{\theta}{L}x)
y'=R-(R+y)\cos(\frac{\theta}{L}x)

プログラム

int gridWidth = 32;  // X方向の点の数
int gridHeight = 6; // Y方向の点の数
float cellSize = 20; // 格子のサイズ
float length = gridWidth * cellSize;  // グリッドの全長
PVector[][] originalGrid = new PVector[gridWidth+1][gridHeight+1];
PVector[][] bentGrid = new PVector[gridWidth+1][gridHeight+1];
float bendAngle;  // 曲げ角度(度)

void setup() {
  size(600, 600);
  generateGrid();
}

void draw() {
  background(255);
  translate(width / 2, height / 2);  // 画面中央に配置

  // ベンド処理
  applyBend(bendAngle);

  // 曲げた後のグリッドを描画(黒)
  stroke(0);
  drawGrid(bentGrid);

  // 角度を変化させてアニメーション
  bendAngle = 360*sin(frameCount*0.01);

}

// **長方形のグリッドを作成**
void generateGrid() {
  for (int x = 0; x <= gridWidth; x++) {
    for (int y = 0; y <= gridHeight; y++) {
      float posX = (x - gridWidth / 2) * cellSize;   // 中心を0にする
      float posY = (y - gridHeight / 2) * cellSize;  // 中心を0にする
      originalGrid[x][y] = new PVector(posX, posY);
    }
  }
}

// **Bend処理**
void applyBend(float angle) {
  float theta = radians(angle);
  float R = (theta != 0) ? length / theta : Float.POSITIVE_INFINITY;  // 曲げる半径

  for (int x = 0; x <= gridWidth; x++) {
    for (int y = 0; y <= gridHeight; y++) {
      PVector p = originalGrid[x][y];

      // **X軸方向の回転角(円周上の位置)**
      float localAngle = p.x*theta/length;

      // **Y軸方向の半径変化(各層が均等にカーブする)**
      float r = R + p.y;  // 層ごとに異なる半径

      // **円弧上に変換**
      float newX = r * sin(localAngle);
      float newY = R - r * cos(localAngle);  // 円の中心基準に配置

      bentGrid[x][y] = new PVector(newX, newY);
    }
  }
}

// **グリッドを描画**
void drawGrid(PVector[][] grid) {
  for (int x = 0; x <= gridWidth; x++) {
    for (int y = 0; y <= gridHeight; y++) {
      PVector p = grid[x][y];
      ellipse(p.x, p.y, 3, 3);

      if (x > 0) {
        PVector prevX = grid[x - 1][y];
        line(prevX.x, prevX.y, p.x, p.y);
      }
      if (y > 0) {
        PVector prevY = grid[x][y - 1];
        line(prevY.x, prevY.y, p.x, p.y);
      }
    }
  }
}

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?