🙆‍♀️

Rust wgpuで球(Sphere)を描く

2023/05/31に公開

はじめに

wgpuになれるために立体を描こうと思った。まずはsphereから。
以下ソースコード。
https://github.com/Soyukke/wgpu-obj-example

Examplesの改変

公式examplesのcubeを参考にしています。

cubeでは面をlineで表示するものと、面上にテクスチャを表示する2つのdrawで構成されている。
このうち、lineで表示する部分だけを流用することにする。

基本的な動き

大雑把な流れは以下の通り。

  1. 頂点データをバッファにセットする。
  2. 面を構成する3頂点のindexの配列をバッファにセットする。
  3. drawする。

球の頂点の計算

半径r=1として、球上の頂点は角\theta、偏角\phiを使って以下で計算できる。

z = \sin \theta \\ x = \cos \theta \cos \phi \\ y = \cos \theta \sin \phi

ただし 0 <= \theta <= \pi0 <= \phi < 2\pi

シンプルに\theta, \phiをそれぞれN個に分割する。

インデックス(i, j)の頂点を計算する関数はこんな感じ。

fn vert(&self, i: i32, j: i32) -> Array1<f32> {
	let i = i as f32;
	let j = j as f32;
	let n = self.n as f32;
	let m = self.m as f32;
	let r = 1.;
	let xy = f32::sin(PI*i/n);
	let z = r * f32::cos(PI*i/n);
	let x = r * xy * f32::cos(2.*PI*j/m);
	let y = r * xy * f32::sin(2.*PI*j/m);
	let p = Array1::from_vec(vec![x, y, z]);
	p
}

球の部分的な面(四角形)を構成する頂点

まずは部分面を構成する4頂点ごとにまとめていく。
部分面を構成する4頂点のインデックスのセットを、反時計回りにならべると:
(i, j), (i+1, j), (i+1, j+1), (i, j+1)となる。

pub fn verts(&self) -> Vec<Vec<Array1<f32>>> {
let mut vvs:Vec<Vec<Array1<f32>>> = vec![];
for i in 0..self.n {
    for j in 0..self.m {
	let i2 = i+1 % self.n;
	let j2 = j+1 % self.m;
	let mut vs: Vec<Array1<f32>> = vec![];
	vs.push(self.vert(i, j));
	vs.push(self.vert(i2, j));
	vs.push(self.vert(i2, j2));
	vs.push(self.vert(i, j2));
	vvs.push(vs);
    }
}
vvs
}

四角形を2つの三角形に分割する。

wgpuでdrawするときは三角形の組み合わせで描画する。
4つの頂点のインデックスを0, 1, 2, 3として、それぞれ反時計回りで三角形になるように0, 1, 22, 3, 0に分ける。

pub fn indices0(&self) -> Vec<u16> {
let mut ids = vec![];
for i in 0..self.n {
    for j in 0..self.m {
	// i*m + jが進むごとに4進む
	let i = i as u16;
	let j = j as u16;
	let m = self.m as u16;
	let offset = (i*m + j)*4;
	// 0 1 2 / 2 3 0
	ids.push(0+offset);
	ids.push(1+offset);
	ids.push(2+offset);
	ids.push(2+offset);
	ids.push(3+offset);
	ids.push(0+offset);
    }
}
ids
}

描画

Discussion