スポンサーリンク

Rust+OpenGLで三角形表示

以前初期化はやったことがあるが、今回はちゃんと三角形を描く。

基本的にglBeginのような古い関数は使えないので、GLSLで書くことになる。

Rust内の文字列をC言語の文字列に変換するところがいちいちややこしい。

draw.rs

use std::ptr::null;
use std::ffi::CString;
use std::ffi::CStr;

pub fn draw_prepare() ->(gl::types::GLuint, gl::types::GLuint){

    // 頂点の定義
    let vertices: [f32;9]=[
        0.0,  0.5, 0.0, // 上頂点
        -0.5, -0.5, 0.0, // 左下
        0.5, -0.5, 0.0, // 右下
    ];
    let color:[f32;9]=[
        1.0,0.0,0.0,
        0.0,1.0,0.0,
        0.0,0.0,1.0,
    ];

    let mut vertexbuffer=0;
    unsafe{
        gl::GenBuffers(1,&mut vertexbuffer);
        gl::BindBuffer(gl::ARRAY_BUFFER,vertexbuffer);
        gl::BufferData(
            gl::ARRAY_BUFFER,
            3*3*std::mem::size_of::<gl::types::GLfloat>() as gl::types::GLsizeiptr,
            vertices.as_ptr() as *const _,
            gl::STATIC_DRAW
        );
    }
    let mut colorbuffer=0;
    unsafe{
        gl::GenBuffers(1,&mut colorbuffer);
        gl::BindBuffer(gl::ARRAY_BUFFER,colorbuffer);
        gl::BufferData(
            gl::ARRAY_BUFFER,
            3*3*std::mem::size_of::<gl::types::GLfloat>() as gl::types::GLsizeiptr,
            color.as_ptr() as *const _,
            gl::STATIC_DRAW
        );
    }

    (vertexbuffer,colorbuffer)
}

pub fn prepare_vertex_shader()->gl::types::GLuint{
    let mut VertexShaderID = 0;

    // 頂点シェーダプログラム
    let vertex_shader_source = "\
#version 460 core

layout (location = 0) in vec3 aPos;
layout (location = 1) in vec3 incolor;

out vec4 vertexColor;

uniform mat4 modelViewMatrix;
uniform mat4 projectionMatrix;

void main()
{
  gl_Position = projectionMatrix * modelViewMatrix * vec4(aPos, 1.0);
  vertexColor = vec4(incolor, 1.0);
}
";
    let c_src = CString::new(vertex_shader_source).unwrap();

    let mut Result:gl::types::GLint = 0;
    let mut InfoLogLength:i32 = 0;
    let mut info_log;
    unsafe{
        VertexShaderID = gl::CreateShader(gl::VERTEX_SHADER);
        gl::ShaderSource(
            VertexShaderID,
            1,
            &c_src.as_ptr(),
            std::ptr::null()
        );


        gl::CompileShader(VertexShaderID);

        // シェーダのチェック
        gl::GetShaderiv(VertexShaderID,gl::COMPILE_STATUS,&mut Result);
        gl::GetShaderiv(VertexShaderID,gl::INFO_LOG_LENGTH,&mut InfoLogLength);

        if InfoLogLength > 0 {
            info_log = vec![0u8; InfoLogLength as usize];
            if Result == gl::FALSE as i32 {
                gl::GetShaderInfoLog(
                    VertexShaderID,
                    InfoLogLength,
                    std::ptr::null_mut(),
                    info_log.as_mut_ptr() as *mut gl::types::GLchar
                );

                if let Ok(msg) = CStr::from_ptr(info_log.as_ptr() as *const i8).to_str() {
                    println!("Vertex Shader Error :\n {}\n", msg);
                }
            }
        }
    }

    VertexShaderID
}

pub fn prepare_fragment_shader()->gl::types::GLuint{
    let mut FragmentShaderID=0;

    let fragment_shader_source = "\
#version 460 core

out vec4 FragColor;

in vec4 vertexColor;

void main()
{
  FragColor = vertexColor;
}
";
    let c_str = CString::new(fragment_shader_source).unwrap();
    let mut Result:gl::types::GLint=0;
    let mut InfoLogLength:i32=0;
    let mut info_log;
    unsafe {
        FragmentShaderID = gl::CreateShader(gl::FRAGMENT_SHADER);
        gl::ShaderSource(
            FragmentShaderID,
            1,
            &c_str.as_ptr(), null()
        );
        gl::CompileShader(FragmentShaderID);

        // フラグメントシェーダ
        gl::GetShaderiv(FragmentShaderID, gl::COMPILE_STATUS, &mut Result);
        gl::GetShaderiv(FragmentShaderID, gl::INFO_LOG_LENGTH, &mut InfoLogLength);
        if InfoLogLength > 0 {
            if Result == gl::FALSE as i32 {
                info_log = vec![0u8; InfoLogLength as usize];
                gl::GetShaderInfoLog(
                    FragmentShaderID,
                    InfoLogLength,
                    std::ptr::null_mut(),
                    info_log.as_mut_ptr() as *mut gl::types::GLchar
                );
                if let Ok(msg) = CStr::from_ptr(info_log.as_ptr() as *const i8).to_str() {
                    println!("Fragment Shader Error :\n{}\n", msg);
                }
            }
        }
    }

    FragmentShaderID

}

pub fn link_program(VertexShaderID:gl::types::GLuint,FragmentShaderID:gl::types::GLuint)->gl::types::GLuint{
    let mut Result:gl::types::GLint = gl::FALSE as i32;
    let mut InfoLogLength:i32=0;
    let mut ProgramID:gl::types::GLuint=0;

    println!("Linking program");

    unsafe{
        ProgramID = gl::CreateProgram();

        gl::AttachShader(ProgramID,VertexShaderID);
        gl::AttachShader(ProgramID,FragmentShaderID);
        gl::LinkProgram(ProgramID);

        gl::GetProgramiv(ProgramID,gl::LINK_STATUS,&mut Result);
        gl::GetProgramiv(ProgramID,gl::INFO_LOG_LENGTH,&mut InfoLogLength);

        if InfoLogLength > 0 {
            let mut ProgramErrorMessage = vec![0u8; InfoLogLength as usize];

            gl::GetProgramInfoLog(
                ProgramID,
                InfoLogLength,
                std::ptr::null_mut(),
                ProgramErrorMessage.as_mut_ptr() as *mut gl::types::GLchar
            );

            if let Ok(msg) = CStr::from_ptr(ProgramErrorMessage.as_ptr() as *const i8).to_str() {
                println!("Program Link Error:\n{}\n",msg);
            }
        }
    }

    ProgramID

}

pub fn draw_triangle(programid:gl::types::GLuint,vertexbuffer: gl::types::GLuint,colorbuffer:gl::types::GLuint){

    unsafe {
        gl::UseProgram(programid);
    }

    let proj_mat:[f32;16]=[
        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.0,
    ];

    let model_mat:[f32;16]=[
        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.0,
    ];

    let proj;
    let model;
    unsafe {
        let proj_location_name = CString::new("projectionMatrix").unwrap();
        let model_location_name = CString::new("modelViewMatrix").unwrap();
        proj = gl::GetUniformLocation(programid,proj_location_name.as_ptr());
        model = gl::GetUniformLocation(programid,model_location_name.as_ptr());

        gl::UniformMatrix4fv(proj,1,gl::FALSE,proj_mat.as_ptr());
        gl::UniformMatrix4fv(model,1,gl::FALSE,model_mat.as_ptr());
    }
/*
    // デバッグ
    println!("proj location: {}",proj);
    println!("model location: {}",model);
*/

    unsafe{
        gl::EnableVertexAttribArray(0);
        gl::BindBuffer(gl::ARRAY_BUFFER,vertexbuffer);
        gl::VertexAttribPointer(
            0,
            3,
            gl::FLOAT,
            gl::FALSE,
            0,
            null()
        );
    }

    unsafe{
        gl::EnableVertexAttribArray(1);
        gl::BindBuffer(gl::ARRAY_BUFFER,colorbuffer);
        gl::VertexAttribPointer(
            1,
            3,
            gl::FLOAT,
            gl::FALSE,
            0,
            null()
        );
    }

    unsafe {
        gl::DrawArrays(gl::TRIANGLES, 0,3);

        let err_check = gl::GetError();
        if err_check != gl::NO_ERROR {
            println!("ERROR::: {}\n", err_check);
        }

        gl::DisableVertexAttribArray(0);
        gl::DisableVertexAttribArray(1);
    }

    unsafe {
        gl::UseProgram(0);
    }

}

main.rs

// 自作ファイル draw.rs の使用
mod draw;

// 自作関数を呼び出せるようにする
use draw::draw_triangle;
use draw::draw_prepare;
use draw::prepare_vertex_shader;
use draw::prepare_fragment_shader;
use draw::link_program;

fn main() {

    let event_loop = glutin::event_loop::EventLoop::new();
    let window = glutin::window::WindowBuilder::new().with_title("Rust glutin OpenGL");

    // GLのコンテキストを作成
    // この gl_context は ContextBuilder<NotCurrent,Window> 型
    let gl_context = glutin::ContextBuilder::new()
        // コアプロファイルを (3, 1) で3.1 に指定。3.2以上だとVAOが必須になりコードの追記が必要なため
        .with_gl(glutin::GlRequest::Specific(glutin::Api::OpenGl, (3, 1)))
        .build_windowed(window, &event_loop)
        .expect("Cannot create context");

    // Rustにはシャドーイングがあるので、同じ名前の別変数を同じスコープ内に定義できる。
    // この gl_context は ContextBuilder<PossibleCurrent,Window> 型
    // 以降、gl_currentは以前の型の意味で用いることはできない
    let gl_context = unsafe {
        gl_context
            .make_current()
            .expect("Failed to make context current")
    };


    // OpenGLの各関数を初期化
    // これでgl::Viewportをはじめとする各関数の関数ポインタにアドレスが設定され、呼び出せるようになる。
    gl::load_with(|symbol| gl_context.get_proc_address(symbol) as *const _);

    ///////////////////////////////////////////////////////////////////////////////////
    let (vbuffer,cbuffer) = draw_prepare(); // 座標と色のバッファを定義
    let VertexShaderID:gl::types::GLuint = prepare_vertex_shader(); // バーテクスシェーダ作成
    let FragmentShaderID:gl::types::GLuint = prepare_fragment_shader(); // フラグメントシェーダ作成
    let ProgramID:gl::types::GLuint = link_program(VertexShaderID,FragmentShaderID); // プログラムのリンク
    ///////////////////////////////////////////////////////////////////////////////////


    // event_loopを開始する
    event_loop.run(move |event, _, control_flow| {

        // Pollを指定するとループが走り続ける
        // Waitを指定するとイベントが発生したときだけループが動く。
        *control_flow = glutin::event_loop::ControlFlow::Wait;


        match event {

            glutin::event::Event::WindowEvent { event, .. } => match event {


                ////////////////////////////////////////////////////////////////////
                // ウィンドウを閉じる
                glutin::event::WindowEvent::CloseRequested => {
                    // ウィンドウが閉じられた場合、event_loopを停止する
                    *control_flow = glutin::event_loop::ControlFlow::Exit;
                },


                ////////////////////////////////////////////////////////////////////
                // ウィンドウのサイズを変更
                glutin::event::WindowEvent::Resized(new_size) => {

                    // ビューポート再設定
                    unsafe {
                        gl::Viewport(0, 0, new_size.width as i32, new_size.height as i32);
                    }
                },


                ////////////////////////////////////////////////////////////////////
                _ => (),
            },


            _ => (),
        }

        // 描画
        unsafe {
            gl::Clear(gl::COLOR_BUFFER_BIT | gl::DEPTH_BUFFER_BIT);
            gl::ClearColor(0.3, 0.3, 0.5, 1.0);
            gl::Disable(gl::DEPTH_TEST);
            gl::Disable(gl::CULL_FACE);

            // 描画
            draw_triangle(ProgramID,vbuffer,cbuffer);

            gl::Flush();
        }
        // スワップバッファ
        gl_context.swap_buffers().unwrap();
        
    });
    
}

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

日本語が含まれない投稿は無視されますのでご注意ください。(スパム対策)


この記事のトラックバックURL: