LoginSignup
3

More than 1 year has passed since last update.

posted at

updated at

🦀Rust + 💻WebGL = 💛 3.実践編

前前回前回を通して、RustでのWebGLと行列計算のやり方を解説しました。最後にこれらを利用し、トーラス(ドーナッツみたいなやつ)を描画したいと思います。
ソースコードをこちらで公開しています。動いているところだけ見たいという方はこちらからどうぞ

前提条件

  • Rustの基礎
  • WebGLの基礎
  • レンダリングの基礎
  • JavaScriptの基礎
  • 板ポリ1枚描画しただけで感動できるようなピュア(?)な心

謝辞

この記事の内容はWebGL 開発支援サイト wgld.orgフォンシェーディングまでを参考に作りました。ありがとうございます。

1. 新規プロジェクトの作成

terminal
$ cargo new webgl --lib

2.vertex/fragmentシェーダーの作成

srcフォルダ内にshaderフォルダを作り、以下のvertexシェーダーとfragmentシェーダーを配置します。

vertex.vert
attribute vec3 position;
attribute vec3 normal;
attribute vec4 color;
uniform mat4 mvpMatrix;
varying vec3 vNormal;
varying vec4 vColor;

void main(void) {
    vNormal = normal;
    vColor = color;
    gl_Position = mvpMatrix * vec4(position, 1.0);
}
fragment.frag
precision mediump float;

uniform mat4 invMatrix;
uniform vec3 lightDirection;
uniform vec3 eyeDirection;
uniform vec4 ambientColor;
varying vec3 vNormal;
varying vec4 vColor;

void main(void){
    vec3  invLight  = normalize(invMatrix * vec4(lightDirection, 0.0)).xyz;
    vec3  invEye    = normalize(invMatrix * vec4(eyeDirection, 0.0)).xyz;
    vec3  halfLE    = normalize(invLight + invEye);
    float diffuse   = clamp(dot(vNormal, invLight), 0.0, 1.0);
    float specular  = pow(clamp(dot(vNormal, halfLE), 0.0, 1.0), 50.0);
    vec4  destColor = vColor * vec4(vec3(diffuse), 1.0) + vec4(vec3(specular), 1.0) + ambientColor;
    gl_FragColor    = destColor;
}

3. トーラスの3dモデルを生成する

こちらを参考にしました。

shapes.rs
pub fn torus(row: u16, column: u16, i_rad: f32, o_rad: f32) -> (Vec<f32>, Vec<f32>, Vec<f32>, Vec<u16>) {
    let mut pos = Vec::new();
    let mut nor = Vec::new();
    let mut col = Vec::new();
    let mut idx = Vec::new();
    const PI: f32 = std::f32::consts::PI;

    for i in 0..=row {
        let r = PI * 2. / row as f32 * i as f32;
        let rr = r.cos();
        let ry = r.sin();
        for ii in 0..=column {
            let tr = PI * 2. / column as f32 * ii as f32;
            let tx = (rr * i_rad + o_rad) * tr.cos();
            let ty = ry * i_rad;
            let tz = (rr * i_rad + o_rad) * tr.sin();
            let rx = rr * tr.cos();
            let rz = rr * tr.sin();
            pos.push(tx);
            pos.push(ty);
            pos.push(tz);
            nor.push(rx);
            nor.push(ry);
            nor.push(rz);
            let tc = hsva(360. / column as f32 * ii as f32, 1., 1., 1.).unwrap();
            for c in tc {
                col.push(c);
            }
        }
    }

    for i in 0..row {
        for ii in 0..column {
            let r = (column + 1) * i + ii;
            idx.push(r);
            idx.push(r + column + 1);
            idx.push(r + 1);
            idx.push(r + column + 1);
            idx.push(r + column + 2);
            idx.push(r + 1);
        }
    }

    (pos, nor, col, idx)
}

fn hsva(h: f32, s: f32, v: f32, a: f32) -> Result<[f32; 4], String> {
    if s > 1. || v > 1. || a > 1. {
        return Err("invalid value".to_string());
    }

    if s == 0. {
        return Ok([v, v, v, a]);
    }

    let th = h % 360.;
    let i = (th / 60.).floor() as usize;
    let f = th / 60. - i as f32;
    let m = v * (1. - s);
    let n = v * (1. - s * f);
    let k = v * (1. - s * (1. - f));

    let r = [v, n, m, m, k, v];
    let g = [k, v, v, n, m, m];
    let b = [m, m, k, v, v, n];

    Ok([r[i], g[i], b[i], a])
}

4. 行列計算

🦀Rust + 💻WebGL = 💛 2. 行列計算編を参照してください。

mat_4.rs
pub struct Matrix {
    value: [f32; 16],
}

#[allow(dead_code)]
impl Matrix {

    //--create new matrix--
    //  <return> Matrix
    pub fn new() -> Self {
        Self {
            value: [
                1., 0., 0., 0., 0., 1., 0., 0., 0., 0., 1., 0., 0., 0., 0., 1.,
            ] as [f32; 16],
        }
    }

    //--Get matrix value--
    //  <return> [f32; 16]
    pub fn get_value(&self) -> [f32; 16] {
        self.value
    }

    //--Set a value in the matrix
    //  <argument>
    //      m &[f32; 16] : value to set
    pub fn set_value(&mut self, m: &[f32; 16]) -> &mut Self {
        self.value = *m;
        self
    }

    //--Set the identity matrix in the matrix
    pub fn set_identity(&mut self) -> &mut Self {
        self.value = [
            1., 0., 0., 0., 0., 1., 0., 0., 0., 0., 1., 0., 0., 0., 0., 1.,
        ];
        self
    }

    //--Set other matrix value in the matrix
    //  <argument>
    //      m &Matrix : other matrix to set
    pub fn substitution(&mut self, m: &Matrix) -> &mut Self {
        self.value = m.value;
        self
    }

    //--Multiply other matrix
    //  <argument>
    //      m &Matrix : other matrix to multiply
    //  <note>
    //      Column-major order!
    pub fn multiply(&mut self, m: &Matrix) -> &mut Self {
        let mut dest: [f32; 16] = [0.; 16];

        dest[0] = m.value[0] * self.value[0]
            + m.value[1] * self.value[4]
            + m.value[2] * self.value[8]
            + m.value[3] * self.value[12];
        dest[1] = m.value[0] * self.value[1]
            + m.value[1] * self.value[5]
            + m.value[2] * self.value[9]
            + m.value[3] * self.value[13];
        dest[2] = m.value[0] * self.value[2]
            + m.value[1] * self.value[6]
            + m.value[2] * self.value[10]
            + m.value[3] * self.value[14];
        dest[3] = m.value[0] * self.value[3]
            + m.value[1] * self.value[7]
            + m.value[2] * self.value[11]
            + m.value[3] * self.value[15];

        dest[4] = m.value[4] * self.value[0]
            + m.value[5] * self.value[4]
            + m.value[6] * self.value[8]
            + m.value[7] * self.value[12];
        dest[5] = m.value[4] * self.value[1]
            + m.value[5] * self.value[5]
            + m.value[6] * self.value[9]
            + m.value[7] * self.value[13];
        dest[6] = m.value[4] * self.value[2]
            + m.value[5] * self.value[6]
            + m.value[6] * self.value[10]
            + m.value[7] * self.value[14];
        dest[7] = m.value[4] * self.value[3]
            + m.value[5] * self.value[7]
            + m.value[6] * self.value[11]
            + m.value[7] * self.value[15];

        dest[8] = m.value[8] * self.value[0]
            + m.value[9] * self.value[4]
            + m.value[10] * self.value[8]
            + m.value[11] * self.value[12];
        dest[9] = m.value[8] * self.value[1]
            + m.value[9] * self.value[5]
            + m.value[10] * self.value[9]
            + m.value[11] * self.value[13];
        dest[10] = m.value[8] * self.value[2]
            + m.value[9] * self.value[6]
            + m.value[10] * self.value[10]
            + m.value[11] * self.value[14];
        dest[11] = m.value[8] * self.value[3]
            + m.value[9] * self.value[7]
            + m.value[10] * self.value[11]
            + m.value[11] * self.value[15];

        dest[12] = m.value[12] * self.value[0]
            + m.value[13] * self.value[4]
            + m.value[14] * self.value[8]
            + m.value[15] * self.value[12];
        dest[13] = m.value[12] * self.value[1]
            + m.value[13] * self.value[5]
            + m.value[14] * self.value[9]
            + m.value[15] * self.value[13];
        dest[14] = m.value[12] * self.value[2]
            + m.value[13] * self.value[6]
            + m.value[14] * self.value[10]
            + m.value[15] * self.value[14];
        dest[15] = m.value[12] * self.value[3]
            + m.value[13] * self.value[7]
            + m.value[14] * self.value[11]
            + m.value[15] * self.value[15];

        self.value = dest;
        self
    }


    //--Transpose the matrix--
    pub fn transpose(&mut self) -> &mut Self {
        self.value = [
            self.value[0],
            self.value[4],
            self.value[8],
            self.value[12],
            self.value[1],
            self.value[5],
            self.value[9],
            self.value[13],
            self.value[2],
            self.value[6],
            self.value[10],
            self.value[14],
            self.value[3],
            self.value[7],
            self.value[11],
            self.value[15],
        ];

        self
    }

    //--Create translation matrix and multiply it--
    pub fn translation(&mut self, v: &[f32; 3]) -> &mut Self {
        let translation_mat = Matrix {
            value: [
                1., 0., 0., 0., 0., 1., 0., 0., 0., 0., 1., 0., v[0], v[1], v[2], 1.,
            ],
        };

        self.multiply(&translation_mat);
        self
    }

    //--Create scaling matrix and multiply it--
    pub fn scaling(&mut self, v: &[f32; 3]) -> &mut Self {
        let scaling_mat = Matrix {
            value: [
                v[0], 0., 0., 0., 0., v[1], 0., 0., 0., 0., v[2], 0., 0., 0., 0., 1.,
            ],
        };

        self.multiply(&scaling_mat);
        self
    }

    pub fn rotate_around_y(&mut self, rad: f32) -> &mut Self {
        let r_c = rad.cos();
        let r_s = rad.sin();
        let y_mut = Matrix {
            value: [
                r_c, 0., r_s, 0., 0., 1., 0., 0., -r_s, 0., r_c, 0., 0., 0., 0., 1.,
            ],
        };

        self.multiply(&y_mut);
        self
    }

    pub fn rotate_around_x(&mut self, rad: f32) -> &mut Self {
        let r_c = rad.cos();
        let r_s = rad.sin();
        let x_mut = Matrix {
            value: [
                r_c, 0., r_s, 0., 0., 1., 0., 0., -r_s, 0., r_c, 0., 0., 0., 0., 1.,
            ],
        };
        self.multiply(&x_mut);
        self
    }

    pub fn rotate_around_z(&mut self, rad: f32) -> &mut Self {
        let r_c = rad.cos();
        let r_s = rad.sin();
        let z_mut = Matrix {
            value: [
                r_c, -r_s, 0., 0., r_s, r_c, 0., 0., 0., 0., 1., 0., 0., 0., 0., 1.,
            ],
        };
        self.multiply(&z_mut);
        self
    }

    //--Create look_at matrix--
    pub fn look_at(&mut self, from: &[f32; 3], to: &[f32; 3], up: &[f32; 3]) -> &mut Self {
        let mut l: f32;
        let mut x: [f32; 3] = [0.; 3];
        let mut y: [f32; 3] = [0.; 3];
        let mut z: [f32; 3] = [0.; 3];

        z[0] = from[0] - to[0];
        z[1] = from[1] - to[1];
        z[2] = from[2] - to[2];
        l = (z[0] * z[0] + z[1] * z[1] + z[2] * z[2]).sqrt().recip();
        z[0] *= l;
        z[1] *= l;
        z[2] *= l;

        x[0] = up[1] * z[2] - up[2] * z[1];
        x[1] = up[2] * z[0] - up[0] * z[2];
        x[2] = up[0] * z[1] - up[1] * z[0];
        l = (x[0] * x[0] + x[1] * x[1] + x[2] * x[2]).sqrt().recip();
        x[0] *= l;
        x[1] *= l;
        x[2] *= l;

        y[0] = z[1] * x[2] - z[2] * x[1];
        y[1] = z[2] * x[0] - z[0] * x[2];
        y[2] = z[0] * x[1] - z[1] * x[0];
        l = (y[0] * y[0] + y[1] * y[1] + y[2] * y[2]).sqrt().recip();
        y[0] *= l;
        y[1] *= l;
        y[2] *= l;

        let d_12: f32 = -(x[0] * from[0] + x[1] * from[1] + x[2] * from[2]);
        let d_13: f32 = -(y[0] * from[0] + y[1] * from[1] + y[2] * from[2]);
        let d_14: f32 = -(z[0] * from[0] + z[1] * from[1] + z[2] * from[2]);
        self.value = [
            x[0], y[0], z[0], 0.,
            x[1], y[1], z[1], 0.,
            x[2], y[2], z[2], 0.,
            d_12, d_13, d_14, 1.,
        ];
        self
    }

    //--Create perspective projections matrix--
    //  <argument>
    //      aspect f32  ratio parameter is the width divided by the height
    //      fovy   f32  field of view y-axis
    //      near   f32  near clipping plane
    //      far    f32  far clipping plane
    //  <note>
    //      Right-Handed Coordinate System!
    pub fn perspective(&mut self, aspect: f32, fovy: f32, near: f32, far: f32) -> &mut Self {
        let mut dest: [f32; 16] = [0.; 16];
        let t: f32 = (fovy / 2.).tan();
        let d: f32 = far - near;
        dest[0] = 1. / (t * aspect);
        dest[5] = 1. / t;
        dest[10] = - far / d;
        dest[11] = -1.;
        dest[14] = - far * near / d;

        self.value = dest;
        self
    }

    //--inverse the matrix
    pub fn inverse(&mut self) -> Result<&mut Self, i8> {
        const SIZE: usize = 4;
        let mut inv = Matrix::identity();
        let mut buf: f32;
        let mut a = self.value;

        for i in 0..SIZE {
            if a[i * SIZE + i] == 0. {
                return Err(-1);
            }
            buf = 1. / a[i * SIZE + i];
            for j in 0..SIZE {
                a[i * SIZE + j] *= buf;
                inv[i * SIZE + j] *= buf;
            }
            for j in 0..SIZE {
                if i != j {
                    buf = a[j * SIZE + i];
                    for k in 0..SIZE {
                        a[j * SIZE + k] -= a[i * SIZE + k] * buf;
                        inv[j * SIZE + k] -= inv[i * SIZE + k] * buf;
                    }
                }
            }
        }

        self.value = inv;
        Ok(self)
    }

    fn identity() -> [f32; 16] {
        [
            1., 0., 0., 0., 0., 1., 0., 0., 0., 0., 1., 0., 0., 0., 0., 1.,
        ] as [f32; 16]
    }
}

5. WebGLの操作

🦀Rust + 💻WebGL = 💛 1.WebGL基本操作編を参照してください。

webgl.rs
use wasm_bindgen::JsCast;
use web_sys::WebGlRenderingContext as GL;
use web_sys::*;

pub fn get_webgl_context(height: u32, width: u32) -> Result<WebGlRenderingContext, String> {
    //Get WebGLContext
    let document = window().unwrap().document().unwrap();
    let canvas = document
        .get_element_by_id("canvas")
        .ok_or_else(|| String::from("canvas doesn't exist :("))?;
    let canvas: web_sys::HtmlCanvasElement =
        canvas.dyn_into::<web_sys::HtmlCanvasElement>().unwrap();

    canvas.set_height(height);
    canvas.set_width(width);

    let gl: WebGlRenderingContext = canvas
        .get_context("webgl")
        .unwrap()
        .ok_or_else(|| String::from("webgl is not supported in this browser :("))?
        .dyn_into()
        .unwrap();

    //Initialize WebGLContext
    gl.enable(GL::BLEND);
    gl.blend_func(GL::SRC_ALPHA, GL::ONE_MINUS_SRC_ALPHA);
    gl.clear_color(0.0, 0.0, 0.0, 1.0); //RGBA
    gl.clear_depth(1.);

    Ok(gl)
}

pub fn link_program(
    gl: &WebGlRenderingContext,
    vert_source: &str,
    frag_source: &str,
) -> Result<WebGlProgram, String> {
    let program = gl
        .create_program()
        .ok_or_else(|| String::from("Error creating program"))?;

    let vert_shader = compile_shader(&gl, GL::VERTEX_SHADER, vert_source).unwrap();

    let frag_shader = compile_shader(&gl, GL::FRAGMENT_SHADER, frag_source).unwrap();

    gl.attach_shader(&program, &vert_shader);
    gl.attach_shader(&program, &frag_shader);
    gl.link_program(&program);

    if gl
        .get_program_parameter(&program, WebGlRenderingContext::LINK_STATUS)
        .as_bool()
        .unwrap_or(false)
    {
        gl.use_program(Some(&program));
        Ok(program)
    } else {
        Err(gl
            .get_program_info_log(&program)
            .unwrap_or_else(|| String::from("Unknown error creating program object")))
    }
}

fn compile_shader(
    gl: &WebGlRenderingContext,
    shader_type: u32,
    source: &str,
) -> Result<WebGlShader, String> {
    let shader = gl
        .create_shader(shader_type)
        .ok_or_else(|| String::from("Error creating shader"))?;
    gl.shader_source(&shader, source);
    gl.compile_shader(&shader);

    if gl
        .get_shader_parameter(&shader, WebGlRenderingContext::COMPILE_STATUS)
        .as_bool()
        .unwrap_or(false)
    {
        Ok(shader)
    } else {
        Err(gl
            .get_shader_info_log(&shader)
            .unwrap_or_else(|| String::from("Unable to get shader info log")))
    }
}

#[allow(dead_code)]
pub fn create_vbo_array(gl: &GL, data: &[f32]) -> Result<WebGlBuffer, String> {
    let vbo = gl.create_buffer().ok_or("Failed to create buffer :(")?;
    gl.bind_buffer(GL::ARRAY_BUFFER, Some(&vbo));
    unsafe {
        let f32_array = js_sys::Float32Array::view(data);
        gl.buffer_data_with_array_buffer_view(GL::ARRAY_BUFFER, &f32_array, GL::STATIC_DRAW)
    }
    gl.bind_buffer(GL::ARRAY_BUFFER, None);

    Ok(vbo)
}

pub fn create_vbo_vector(gl: &GL, data: &Vec<f32>) -> Result<WebGlBuffer, String> {
    let vbo = gl.create_buffer().ok_or("Failed to create buffer :(")?;
    gl.bind_buffer(GL::ARRAY_BUFFER, Some(&vbo));
    unsafe {
        let f32_array = js_sys::Float32Array::view(&(*data));
        gl.buffer_data_with_array_buffer_view(GL::ARRAY_BUFFER, &f32_array, GL::STATIC_DRAW)
    }
    gl.bind_buffer(GL::ARRAY_BUFFER, None);

    Ok(vbo)
}

#[allow(dead_code)]
pub fn create_ibo_array(gl: &GL, data: &[u16]) -> Result<WebGlBuffer, String> {
    let ibo = gl.create_buffer().ok_or("Failed to create buffer :(")?;

    gl.bind_buffer(GL::ELEMENT_ARRAY_BUFFER, Some(&ibo));
    unsafe {
        let ui16_array = js_sys::Uint16Array::view(data);
        gl.buffer_data_with_array_buffer_view(
            GL::ELEMENT_ARRAY_BUFFER,
            &ui16_array,
            GL::STATIC_DRAW,
        );
    }
    gl.bind_buffer(GL::ELEMENT_ARRAY_BUFFER, None);

    Ok(ibo)
}

pub fn create_ibo_vector(gl: &GL, data: &Vec<u16>) -> Result<WebGlBuffer, String> {
    let ibo = gl.create_buffer().ok_or("Failed to create buffer")?;

    gl.bind_buffer(GL::ELEMENT_ARRAY_BUFFER, Some(&ibo));
    unsafe {
        let ui16_array = js_sys::Uint16Array::view(&(*data));
        gl.buffer_data_with_array_buffer_view(
            GL::ELEMENT_ARRAY_BUFFER,
            &ui16_array,
            GL::STATIC_DRAW,
        );
    }
    gl.bind_buffer(GL::ELEMENT_ARRAY_BUFFER, None);

    Ok(ibo)
}

pub fn set_attribute(gl: &GL, vbo: &[WebGlBuffer], att_location: &[u32], att_stride: &[i32]) {
    for i in 0..vbo.len() {
        gl.bind_buffer(GL::ARRAY_BUFFER, Some(&vbo[i]));
        gl.enable_vertex_attrib_array(att_location[i]);
        gl.vertex_attrib_pointer_with_i32(att_location[i], att_stride[i], GL::FLOAT, false, 0, 0);
    }
}

6. lib.rsの編集

lib.rs

use wasm_bindgen::prelude::*;
use wasm_bindgen::*;
use web_sys::WebGlRenderingContext as GL;
mod mat_4;
mod shapes;
mod webgl;

fn request_animation_frame(f: &Closure<dyn FnMut()>) {
    web_sys::window()
        .unwrap()
        .request_animation_frame(f.as_ref().unchecked_ref())
        .expect("should register 'requestAnimationFrame'");
}

#[wasm_bindgen(start)]
pub fn start() -> Result<(), JsValue> {
    console_error_panic_hook::set_once();

    let height: f32 = 500.;
    let width: f32 = 500.;

    //-----Get context
    let gl = webgl::get_webgl_context(height as u32, width as u32).unwrap();

    //-----Compile and link program
    let program = webgl::link_program(
        &gl,
        include_str!("shader/vertex.vert"),
        include_str!("shader/fragment.frag"),
    )
    .unwrap();

    //Create vertex buffer object
    let att_location: [u32; 3] = [
        gl.get_attrib_location(&program, "position") as u32,
        gl.get_attrib_location(&program, "normal") as u32,
        gl.get_attrib_location(&program, "color") as u32,
    ];

    let att_stride: [i32; 3] = [3, 3, 4];
    let torus_data = shapes::torus(32, 32, 1.0, 2.0);

    let position = torus_data.0;
    let normal = torus_data.1;
    let color = torus_data.2;
    let index = torus_data.3;

    //Crate and set vbo
    let position_vbo = webgl::create_vbo_vector(&gl, &position).unwrap();
    let normal_vbo = webgl::create_vbo_vector(&gl, &normal).unwrap();
    let color_vbo = webgl::create_vbo_vector(&gl, &color).unwrap();
    webgl::set_attribute(
        &gl,
        &[position_vbo, normal_vbo, color_vbo],
        &att_location,
        &att_stride,
    );

    //Crate and set ibo
    let ibo = webgl::create_ibo_vector(&gl, &index).unwrap();
    gl.bind_buffer(GL::ELEMENT_ARRAY_BUFFER, Some(&ibo));

    //Model, view and projection transformation
    let uni_location = [
        gl.get_uniform_location(&program, "mvpMatrix").unwrap(),
        gl.get_uniform_location(&program, "invMatrix").unwrap(),
        gl.get_uniform_location(&program, "lightDirection").unwrap(),
        gl.get_uniform_location(&program, "eyeDirection").unwrap(),
        gl.get_uniform_location(&program, "ambientColor").unwrap(),
    ];

    gl.enable(GL::DEPTH_TEST);
    gl.enable(GL::CULL_FACE);
    gl.depth_func(GL::LEQUAL);

    let mut m_matrix = mat_4::Matrix::new();
    let mut v_matrix = mat_4::Matrix::new();
    let mut p_matrix = mat_4::Matrix::new();
    let mut mvp_matrix = mat_4::Matrix::new();
    let mut tmp_matrix = mat_4::Matrix::new();
    let mut inv_matrix = mat_4::Matrix::new();

    let eye_direction = [0., 0., 15.];
    v_matrix.look_at(&eye_direction, &[0., 0., 0.], &[0., 1., 0.]);
    p_matrix.perspective(width / height, 45., 0.1, 100.);
    tmp_matrix.substitution(&p_matrix).multiply(&v_matrix);

    let light_direction = [-0.5, 0.5, 0.5];
    let ambient_color = [0.1, 0.1, 0.1, 1.0];


    //call once per animation frame
    let f = std::rc::Rc::new(std::cell::RefCell::new(None));
    let g = f.clone();
    let mut i: f32 = 0.;
    *g.borrow_mut() = Some(Closure::wrap(Box::new(move || {
        if i >= 360. {
            i = 0.;
        }
        i += 1.;

        //Webgl initialize
        gl.clear_color(0.0, 0.0, 0.0, 1.0);
        gl.clear_depth(1.);
        gl.clear(GL::COLOR_BUFFER_BIT | GL::DEPTH_BUFFER_BIT);

        let rad = i * std::f32::consts::PI / 180.;

        //Draw by element
        m_matrix
            .set_identity()
            .rotate_around_y(rad)
            .rotate_around_z(rad);
        mvp_matrix.substitution(&tmp_matrix).multiply(&m_matrix);

        inv_matrix.substitution(&m_matrix).inverse().unwrap();

        gl.uniform_matrix4fv_with_f32_array(Some(&uni_location[0]), false, &mvp_matrix.get_value());
        gl.uniform_matrix4fv_with_f32_array(Some(&uni_location[1]), false, &inv_matrix.get_value());
        gl.uniform3fv_with_f32_array(Some(&uni_location[2]), &light_direction);
        gl.uniform3fv_with_f32_array(Some(&uni_location[3]), &eye_direction);
        gl.uniform4fv_with_f32_array(Some(&uni_location[4]), &ambient_color);
        gl.draw_elements_with_i32(GL::TRIANGLES, index.len() as i32, GL::UNSIGNED_SHORT, 0);

        //Context redrawn
        gl.flush();

        request_animation_frame(f.borrow().as_ref().unwrap());
    }) as Box<dyn FnMut()>));

    request_animation_frame(g.borrow().as_ref().unwrap());

    Ok(())
}

7. コンパイル & 呼び出し

まず、WebAssemblyにコンパイルします。

terminal
$ wasm-pack build --target web

そしたら以下のhtmlファイルで呼び出します。

index.html
<!DOCTYPE html>
<html>

<head>
    <title>WebGL & Rust</title>
</head>

<body>
    <canvas id="canvas"></canvas>
    <script type="module">
        import init from './pkg/webgl.js';
        async function start() {
            await init();
        };
        start();
    </script>
</body>

</html>

そうしたら、ローカルでサーバーを立ち上げます。VScodeの拡張機能であるLive Serverが手軽で良いでしょう。

まとめ

始めはなかなか難しいですが、慣れればRustって凄い書きやすいなーと感じます。皆さんもRust + WebGLでグラフィックスプログラミング、始めませんか?

関連記事

参考

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
What you can do with signing up
3