LoginSignup
9
11

More than 5 years have passed since last update.

プログラムでシダを描画する

Last updated at Posted at 2014-12-07

 やっはろー。これは、Rust Advent Calendar 2014 の 7日目の記事です。Rust から OpenGL を叩いてみたかったので、シダを描くことにしました。

 元ネタです。

プログラムでシダを描画する - 強火で進め

「プログラムでシダを描画する」一覧 - Qiita

// 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);
  }
}

シダ

楽しい!₍₍ (ง╹◡╹)ว ⁾⁾

床井研究室

wgld.org

bjz/gl-rs - GitHub

bjz/glfw-rs - GitHub

9
11
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
9
11