概要
gliumはRustでOpenGLを扱うライブラリです。
しかし、Qiitaを探してみても、GUIはあれど肝心の3Dの表示サンプルがない...。
ということで、サンプルと、はまりどころをメモ程度に残しておきます。
完成品
以下のコードを実行すると、xyzの3軸、メッシュ、x=10, z=10 の直角三角形を y 方向に 1 ずらしたものが表示されます。
ソースコード
# [macro_use]
extern crate glium;
fn main() {
#![allow(unused_imports)]
use glium::{glutin, Surface};
#[derive(Copy, Clone)]
pub struct Vertex {
position: (f32, f32, f32)
}
implement_vertex!(Vertex, position);
let event_loop = glutin::event_loop::EventLoop::new();
let wb = glutin::window::WindowBuilder::new();
let cb = glutin::ContextBuilder::new().with_depth_buffer(24);
let display = glium::Display::new(wb, cb, &event_loop).unwrap();
const TRIANGLE_SHAPE: [Vertex;3] = [
Vertex { position: (0.0, 1.0, 10.0) },
Vertex { position: (10.0, 1.0, 0.0) },
Vertex { position: (0.0, 1.0, 0.0) },
];
let triangle_positions = glium::VertexBuffer::new(&display, &TRIANGLE_SHAPE).unwrap();
let triangle_indices = glium::index::NoIndices(glium::index::PrimitiveType::TrianglesList);
const AXIS_LEN:f32 = 20.0;
const LINE_X: [Vertex;2] = [
Vertex { position: (-AXIS_LEN, 0.0, 0.0) },
Vertex { position: (AXIS_LEN, 0.0, 0.0) },
];
let line_x = glium::VertexBuffer::new(&display, &LINE_X).unwrap();
const LINE_Y: [Vertex;2] = [
Vertex { position: (0.0, -AXIS_LEN, 0.0) },
Vertex { position: (0.0, AXIS_LEN, 0.0) },
];
let line_y = glium::VertexBuffer::new(&display, &LINE_Y).unwrap();
const LINE_Z: [Vertex;2] = [
Vertex { position: (0.0, 0.0, -AXIS_LEN) },
Vertex { position: (0.0, 0.0, AXIS_LEN) },
];
let line_z = glium::VertexBuffer::new(&display, &LINE_Z).unwrap();
let line_indices = glium::index::NoIndices(glium::index::PrimitiveType::LinesList);
const MESH_LINES: [Vertex; 80] = [
Vertex { position: (-10.0, 0.0, -10.0) },
Vertex { position: ( 10.0, 0.0, -10.0) },
Vertex { position: (-10.0, 0.0, -9.0) },
Vertex { position: ( 10.0, 0.0, -9.0) },
Vertex { position: (-10.0, 0.0, -8.0) },
Vertex { position: ( 10.0, 0.0, -8.0) },
Vertex { position: (-10.0, 0.0, -7.0) },
Vertex { position: ( 10.0, 0.0, -7.0) },
Vertex { position: (-10.0, 0.0, -6.0) },
Vertex { position: ( 10.0, 0.0, -6.0) },
Vertex { position: (-10.0, 0.0, -5.0) },
Vertex { position: ( 10.0, 0.0, -5.0) },
Vertex { position: (-10.0, 0.0, -4.0) },
Vertex { position: ( 10.0, 0.0, -4.0) },
Vertex { position: (-10.0, 0.0, -3.0) },
Vertex { position: ( 10.0, 0.0, -3.0) },
Vertex { position: (-10.0, 0.0, -2.0) },
Vertex { position: ( 10.0, 0.0, -2.0) },
Vertex { position: (-10.0, 0.0, -1.0) },
Vertex { position: ( 10.0, 0.0, -1.0) },
Vertex { position: (-10.0, 0.0, 10.0) },
Vertex { position: ( 10.0, 0.0, 10.0) },
Vertex { position: (-10.0, 0.0, 9.0) },
Vertex { position: ( 10.0, 0.0, 9.0) },
Vertex { position: (-10.0, 0.0, 8.0) },
Vertex { position: ( 10.0, 0.0, 8.0) },
Vertex { position: (-10.0, 0.0, 7.0) },
Vertex { position: ( 10.0, 0.0, 7.0) },
Vertex { position: (-10.0, 0.0, 6.0) },
Vertex { position: ( 10.0, 0.0, 6.0) },
Vertex { position: (-10.0, 0.0, 5.0) },
Vertex { position: ( 10.0, 0.0, 5.0) },
Vertex { position: (-10.0, 0.0, 4.0) },
Vertex { position: ( 10.0, 0.0, 4.0) },
Vertex { position: (-10.0, 0.0, 3.0) },
Vertex { position: ( 10.0, 0.0, 3.0) },
Vertex { position: (-10.0, 0.0, 2.0) },
Vertex { position: ( 10.0, 0.0, 2.0) },
Vertex { position: (-10.0, 0.0, 1.0) },
Vertex { position: ( 10.0, 0.0, 1.0) },
Vertex { position: (-10.0, 0.0, -10.0) },
Vertex { position: (-10.0, 0.0, 10.0) },
Vertex { position: ( -9.0, 0.0, -10.0) },
Vertex { position: ( -9.0, 0.0, 10.0) },
Vertex { position: ( -8.0, 0.0, -10.0) },
Vertex { position: ( -8.0, 0.0, 10.0) },
Vertex { position: ( -7.0, 0.0, -10.0) },
Vertex { position: ( -7.0, 0.0, 10.0) },
Vertex { position: ( -6.0, 0.0, -10.0) },
Vertex { position: ( -6.0, 0.0, 10.0) },
Vertex { position: ( -5.0, 0.0, -10.0) },
Vertex { position: ( -5.0, 0.0, 10.0) },
Vertex { position: ( -4.0, 0.0, -10.0) },
Vertex { position: ( -4.0, 0.0, 10.0) },
Vertex { position: ( -3.0, 0.0, -10.0) },
Vertex { position: ( -3.0, 0.0, 10.0) },
Vertex { position: ( -2.0, 0.0, -10.0) },
Vertex { position: ( -2.0, 0.0, 10.0) },
Vertex { position: ( -1.0, 0.0, -10.0) },
Vertex { position: ( -1.0, 0.0, 10.0) },
Vertex { position: ( 10.0, 0.0, -10.0) },
Vertex { position: ( 10.0, 0.0, 10.0) },
Vertex { position: ( 9.0, 0.0, -10.0) },
Vertex { position: ( 9.0, 0.0, 10.0) },
Vertex { position: ( 8.0, 0.0, -10.0) },
Vertex { position: ( 8.0, 0.0, 10.0) },
Vertex { position: ( 7.0, 0.0, -10.0) },
Vertex { position: ( 7.0, 0.0, 10.0) },
Vertex { position: ( 6.0, 0.0, -10.0) },
Vertex { position: ( 6.0, 0.0, 10.0) },
Vertex { position: ( 5.0, 0.0, -10.0) },
Vertex { position: ( 5.0, 0.0, 10.0) },
Vertex { position: ( 4.0, 0.0, -10.0) },
Vertex { position: ( 4.0, 0.0, 10.0) },
Vertex { position: ( 3.0, 0.0, -10.0) },
Vertex { position: ( 3.0, 0.0, 10.0) },
Vertex { position: ( 2.0, 0.0, -10.0) },
Vertex { position: ( 2.0, 0.0, 10.0) },
Vertex { position: ( 1.0, 0.0, -10.0) },
Vertex { position: ( 1.0, 0.0, 10.0) },
];
let mesh_lines = glium::VertexBuffer::new(&display, &MESH_LINES).unwrap();
let vertex_shader_src = r#"
#version 150
in vec3 position;
out vec3 col;
out vec3 v_position;
uniform vec3 color;
uniform mat4 perspective;
uniform mat4 view;
uniform mat4 model;
void main() {
mat4 modelview = view * model;
gl_Position = perspective * modelview * vec4(position, 1.0);
col = color;
}
"#;
let fragment_shader_src = r#"
#version 150
in vec3 col;
out vec4 color;
void main() {
color = vec4(col, 1);
}
"#;
let program = glium::Program::from_source(&display, vertex_shader_src, fragment_shader_src,
None).unwrap();
event_loop.run(move |event, _, control_flow| {
let next_frame_time = std::time::Instant::now() +
std::time::Duration::from_nanos(16_666_667);
*control_flow = glutin::event_loop::ControlFlow::WaitUntil(next_frame_time);
match event {
glutin::event::Event::WindowEvent { event, .. } => match event {
glutin::event::WindowEvent::CloseRequested => {
*control_flow = glutin::event_loop::ControlFlow::Exit;
return;
},
_ => return,
},
glutin::event::Event::NewEvents(cause) => match cause {
glutin::event::StartCause::ResumeTimeReached { .. } => (),
glutin::event::StartCause::Init => (),
_ => return,
},
_ => return,
}
let mut target = display.draw();
target.clear_color_and_depth((0.8, 0.8, 0.8, 1.0), 1.0);
let model = [
[1.0, 0.0, 0.0, 0.0],
[0.0, 1.0, 0.0, 0.0],
[0.0, 0.0, 1.0, 0.0],
[0.0, 0.0, 0.0, 1.0f32]
];
let view = view_matrix(&[10.0, 20.0, -20.0], &[-10.0, -20.0, 20.0], &[0.0, 1.0, 0.0]);
let perspective = {
let (width, height) = target.get_dimensions();
let aspect_ratio = height as f32 / width as f32;
let fov: f32 = 3.141592 / 4.0; //視野角45度
let zfar = 1024.0;
let znear = 0.1;
let f = 1.0 / (fov / 2.0).tan();
[
[f * aspect_ratio , 0.0, 0.0 , 0.0],
[ 0.0 , f , 0.0 , 0.0],
[ 0.0 , 0.0, (zfar+znear)/(zfar-znear) , 1.0],
[ 0.0 , 0.0, -(2.0*zfar*znear)/(zfar-znear), 0.0],
]
};
let light = [1.4, 0.4, -0.7f32];
let params = glium::DrawParameters {
depth: glium::Depth {
test: glium::draw_parameters::DepthTest::IfLess,
write: true,
.. Default::default()
},
.. Default::default()
};
target.draw(&triangle_positions, &triangle_indices, &program,
&uniform! { model: model, view: view, perspective: perspective, u_light: light, color: [1.0, 0.0, 0.0f32] },
¶ms).unwrap();
target.draw(&line_x, &line_indices, &program,
&uniform! { model: model, view: view, perspective: perspective, u_light: light, color: [1.0, 0.0, 0.0f32] },
¶ms).unwrap();
target.draw(&line_y, &line_indices, &program,
&uniform! { model: model, view: view, perspective: perspective, u_light: light, color: [0.0, 1.0, 0.0f32] },
¶ms).unwrap();
target.draw(&line_z, &line_indices, &program,
&uniform! { model: model, view: view, perspective: perspective, u_light: light, color: [0.0, 0.0, 1.0f32] },
¶ms).unwrap();
target.draw(&mesh_lines, &line_indices, &program,
&uniform! { model: model, view: view, perspective: perspective, u_light: light, color: [0.3, 0.3, 0.3f32] },
¶ms).unwrap();
target.finish().unwrap();
});
}
fn view_matrix(position: &[f32; 3], direction: &[f32; 3], up: &[f32; 3]) -> [[f32; 4]; 4] {
let f = {
let f = direction;
let len = f[0] * f[0] + f[1] * f[1] + f[2] * f[2];
let len = len.sqrt();
[f[0] / len, f[1] / len, f[2] / len]
};
let s = [up[1] * f[2] - up[2] * f[1],
up[2] * f[0] - up[0] * f[2],
up[0] * f[1] - up[1] * f[0]];
let s_norm = {
let len = s[0] * s[0] + s[1] * s[1] + s[2] * s[2];
let len = len.sqrt();
[s[0] / len, s[1] / len, s[2] / len]
};
let u = [f[1] * s_norm[2] - f[2] * s_norm[1],
f[2] * s_norm[0] - f[0] * s_norm[2],
f[0] * s_norm[1] - f[1] * s_norm[0]];
let p = [-position[0] * s_norm[0] - position[1] * s_norm[1] - position[2] * s_norm[2],
-position[0] * u[0] - position[1] * u[1] - position[2] * u[2],
-position[0] * f[0] - position[1] * f[1] - position[2] * f[2]];
[
[s_norm[0], u[0], f[0], 0.0],
[s_norm[1], u[1], f[1], 0.0],
[s_norm[2], u[2], f[2], 0.0],
[p[0], p[1], p[2], 1.0],
]
}
はまりどころ
はまった点は2つありました。
1つ目は、gliumはOpenGLなのにも関わらず 「左手系」 です。
(もしかしたら書き方次第でどうにかなるかもしれないです)
上記の例でもz軸の正方向が画面奥になっています。
少し勝手が違うので、注意する必要があります。
2つ目はVAOのサンプルが皆無ということです。
OpenGLで大量の点を扱うとなると、高速表示のためにもデータの受け渡しの効率化が必須です。
VAOは実装されているように見えるのですが、少し探してもサンプル等見当たらなかったので、本格的に使えるようになるまで、調べたりなんだりで結構かかるのではないかと思います。
上記の例でも、VAOで軸と色をまとめて渡したかったのですが、泣く泣くあきらめています。
最後に
gliumについてざっとしか調べていませんが、gliumのサンプル自体が少なかったのでメモ程度に残してみました。
メッシュの座標、どうにかならなかったんかい