やっはろー。これは、Rust Advent Calendar 2014 の 7日目の記事です。Rust から OpenGL を叩いてみたかったので、シダを描くことにしました。
元ネタです。
// rustc 0.13.0-nightly (d9c7c00b9 2014-12-04 21:33:07 +0000)
extern crate gl;
extern crate glfw;
use std::{mem, ptr, rand};
use gl::types::{GLboolean, GLfloat, GLsizeiptr};
use glfw::Context;
const WIDTH: uint = 512;
const HEIGHT: uint = 512;
static VERTEX_SOURCE: &'static str = "
#version 150
in vec2 position;
out vec2 texcoord;
void main() {
texcoord = (vec2(position.x, -1.0 * position.y) + 1.0) / 2.0;
gl_Position = vec4(position, 0.0, 1.0);
}";
static FRAGMENT_SOURCE: &'static str = "
#version 150
uniform sampler2D tex;
in vec2 texcoord;
out vec4 color;
void main() {
if (0 < texture(tex, texcoord).r) {
color = vec4(0.125, 0.502, 0.125, 1.0);
} else {
color = vec4(0.0);
}
}";
fn main() {
let image = {
static mut buffer: [u8, ..WIDTH * HEIGHT] = [0, ..WIDTH * HEIGHT];
fn fern(n: uint, x: f64, y: f64) {
fn w1x(x: f64, y: f64) -> f64 { 0.836 * x + 0.044 * y }
fn w1y(x: f64, y: f64) -> f64 { -0.044 * x + 0.836 * y + 0.169 }
fn w2x(x: f64, y: f64) -> f64 { -0.141 * x + 0.302 * y }
fn w2y(x: f64, y: f64) -> f64 { 0.302 * x + 0.141 * y + 0.127 }
fn w3x(x: f64, y: f64) -> f64 { 0.141 * x - 0.302 * y }
fn w3y(x: f64, y: f64) -> f64 { 0.302 * x + 0.141 * y + 0.169 }
fn w4x(_: f64, _: f64) -> f64 { 0.0 }
fn w4y(_: f64, y: f64) -> f64 { 0.175337 * y }
if 0 < n {
fern(n - 1, w1x(x, y), w1y(x, y));
if rand::random::<f64>() < 0.3 { fern(n - 1, w2x(x, y), w2y(x, y)); }
if rand::random::<f64>() < 0.3 { fern(n - 1, w3x(x, y), w3y(x, y)); }
if rand::random::<f64>() < 0.3 { fern(n - 1, w4x(x, y), w4y(x, y)); }
} else {
let x = (x * WIDTH as f64 + WIDTH as f64 * 0.5) as uint;
let y = (HEIGHT as f64 - y * HEIGHT as f64) as uint;
unsafe { buffer[x + y * HEIGHT] = 1; }
}
}
fern(20, 0.0, 0.0);
unsafe { buffer.to_vec() }
};
unsafe {
let glfw = glfw::init(glfw::FAIL_ON_ERRORS).unwrap();
glfw.window_hint(glfw::WindowHint::ContextVersion(3, 2));
glfw.window_hint(glfw::WindowHint::OpenglForwardCompat(true));
glfw.window_hint(glfw::WindowHint::OpenglProfile(glfw::OpenGlProfileHint::Core));
let (window, _) =
glfw.create_window(WIDTH as u32, HEIGHT as u32, "fern", glfw::WindowMode::Windowed).unwrap();
window.make_current();
gl::load_with(|s| window.get_proc_address(s));
let program = gl::CreateProgram();
for &(type_, source) in [(gl::VERTEX_SHADER, VERTEX_SOURCE),
(gl::FRAGMENT_SHADER, FRAGMENT_SOURCE)].iter() {
let shader = gl::CreateShader(type_);
source.with_c_str(|source| gl::ShaderSource(shader, 1, &source, ptr::null()));
gl::CompileShader(shader);
gl::AttachShader(program, shader);
gl::DeleteShader(shader);
}
gl::LinkProgram(program);
gl::UseProgram(program);
"color".with_c_str(|p| gl::BindFragDataLocation(program, 0, p));
"tex".with_c_str(|p| gl::Uniform1i(gl::GetUniformLocation(program, p), 0));
gl::UseProgram(0);
let mut array = 0;
gl::GenVertexArrays(1, &mut array);
gl::BindVertexArray(array);
let position = [-1.0f32, 1.0, 1.0, 1.0, -1.0, -1.0, 1.0, -1.0];
let mut buffer = 0;
gl::GenBuffers(1, &mut buffer);
gl::BindBuffer(gl::ARRAY_BUFFER, buffer);
gl::BufferData(gl::ARRAY_BUFFER,
(position.len() * mem::size_of::<GLfloat>()) as GLsizeiptr,
mem::transmute(position.as_ptr()),
gl::STATIC_DRAW);
"position".with_c_str(|p| {
let location = gl::GetAttribLocation(program, p) as u32;
gl::EnableVertexAttribArray(location);
gl::VertexAttribPointer(location, 2, gl::FLOAT, gl::FALSE as GLboolean, 0, ptr::null());
});
gl::BindBuffer(gl::ARRAY_BUFFER, 0);
gl::BindVertexArray(0);
let mut texture = 0;
gl::GenTextures(1, &mut texture);
gl::BindTexture(gl::TEXTURE_2D, texture);
gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MIN_FILTER, gl::LINEAR as i32);
gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MAG_FILTER, gl::LINEAR as i32);
gl::TexImage2D(gl::TEXTURE_2D, 0, gl::RGB as i32,
WIDTH as i32, HEIGHT as i32,
0, gl::RED, gl::UNSIGNED_BYTE, mem::transmute(image.as_ptr()));
gl::BindTexture(gl::TEXTURE_2D, 0);
gl::ClearColor(1.0, 1.0, 1.0, 1.0);
gl::Enable(gl::BLEND);
gl::BlendFunc(gl::SRC_ALPHA, gl::ONE_MINUS_SRC_ALPHA);
while !window.should_close() {
glfw.poll_events();
gl::Clear(gl::COLOR_BUFFER_BIT);
gl::UseProgram(program);
gl::BindTexture(gl::TEXTURE_2D, texture);
gl::BindVertexArray(array);
gl::DrawArrays(gl::TRIANGLE_STRIP, 0, 4);
gl::BindVertexArray(0);
gl::BindTexture(gl::TEXTURE_2D, 0);
gl::UseProgram(0);
window.swap_buffers();
}
gl::DeleteTextures(1, &texture);
gl::DeleteBuffers(1, &buffer);
gl::DeleteVertexArrays(1, &array);
gl::DeleteProgram(program);
}
}
楽しい!₍₍ (ง╹◡╹)ว ⁾⁾