おはようございます。先日次男の鼻水と咳が多かったので、大事をとって仕事を休んで、 1日次男と爆睡していた@kjunichiです。
はじめに
OCamlでシダを描画するをやった際に、OpenGLで任意の色を付けて点を打っていく事が出来ると知った。Rustでもシダの描画をしたくて、初めてすぐに、RustでもFFIが言語レベルでサポートされていることを知り、ではOpenGLが使えるかも?ということで、gl-rsやglfw-rsをすぐに見つけることが出来た。
やってみる
必要なもの
- Nightly版のrustがインストールされていること
- cargoがインストールされていること
cargoのnewコマンドで新規プロジェクトを作る
cargo new myogl cd myogl
指定したディレクトリ名でディレクトリが作られ、配下にCargo.tomlやら雛形が作られる。作成されたディレクトリへ移動しておく。
Cargo.tomlの編集
以下のように、glやglfwを依存関係に追加する。
[package] name = "myogl" version = "0.0.1" authors = ["@kjunichi"] [dependencies.gl] git = "https://github.com/bjz/gl-rs" [dependencies.glfw] git = "https://github.com/bjz/glfw-rs.git"
main.rsをsrcフォルダに作る
gl-rsのexamples/static_triangle.rsに頂点ごとに色を付けただけなので、コピーして編集するのが楽かも。
// Copyright 2013 The gl-rs developers. For a full listing of the authors, // refer to the AUTHORS file at the top-level directory of this distribution. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #![feature(globs)] extern crate gl; extern crate glfw; extern crate native; use gl::types::*; use glfw::Context; use std::mem; use std::ptr; use std::str; // Vertex data static VERTEX_DATA: [GLfloat, ..6] = [ 0.0, 0.5, 0.5, -0.5, -0.5, -0.5 ]; // Color data static COLOR_DATA: [GLfloat, ..9] = [ 1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0 ]; // Shader sources static VS_SRC: &'static str = "#version 150\n\ in vec2 position;\n\ in vec3 color;\n\ out vec3 Color;\n\ void main() {\n\ Color = color;\n\ gl_Position = vec4(position, 0.0, 1.0);\n\ }"; static FS_SRC: &'static str = "#version 150\n\ in vec3 Color;\n\ out vec4 out_color;\n\ void main() {\n\ out_color = vec4(Color, 1.0);\n\ }"; #[start] fn start(argc: int, argv: *const *const u8) -> int { native::start(argc, argv, main) } fn compile_shader(src: &str, ty: GLenum) -> GLuint { let shader = gl::CreateShader(ty); unsafe { // Attempt to compile the shader src.with_c_str(|ptr| gl::ShaderSource(shader, 1, &ptr, ptr::null())); gl::CompileShader(shader); // Get the compile status let mut status = gl::FALSE as GLint; gl::GetShaderiv(shader, gl::COMPILE_STATUS, &mut status); // Fail on error if status != (gl::TRUE as GLint) { let mut len = 0; gl::GetShaderiv(shader, gl::INFO_LOG_LENGTH, &mut len); let mut buf = Vec::from_elem(len as uint - 1, 0u8); // subtract 1 to skip the trailing null character gl::GetShaderInfoLog(shader, len, ptr::null_mut(), buf.as_mut_ptr() as *mut GLchar); fail!("{}", str::from_utf8(buf.as_slice()).expect("ShaderInfoLog not valid utf8")); } } shader } fn link_program(vs: GLuint, fs: GLuint) -> GLuint { let program = gl::CreateProgram(); gl::AttachShader(program, vs); gl::AttachShader(program, fs); gl::LinkProgram(program); unsafe { // Get the link status let mut status = gl::FALSE as GLint; gl::GetProgramiv(program, gl::LINK_STATUS, &mut status); // Fail on error if status != (gl::TRUE as GLint) { let mut len: GLint = 0; gl::GetProgramiv(program, gl::INFO_LOG_LENGTH, &mut len); let mut buf = Vec::from_elem(len as uint - 1, 0u8); // subtract 1 to skip the trailing null character gl::GetProgramInfoLog(program, len, ptr::null_mut(), buf.as_mut_ptr() as *mut GLchar); fail!("{}", str::from_utf8(buf.as_slice()).expect("ProgramInfoLog not valid utf8")); } } program } fn main() { let glfw = glfw::init(glfw::FAIL_ON_ERRORS).unwrap(); // Choose a GL profile that is compatible with OS X 10.7+ glfw.window_hint(glfw::ContextVersion(3, 2)); glfw.window_hint(glfw::OpenglForwardCompat(true)); glfw.window_hint(glfw::OpenglProfile(glfw::OpenGlCoreProfile)); let (window, _) = glfw.create_window(800, 600, "OpenGL", glfw::Windowed) .expect("Failed to create GLFW window."); // It is essential to make the context current before calling `gl::load_with`. window.make_current(); // Load the OpenGL function pointers gl::load_with(|s| window.get_proc_address(s)); // Create GLSL shaders let vs = compile_shader(VS_SRC, gl::VERTEX_SHADER); let fs = compile_shader(FS_SRC, gl::FRAGMENT_SHADER); let program = link_program(vs, fs); let mut vao = 0; let mut vbo : [GLuint,..2] = [0,0]; unsafe { // Create Vertex Array Object gl::GenVertexArrays(1, &mut vao); gl::BindVertexArray(vao); // Create a Vertex Buffer Object and copy the vertex data to it gl::GenBuffers(2, &mut vbo[0]); gl::BindBuffer(gl::ARRAY_BUFFER, vbo[0]); gl::BufferData(gl::ARRAY_BUFFER, (VERTEX_DATA.len() * mem::size_of::<GLfloat>()) as GLsizeiptr, mem::transmute(&VERTEX_DATA[0]), gl::STATIC_DRAW); let pos_attr = "position".with_c_str(|ptr| gl::GetAttribLocation(program, ptr)); gl::EnableVertexAttribArray(pos_attr as GLuint); gl::VertexAttribPointer(pos_attr as GLuint, 2, gl::FLOAT, gl::FALSE as GLboolean, 0, ptr::null()); gl::BindBuffer(gl::ARRAY_BUFFER, vbo[1]); gl::BufferData(gl::ARRAY_BUFFER, (COLOR_DATA.len() * mem::size_of::<GLfloat>()) as GLsizeiptr, mem::transmute(&COLOR_DATA[0]), gl::STATIC_DRAW); // Use shader program gl::UseProgram(program); "out_color".with_c_str(|ptr| gl::BindFragDataLocation(program, 0, ptr)); // Specify the layout of the vertex data let color_attr = "color".with_c_str(|ptr| gl::GetAttribLocation(program, ptr)); gl::EnableVertexAttribArray(color_attr as GLuint); gl::VertexAttribPointer(color_attr as GLuint, 3, gl::FLOAT, gl::FALSE as GLboolean, 0, ptr::null()); } while !window.should_close() { // Poll events glfw.poll_events(); // Clear the screen to black gl::ClearColor(0.3, 0.3, 0.3, 1.0); gl::Clear(gl::COLOR_BUFFER_BIT); // Draw a triangle from the 3 vertices gl::DrawArrays(gl::TRIANGLES, 0, 3); // Swap buffers window.swap_buffers(); } // Cleanup gl::DeleteProgram(program); gl::DeleteShader(fs); gl::DeleteShader(vs); unsafe { gl::DeleteBuffers(1, &vbo[0]); gl::DeleteVertexArrays(1, &vao); } }
ビルドする
cargo build
動かすには
./target/myogl
まとめ
Rustは開発中で、Nightlyのバイナリも頻繁に更新されている。ちょっと間をおくとこのサンプルもビルド出来なくなる可能性大だが、それはそれで、勉強になるかと思う。
関連記事
Rustつながり
- Rustでコマンドラインアプリでキーが押されたかの判定しつつ、別の処理も進めるには
- RustでWebカメラの映像をコマンドプロンプトに出すコマンドを作った
- RustでGoのライブラリを使うときのCargoのbuild.rsの書き方
- Surface Goのカメラ映像をRustでコマンドプロンプトに表示した
- Rustで共有ライブラリを作る際に必要だったCのconst char*をRustの&strにする方法