ValaGL + Gtk.GLArea ( Vala & OpenGL)
実行結果
(実際の実行では、スペースキーで、cubeが回転、停止します。)
下記のサイトで、Vala, SDL, GLEW で記述された、OpenGL 3.x に対応したrotating cubeアプリケーションが紹介されています。
(30/03/2013) ValaGL(simple skeletal application)
(https://github.com/lucidfox/valagl)
lucidfox | Vala and Modern OpenGL
http://lucidfox.dreamwidth.org/11614.html
http://en.lucidfox.org/2013/03/vala-and-modern-opengl/
1 GL 3.x APIに対応した vapiの作成
2 vec3とvec4に対応したmatrix とvector algebra、またprojections(camera)、
model matrix transformationsのVala言語による再実装
がなされています。
今回は、これらのプログラムを用いて、Gtk.GLArea上に同じrotating cubeを描画
します。
使用するプログラム
1 vapi
GLAreaでは、GLライブラリとしてepoxyを用いています。これに対応するように、
ここでは、CヘッダーファイルをGL/glew.hからepoxy/gl.hに変更しています。
(glepoxy.vapi)
2 ValaGL.Core内のプログラム
(GLProgram.vala、VBO.vala、IBO.vala、CoreError.vala、Camera.vala、
MatrixMath.vala、GeometryUtils.vala)
(1) VBO.vala、CoreError.vala、Camera.vala、MatrixMath.vala、GeometryUtils.vala
は変更なしで使用します。
(2) IBO.vala
1行コメントアウトします。
52行目 //glBindBuffer (GL_ELEMENT_ARRAY_BUFFER, 0);
(3) GLProgram.vala
変更します。下記参照。
3 main.vala (App.valaに相当)
## main.vala
using Gtk;
using Gdk;
public class MainWindow : Gtk.Window
{
private glArea gl_area;
private float angle = 0.0f;
private uint tick_id;
private bool isRunning = false;
public MainWindow ()
{
title = "GLArea Test";
destroy.connect (Gtk.main_quit);
set_events (EventMask.KEY_PRESS_MASK );
var box = new Box (Orientation.VERTICAL, 0);
gl_area = new glArea ();
this.key_press_event.connect ((evt) => {
if (evt.keyval == Gdk.Key.space) {
GLib.message ("Key Pressed");
if (isRunning == false) {
tick_id = gl_area.add_tick_callback (tick);
} else {
gl_area.remove_tick_callback (tick_id);
}
isRunning = !isRunning;
}
return true;
});
var button = new Button.with_label ("Quit");
button.clicked.connect (Gtk.main_quit);
box.pack_start (gl_area, true, true);
box.pack_start (button, false, false);
add (box);
}
private bool tick (Widget widget, FrameClock frame_clock)
{
angle = angle + 1.0f;
stdout.printf("angle: %f\n", angle);
gl_area.update_scene_data(angle);
gl_area.queue_render ();
return true;
}
}
int main (string args)
{
Gtk.init (ref args);
var mainWindow = new MainWindow();
mainWindow.show_all ();
Gtk.main ();
return 0;
}
4 glArea.vala (Canvas.valaに相当)
## glArea.vala
using Gtk;
using Gdk;
using GL;
using ValaGL.Core;
public class glArea : Gtk.GLArea
{
private GLuint vao = {0};
private GLProgram gl_program;
private VBO coord_vbo;
private VBO color_vbo;
private IBO element_ibo;
private GLint mvp_location;
private float rotation_angle;
private GLfloat cube_vertices = {
// front
-1, -1, 1,
1, -1, 1,
1, 1, 1,
-1, 1, 1,
// back
-1, -1, -1,
1, -1, -1,
1, 1, -1,
-1, 1, -1
};
private GLfloat cube_colors = {
// front colors
1, 0, 0,
0, 1, 0,
0, 0, 1,
1, 1, 1,
// back colors
1, 0, 0,
0, 1, 0,
0, 0, 1,
1, 1, 1
};
private GLushort cube_elements = {
// front
0, 1, 2,
2, 3, 0,
// top
1, 5, 6,
6, 2, 1,
// back
7, 6, 5,
5, 4, 7,
// bottom
4, 0, 3,
3, 7, 4,
// left
4, 5, 1,
1, 0, 4,
// right
3, 2, 6,
6, 7, 3
};
public glArea ()
{
set_size_request (500, 400);
has_depth_buffer = true;
render.connect (on_render);
}
private bool on_render (GLContext contxt)
{
stdout.printf ("render\n");
// realize
glGenVertexArrays(1, vao);
glBindVertexArray(vao[0]);
try {
gl_program = new GLProgram();
coord_vbo = new VBO(cube_vertices);
color_vbo = new VBO(cube_colors);
element_ibo = new IBO(cube_elements);
} catch (CoreError e) {
stdout.printf("CoreError: %s\n", e.message);
}
mvp_location = gl_program.get_uniform_location("MVP");
glEnableVertexAttribArray(0);
coord_vbo.apply_as_vertex_array(0, 3);
glEnableVertexAttribArray(1);
color_vbo.apply_as_vertex_array(1, 3);
glBindVertexArray(0);
// render
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
var camera = new Camera ();
camera.set_perspective_projection (70.0f, 5.0f/4.0f, 0.01f, 100.0f);
var eye = Vec3.from_data (0, 2, 0);
var center = Vec3.from_data (0, 0, -2);
var up = Vec3.from_data (0, 1, 0);
camera.look_at (ref eye, ref center, ref up);
var model_matrix = Mat4.identity ();
Vec3 translation = Vec3.from_data (0, 0, -4);
GeometryUtil.translate (ref model_matrix, ref translation);
Vec3 rotation = Vec3.from_data (0, 1, 0);
GeometryUtil.rotate (ref model_matrix, rotation_angle, ref rotation);
gl_program.make_current();
camera.apply (mvp_location, ref model_matrix);
glBindVertexArray(vao[0]);
glDrawElements((GLenum)GL_TRIANGLES, cube_elements.length,
GL_UNSIGNED_SHORT, null);
glBindVertexArray (0);
glUseProgram (0);
glFlush();
return true;
}
public void update_scene_data (float angle)
{
rotation_angle = angle;
}
}
* depth_bufferを有効にするには、GLAreaクラスのプロパティhas_depth_bufferを
trueにします。
5 GLProgram.vala
vertex shaderとfragment shaderをここに記述します。これは、前回(2015/11/15)
説明したワーニングが出ないようにするためです。各shaderプログラムを直接glShaderSource()に渡します。
(1) shaderプログラムをクラス定義(public class GLProgram)の前に挿入します。
・## vertex shader
const string vert_src = {
"#version 330 core\n" +
"layout(location = 0) in vec3 coord3d;\n" +
"layout(location = 1) in vec3 v_color;\n" +
"out vec3 f_color;\n" +
"uniform mat4 MVP;\n" +
"void main() {\n" +
" f_color = v_color;\n" +
" gl_Position = MVP * vec4(coord3d, 1.0);\n" +
"}"};
・## fragment shader
const string[] frag_src = {
"#version 330 core\n" +
"in vec3 f_color;\n" +
"void main() {\n" +
" gl_FragColor = vec4(f_color.x, f_color.y, f_color.z, 1.0);\n" +
"}"};
(2) shaderプログラムが関連する引数を削除し、変更します。
・ public GLProgram () throws CoreError
・ vertex_shader = create_shader (GL_VERTEX_SHADER)
fragment_shaderも同様。
(3) shaderプログラムの読込み部分を変更します。
・ create_shader_from_file メソッドは使用しない。
・ private static Gluint create_shader (Gluint shader_type) throws CoreErroe
・ if (shader_type == GL_VERTEX_SHADER) {
glShaderSource (shader, 1, vert_src, null);
} else {
glShaderSource (shader, 1, frag_src, null);
}
* プログラム
ValaGL-GLArea.zip (http://yahoo.jp/box/PhHb1j )
* ビルド
valac --vapidir=./ --pkg gtk+-3.0 --pkg gdk-pixbuf-2.0 --pkg glepoxy -X -lepoxy -X -lm
main.vala glArea.vala Camera.vala MatrixMath.vala GeometryUtil.vala GLProgram.vala
VBO.vala IBO.vala CoreError.vala