Vala プログラミング

Rust & Vala プログラミング

おながのブログ

Rust kiss3d ライブラリ

kiss3dライブラリ(https://github.com/sebcrozet/kiss3d)の紹介です。

kiss3dは、3dのグラフィックス エンジン(graphics engine)です。
オブジェクト(cubeやsphereなど)の描画、オブジェクトの拡大縮小や回転などの
操作が簡単にできるように設定されています。
以下、examplesフォルダ内のサンプルです。

cube.rs
f:id:onagat12:20170507173142g:plain
実行直後は、オブジェクトが画面全体に広がっています。
オブジェクトの操作
・マウスホイールの上げ下げ:拡大縮小
・左ボタンのドラッグ;回転
・右ボタンのドラッグ:移動

primitives.rs
f:id:onagat12:20170507173234g:plain
(縮小した状態からのアニメーションです。以下の例も同様)

custom_mesh_shared.rs
f:id:onagat12:20170507173307g:plain

texturing.rs
f:id:onagat12:20170507173332g:plain

obj.rs
f:id:onagat12:20170507173359g:plain

Rust glium Tessellation (4) 3D surface

glium テッセレーション

David Wolff著「OpenGL 4.0 シェーディング言語 ( OpenGL 4.0 Shading
Language Cookbook )」
第6章「ジオメトリシェーダとテッセレーションシェーダ」
「3Dサーフェイスをテッセレートする」
「シェーディングしたメッシュの上にワイヤフレームを描く」

実行結果
f:id:onagat12:20170504171256p:plain tessellation level = 2
f:id:onagat12:20170504171311p:plain tessellation level = 4
f:id:onagat12:20170504171321p:plain tessellation level = 8

teapot多面体をテッセレーションを使って描画し、シェーディングした
メッシュの上にワイヤフレーム(エッジ)を描きます。

teapotの描画は、前回と同様です。
エッジの描画は次のようにします。

  1. メッシュ(三角形)の頂点と対辺との距離を求める。
  2. 描画するフラグメントが、3つの距離の最短の距離より短いか長いかにより、描画する色を決める。

(「シェーディングしたメッシュの上にワイヤフレームを描く」の項参照)


・シェーダプログラムの設定
バーテックスシェーダとテッセレーションシェーダは、前回(teapot)と
同じものを使用します。
ジオメトリシェーダとフラグメントシェーダは以下の様にします。

geometry_shader:  Some("
   #version 400

   layout(triangles) in;
   layout(triangle_strip, max_vertices = 3) out;

   in vec3 TENormal[];
   in vec4 TEPosition[];

   noperspective out vec3 EdgeDistance;
   out vec3 Normal;
   out vec4 Position;

   uniform mat4 ViewportMatrix;

   void main() {
      vec3 p0 = vec3(ViewportMatrix * (gl_in[0].gl_Position / gl_in[0].gl_Position.w));
      vec3 p1 = vec3(ViewportMatrix * (gl_in[1].gl_Position / gl_in[1].gl_Position.w));
      vec3 p2 = vec3(ViewportMatrix * (gl_in[2].gl_Position / gl_in[2].gl_Position.w));

      float a = length(p1 - p2);
      float b = length(p2 - p0);
      float c = length(p1 - p0);
      float alpha = acos( (b*b + c*c - a*a) / (2.0*b*c) );
      float beta = acos( (a*a + c*c - b*b) / (2.0*a*c) );
      float ha = abs( c * sin( beta ) );
      float hb = abs( c * sin( alpha ) );
      float hc = abs( b * sin( alpha ) );

      EdgeDistance = vec3( ha, 0, 0 );
      Normal = TENormal[0];
      Position = TEPosition[0];
      gl_Position = gl_in[0].gl_Position;
      EmitVertex();

      EdgeDistance = vec3( 0, hb, 0 );
      Normal = TENormal[1];
      Position = TEPosition[1];
      gl_Position = gl_in[1].gl_Position;
      EmitVertex();

      EdgeDistance = vec3( 0, 0, hc );
      Normal = TENormal[2];
      Position = TEPosition[2];
      gl_Position = gl_in[2].gl_Position;
      EmitVertex();

      EndPrimitive();
   }
"),
fragment_shader: "
   #version 140

   noperspective in vec3 EdgeDistance;
   in vec3 Normal;
   in vec4 Position;
   out vec4 f_color;

   uniform float LineWidth;
   uniform vec4 LineColor;
   uniform vec3 LightPosition;
   uniform vec3 LightIntensity;
   uniform vec3 Kd;

   vec3 diffuseModel( vec3 pos, vec3 norm ) {
      vec3 s = normalize( LightPosition - pos);
      float sDotN = max( dot(s,norm), 0.0 );
      vec3 diffuse = LightIntensity * Kd * sDotN;
      vec3 ambient = vec3(0.01, 0.01, 0.02);

      return ambient + diffuse;
   }

   float edgeMix() {
      float d = min( min( EdgeDistance.x, EdgeDistance.y ), EdgeDistance.z );

      if( d < LineWidth - 1 ) {
         return 1.0;
      } else if( d > LineWidth + 1 ) {
         return 0.0;
      } else {
         float x = d - (LineWidth - 1);
         return exp2(-2.0 * (x*x));
      }
   }

   void main() {
      float mixVal = edgeMix();
      vec4 color = vec4( diffuseModel( Position.xyz, Normal ), 1.0);
      color = pow( color, vec4(1.0/2.2) );
      f_color = mix( color, LineColor, mixVal );
   }
",

ジオメトリシェーダで三角形の頂点と対辺との距離を求め、フラグメント
シェーダで塗りつぶす色を決めています。
この部分は、Example code for the OpenGL Shading Language Cookbook
(https://github.com/daw42/glslcookbook)を参照しています。


・ビューポート(Viewport)変換
この例では、ビューポート変換を使います。
ビューポートの設定は、DrawParametersで行います。また、変換用の
マトリックスを設定します。

let params = glium::DrawParameters {
   depth: glium::Depth {
      test: glium::draw_parameters::DepthTest::IfLess,
      write: true,
      .. Default::default()
   },
   viewport: Some(glium::Rect {left:0, bottom:0, width:800, height:600}),
   .. Default::default()
};

let w2 :f32 = 800.0 / 2.0;
let h2 :f32 = 600.0 / 2.0;
let viewport = [
    [w2, 0.0, 0.0, 0.0f32],
    [0.0, h2, 0.0, 0.0f32],
    [0.0, 0.0, 1.0, 0.0f32],
    [w2, h2, 0.0, 1.0f32],
]; // マトリックス

Rust glium Tessellation (3) Teapot

glium テッセレーション
Teapot ( Phong Model )

実行結果
f:id:onagat12:20170503181454p:plain tessellation level = 2
f:id:onagat12:20170503181504p:plain tessellation level = 4
f:id:onagat12:20170503181513p:plain tessellation level = 8

1 depth bufferの設定

let display = glutin::WindowBuilder::new()
   .with_dimensions(800, 600)
   .with_title(format!("Tess Teapot PhongModel"))
   .with_depth_buffer(24)
   .build_glium().unwrap();

let params = glium::DrawParameters {
   depth: glium::Depth {
      test: glium::draw_parameters::DepthTest::IfLess,
      write: true,
      .. Default::default()
   },
   .. Default::default()
};

この例では、depth bufferを使用します。
depth bufferを有効にする方法

  1. OpenGL contextを生成する文で、「.with_depth_buffer(24)」を指定します。
  2. DrawParametersで、depthを設定します。


2 制御点の設定

#[derive(Copy, Clone)]
pub struct Vertex {
   position: [f32; 3],
}

implement_vertex!(Vertex, position);

pub const VERTICES: [Vertex; 512] = [
   // patch 1(rim1)
   Vertex { position: [ 1.4   ,  0.0   , 2.4] },
   Vertex { position: [ 1.4   , -0.784 , 2.4] },
   Vertex { position: [ 0.784 , -1.4   , 2.4] },
   Vertex { position: [ 0.0   , -1.4   , 2.4] },
   Vertex { position: [ 1.3375,  0.0   , 2.53125] },
   Vertex { position: [ 1.3375, -0.749 , 2.53125] },
   Vertex { position: [ 0.749 , -1.3375, 2.53125] },
   Vertex { position: [ 0.0   , -1.3375, 2.53125] },
   Vertex { position: [ 1.4375,  0.0   , 2.53125] },
   Vertex { position: [ 1.4375, -0.805 , 2.53125] },
   Vertex { position: [ 0.805 , -1.4375, 2.53125] },
   Vertex { position: [ 0.0   , -1.4375, 2.53125] },
   Vertex { position: [ 1.5   ,  0.0   , 2.4] },
   Vertex { position: [ 1.5   , -0.84  , 2.4] },
   Vertex { position: [ 0.84  , -1.5   , 2.4] },
   Vertex { position: [ 0.0   , -1.5   , 2.4] },
   // patch 2(rim2)
   Vertex { position: [ 0.0   , -1.4   , 2.4] },
   Vertex { position: [-0.784 , -1.4   , 2.4] },
   Vertex { position: [-1.4   , -0.784 , 2.4] },
   Vertex { position: [-1.4   ,  0.0   , 2.4] },
   ・・・
     省略
   ・・・
   // patch 32(bottom4)
   Vertex { position: [ 0.0  ,  0.0  , 0.0] },
   Vertex { position: [ 0.0  ,  0.0  , 0.0]},
   Vertex { position: [ 0.0  ,  0.0  , 0.0] },
   Vertex { position: [ 0.0  ,  0.0  , 0.0] },
   Vertex { position: [ 1.425,  0.0  , 0.0] },
   Vertex { position: [ 1.425,  0.798, 0.0] },
   Vertex { position: [ 0.798,  1.425, 0.0] },
   Vertex { position: [ 0.0  ,  1.425, 0.0] },
   Vertex { position: [ 1.5  ,  0.0  , 0.075] },
   Vertex { position: [ 1.5  ,  0.84 , 0.075] },
   Vertex { position: [ 0.84 ,  1.5  , 0.075] },
   Vertex { position: [ 0.0  ,  1.5  , 0.075] },
   Vertex { position: [ 1.5  ,  0.0  , 0.15] },
   Vertex { position: [ 1.5  ,  0.84 , 0.15] },
   Vertex { position: [ 0.84 ,  1.5  , 0.15] },
   Vertex { position: [ 0.0  ,  1.5  , 0.15] },
];

制御点はモジュールファイル(teapot.rs)で定義しています。
teapotは32のパッチで描画されます。各パッチは16個の制御点で構成
されます。
この16個の制御点がパッチプリミティブとなります。


3 VertexBufferの設定

let vertex_buffer = glium::VertexBuffer::new(&display, &teapot::VERTICES).unwrap();

モジュールファイルを使用しているので、「&teapot::VERTICES」と設定
します。


4 IndexBufferの設定(パッチプリミティブとパッチあたりの頂点数を設定)

let indices = glium::index::NoIndices(
    PrimitiveType::Patches { vertices_per_patch: 16 });

IndexBufferの設定は、1個のパッチ場合と同様です。これで全パッチの
描画が可能です。


5 シェーダプログラムの設定

let program = glium::Program::new(&display,
   glium::program::SourceCode {
      vertex_shader: "
         #version 140

         in vec3 position;

         void main() {
            gl_Position = vec4(position, 1.0);
         }
      ",
      tessellation_control_shader: Some("
         #version 400

         layout(vertices = 16) out;

         uniform int tess_level = 5;

         void main() {
            gl_out[gl_InvocationID].gl_Position =
               gl_in[gl_InvocationID].gl_Position;

            gl_TessLevelOuter[0] = tess_level;
            gl_TessLevelOuter[1] = tess_level;
            gl_TessLevelOuter[2] = tess_level;
            gl_TessLevelOuter[3] = tess_level;

            gl_TessLevelInner[0] = tess_level;
            gl_TessLevelInner[1] = tess_level;
         }
      "),
      tessellation_evaluation_shader: Some("
         #version 400

         layout(quads) in;

         out vec4 TEPosition;
         out vec3 TENormal;

         uniform mat4 model;
         uniform mat4 view;
         uniform mat4 perspective;

         void basisFunctions(out float[4] b, out float[4] db, float t) {
            float t1 = (1.0 - t);
            float t12 = t1 * t1;

            b[0] = t12 * t1;
            b[1] = 3.0 * t12 * t;
            b[2] = 3.0 * t1 * t * t;
            b[3] = t * t * t;

            db[0] = -3.0 * t1 * t1;
            db[1] = -6.0 * t * t1 + 3.0 * t12;
            db[2] = -3.0 * t * t + 6.0 * t * t1;
            db[3] = 3.0 * t * t;
         }

         void main() {
            float u = gl_TessCoord.x;
            float v = gl_TessCoord.y;

            vec4 p00 = gl_in[0].gl_Position;
            vec4 p01 = gl_in[1].gl_Position;
            vec4 p02 = gl_in[2].gl_Position;
            vec4 p03 = gl_in[3].gl_Position;
            vec4 p10 = gl_in[4].gl_Position;
            vec4 p11 = gl_in[5].gl_Position;
            vec4 p12 = gl_in[6].gl_Position;
            vec4 p13 = gl_in[7].gl_Position;
            vec4 p20 = gl_in[8].gl_Position;
            vec4 p21 = gl_in[9].gl_Position;
            vec4 p22 = gl_in[10].gl_Position;
            vec4 p23 = gl_in[11].gl_Position;
            vec4 p30 = gl_in[12].gl_Position;
            vec4 p31 = gl_in[13].gl_Position;
            vec4 p32 = gl_in[14].gl_Position;
            vec4 p33 = gl_in[15].gl_Position;

            float bu[4], bv[4];
            float dbu[4], dbv[4];
            basisFunctions(bu, dbu, u);
            basisFunctions(bv, dbv, v);

            TEPosition =
             p00*bu[0]*bv[0] + p01*bu[0]*bv[1] + p02*bu[0]*bv[2] +
             p03*bu[0]*bv[3] + p10*bu[1]*bv[0] + p11*bu[1]*bv[1] +
             p12*bu[1]*bv[2] + p13*bu[1]*bv[3] + p20*bu[2]*bv[0] +
             p21*bu[2]*bv[1] + p22*bu[2]*bv[2] + p23*bu[2]*bv[3] +
             p30*bu[3]*bv[0] + p31*bu[3]*bv[1] + p32*bu[3]*bv[2] +
             p33*bu[3]*bv[3];

            vec4 du =
             p00*dbu[0]*bv[0] + p01*dbu[0]*bv[1] +
             p02*dbu[0]*bv[2] + p03*dbu[0]*bv[3] +
             p10*dbu[1]*bv[0] + p11*dbu[1]*bv[1] +
             p12*dbu[1]*bv[2] + p13*dbu[1]*bv[3] +
             p20*dbu[2]*bv[0] + p21*dbu[2]*bv[1] +
             p22*dbu[2]*bv[2] + p23*dbu[2]*bv[3] +
             p30*dbu[3]*bv[0] + p31*dbu[3]*bv[1] +
             p32*dbu[3]*bv[2] + p33*dbu[3]*bv[3];

            vec4 dv =
             p00*bu[0]*dbv[0] + p01*bu[0]*dbv[1] +
             p02*bu[0]*dbv[2] + p03*bu[0]*dbv[3] +
             p10*bu[1]*dbv[0] + p11*bu[1]*dbv[1] +
             p12*bu[1]*dbv[2] + p13*bu[1]*dbv[3] +
             p20*bu[2]*dbv[0] + p21*bu[2]*dbv[1] +
             p22*bu[2]*dbv[2] + p23*bu[2]*dbv[3] +
             p30*bu[3]*dbv[0] + p31*bu[3]*dbv[1] +
             p32*bu[3]*dbv[2] + p33*bu[3]*dbv[3];

            vec3 n = normalize( cross(du.xyz, dv.xyz) );

            mat4 modelview = view * model;
            mat3 normal = mat3(modelview);

            gl_Position = perspective * modelview * TEPosition;

            TEPosition = modelview * TEPosition;
            TENormal = normalize( normal * n);
         }
      "),
      geometry_shader:  Some("
         #version 400

         layout(triangles) in;
         layout(triangle_strip, max_vertices = 3) out;

         in vec3 TENormal[];
         in vec4 TEPosition[];

         out vec3 Normal;
         out vec4 Position;

         void main() {
            Normal = TENormal[0];
            Position = TEPosition[0];
            gl_Position = gl_in[0].gl_Position;
            EmitVertex();

            Normal = TENormal[1];
            Position = TEPosition[1];
            gl_Position = gl_in[1].gl_Position;
            EmitVertex();

            Normal = TENormal[2];
            Position = TEPosition[2];
            gl_Position = gl_in[2].gl_Position;
            EmitVertex();

            EndPrimitive();
         }
      "),
      fragment_shader: "
         #version 140

         in vec3 Normal;
         in vec4 Position;
         out vec4 f_color;

         uniform vec3 LightPosition;
         uniform vec3 LightIntensity;
         uniform vec3 Ka;
         uniform vec3 Kd;
         uniform vec3 Ks;
         uniform float Shininess;

         void main() {
            vec3 s = normalize( LightPosition - Position.xyz);
            vec3 v = normalize(Position.xyz);
            vec3 r = reflect(-s, Normal);
            float sDotN = max( dot(s,Normal), 0.0 );

            vec3 ambient = Ka;
            vec3 diffuse = Kd * sDotN;
            vec3 specular = Ks * pow( max( dot(r,v), 0.0), Shininess);

            f_color = vec4(LightIntensity *(ambient + diffuse + specular), 0.1);
         }
      ",
   }).unwrap();

teapotの描画には、3次べジェ曲面を使います。
フォンモデルで描画するので、テッセレートされた各頂点での法線ベクトルも
必要になります。法線ベクトルは、補間式をテッセレーション変数u,vで微分し、
それらの外積から得られます。これらは、テッセレーション評価シェーダで
行います。
フォンモデルは、フラグメントシェーダで実装しています。


6 uniformの設定

let uniforms = uniform! {
   tess_level: tess_level,
   model: model,
   view: view,
   perspective: perspective,
   LightPosition: light_position,
   LightIntensity: light_intensity,
   Ka: ka,
   Kd: kd,
   Ks: ks,
   Shininess: shininess,
};

uniformの設定には、uniform!マクロを使います。コロンの左側がシェーダ内の
uniform変数で、右側がプログラム内で値を割り当てた変数束縛です。


7 描画

target.draw(&vertex_buffer, &indices, &program, &uniforms,
       &params).unwrap();

この例では、uniform変数と描画のパラメータ(DrawParameters)を使用して
います。
これらは、target.draw()文で、定義する変数束縛(uniformsとparams)を
設定します。

プロブラム(http://yahoo.jp/box/j6YVBz)

Rust glium Tessellation (2) 2D quad

glium テッセレーション

David Wolff著「OpenGL 4.0 シェーディング言語 ( OpenGL 4.0 Shading
Language Cookbook )」
第6章「ジオメトリシェーダとテッセレーションシェーダ」
「2D四角形をテッセレートする」

実行結果
f:id:onagat12:20170502132006p:plain tessellation level = 2
f:id:onagat12:20170502132017p:plain tessellation level = 4
f:id:onagat12:20170502132027p:plain tessellation level = 8
(Innerレベルは4に固定し、Outerレベルを変えている)

四角形の内部の点Pは、四角形の4隅(p0, p1, p2, p3)を制御点として、
次の補間式で表せる。u, vはパラメータで、0〜1の値をとる。

  P = p0 * (1-u) * (1-v)  + p1 * u * (1-v) +
      p2 * u * v + p3 * v * (1-u)
  制御点:p0 = [-0.8, -0.8], p1 = [ 0.8, -0.8], p2 = [ 0.8,  0.8],
         p3 = [-0.8,  0.8]

この制御点をパッチ プリミティブとして、テッセレーションを使って、
レベルに対応したu, vを生成し描画する。

1 制御点(コントールポイント)の設定

let vertex_buffer = {
   #[derive(Copy, Clone)]
   struct Vertex {
      position: [f32; 2],
   }

   implement_vertex!(Vertex, position);

   glium::VertexBuffer::new(&display,
      &[
         Vertex { position: [-0.8, -0.8] },
         Vertex { position: [ 0.8, -0.8] },
         Vertex { position: [ 0.8,  0.8] },
         Vertex { position: [-0.8,  0.8] },
      ]
   ).unwrap()
};

(内容は、前例と同様)


2 パッチ プリミティブとパッチあたりの頂点数を設定

let indices = glium::index::NoIndices(
    PrimitiveType::Patches { vertices_per_patch: 4 });

(内容は、前例と同様)


3 シェーダプログラムの設定

let program = glium::Program::new(&display,
  glium::program::SourceCode {
    vertex_shader: "
      #version 140

      in vec2 position;

      void main() {
        gl_Position = vec4(position, 0.0, 1.0);
      }
    ",
    tessellation_control_shader: Some("
      #version 400

      layout(vertices = 4) out;

      uniform int Outer = 5;
      uniform int Inner;

      void main() {
        gl_out[gl_InvocationID].gl_Position =
          gl_in[gl_InvocationID].gl_Position;

        gl_TessLevelOuter[0] = Outer;
        gl_TessLevelOuter[1] = Outer;
        gl_TessLevelOuter[2] = Outer;
        gl_TessLevelOuter[3] = Outer;

        gl_TessLevelInner[0] = Inner;
        gl_TessLevelInner[1] = Inner;
      }
    "),
    tessellation_evaluation_shader: Some("
      #version 400

      layout(quads, equal_spacing, ccw) in;

      void main() {
        float u = gl_TessCoord.x;
        float v = gl_TessCoord.y;

        vec4 p0 = gl_in[0].gl_Position;
        vec4 p1 = gl_in[1].gl_Position;
        vec4 p2 = gl_in[2].gl_Position;
        vec4 p3 = gl_in[3].gl_Position;

        gl_Position = p0 * (1-u) * (1-v)  + p1 * u * (1-v) +
              p2 * u * v + p3 * v * (1-u);
      }
    "),
    geometry_shader:  Some("
      #version 400

      layout(triangles) in;
      layout(triangle_strip, max_vertices = 3) out;

      out vec3 color;

      void main() {
         gl_Position = gl_in[0].gl_Position;
         color = vec3(1.0, 0.0, 0.0);
         EmitVertex();

         gl_Position = gl_in[1].gl_Position;
         color = vec3(1.0, 1.0, 0.0);
         EmitVertex();

         gl_Position = gl_in[2].gl_Position;
         color = vec3(0.0, 0.0, 1.0);
         EmitVertex();

         EndPrimitive();
      }
    "),
    fragment_shader: "
      #version 140

      in vec3 color;
      out vec4 f_color;

      void main() {
         f_color = vec4(color, 1.0);
      }
    ",
  }).unwrap();

この例ではジオメトリシェーダを使う。ジオメトリシェーダの設定にはOptionを
使うので、Some()で設定する。
テッセレーション座標u, vは、テッセレーション評価シェーダで、

 u = gl_TessCoord.x
 v = gl_TessCoord.y

の式で生成される。

Rust glium Tessellation (1) Bezier Curve

glium(https://github.com/tomaka/glium)のexamplesにあるtessellation
プログラム(tessellation.rs)を参考にして、David Wolff著「OpenGL 4.0
シェーディング言語 ( OpenGL 4.0 Shading Language Cookbook )」の6章
「ジオメトリシェーダとテッセレーションシェーダ」をgliumで書いてみました。

今回は、「曲線をテッセレートする」の項です。テッセレーションの方法で、
曲線を描きます。

実行結果
f:id:onagat12:20170502124430p:plain tessellation level = 3
f:id:onagat12:20170502124451p:plain tessellation level = 5
f:id:onagat12:20170502124458p:plain tessellation level = 8

この例では、曲線の描画に3次べジェ曲線(ブレンド関数はベルンシュタイン
多項式)を使うので、4個の制御点(コントロールポイント)が必要です。
この4個の制御点がパッチ プリミティブになります。

1 コントロールポイントの設定

let vertex_buffer = {
   #[derive(Copy, Clone)]
   struct Vertex {
      position: [f32; 2],
   }

   implement_vertex!(Vertex, position);

   glium::VertexBuffer::new(&display,
      &[
         Vertex { position: [-0.8, -0.8] },
         Vertex { position: [-0.4,  0.8] },
         Vertex { position: [ 0.4, -0.8] },
         Vertex { position: [ 0.8,  0.8] },
      ]
   ).unwrap()
};

VertexBufferを使ってコントロールポイントの頂点座標を設定します。


2 パッチ プリミティブとパッチあたりの頂点数を設定

let indices = glium::index::NoIndices(
    PrimitiveType::Patches { vertices_per_patch: 4 });

IndexBufferのPrimitiveTypeとvertices_per_patchで、パッチ プリミティブの
使用とパッチあたりの頂点数を設定します。


3 シェーダプログラムの設定

let program = glium::Program::new(&display,
   glium::program::SourceCode {
      vertex_shader: "
         #version 140

         in vec2 position;

         void main() {
            gl_Position = vec4(position, 0.0, 1.0);
         }
      ",
      tessellation_control_shader: Some("
         #version 400

         layout(vertices = 4) out;

         uniform int tess_level = 5;

         void main() {
            gl_out[gl_InvocationID].gl_Position =
               gl_in[gl_InvocationID].gl_Position;

            gl_TessLevelOuter[0] = 1;
            gl_TessLevelOuter[1] = tess_level;
         }
      "),
      tessellation_evaluation_shader: Some("
         #version 400

         layout(isolines) in;

         void main() {
            float u = gl_TessCoord.x;

            vec3 p0 = gl_in[0].gl_Position.xyz;
            vec3 p1 = gl_in[1].gl_Position.xyz;
            vec3 p2 = gl_in[2].gl_Position.xyz;
            vec3 p3 = gl_in[3].gl_Position.xyz;

            float u1 = (1.0 - u);
            float u2 = u * u;

            float b3 = u2 * u;
            float b2 = 3.0 * u2 * u1;
            float b1 = 3.0 * u * u1 * u1;
            float b0 = u1 * u1 * u1;

            vec3 p = p0 * b0 + p1 * b1 + p2 * b2 + p3 * b3;

            gl_Position = vec4(p, 1.0);
        }
      "),
      geometry_shader: None,
      fragment_shader: "
         #version 140

         out vec4 f_color;

         void main() {
            f_color = vec4(1.0, 1.0, 1.0, 1.0);
         }
      ",
   }).unwrap();

このプログラムでは、バーテックスシェーダ、テッセレーション制御シェーダ、
テッセレーション評価シェーダを使用します。このような場合は、Programの
SourceCodeを使って設定します。
テッセレーションシェーダとジオメトリシェーダはOptionとして設定するので、
None(使用しない)かSome()(使用する場合)で設定します。
曲線の描画では、Innerレベルは使用しません。Outerレベルでは、Outer[0]を1、
Outer[1]にレベルを設定します。GPUによっては、入れ替えが必要な場合が
あります。

Rust glium ( OpenGL ) triangle and square

rust の glium ライブラリを利用して、三角と四角を同一ウィンドウに
表示します。

実行結果
f:id:onagat12:20170430235917p:plain

プログラム

#[macro_use]
extern crate glium;

fn main() {
    use glium::{DisplayBuild, Surface};
    let display = glium::glutin::WindowBuilder::new()
        .with_dimensions(800, 600)
        .with_title(format!("Glium Triangle and Square"))
        .build_glium().unwrap();

    #[derive(Copy, Clone)]
    struct Vertex {
        position: [f32; 2],
    }

    implement_vertex!(Vertex, position);

    // triangle
    let vertex1 = Vertex { position: [-0.75, -0.5] };
    let vertex2 = Vertex { position: [-0.5 ,  0.5] };
    let vertex3 = Vertex { position: [-0.25, -0.5] };
    let shape1 = vec![vertex1, vertex2, vertex3];

    let vertex_buffer1 = glium::VertexBuffer::new(&display, &shape1).unwrap();
    let indices1 = glium::index::NoIndices(glium::index::PrimitiveType::TrianglesList);

    //square
    let vertex1 = Vertex { position: [ 0.25, -0.5] };
    let vertex2 = Vertex { position: [ 0.25,  0.5] };
    let vertex3 = Vertex { position: [ 0.75,  0.5] };
    let vertex4 = Vertex { position: [ 0.75, -0.5] };
    let shape2 = vec![vertex1, vertex2, vertex3, vertex4];

    let vertex_buffer2 = glium::VertexBuffer::new(&display, &shape2).unwrap();
    let indices2 = glium::IndexBuffer::new(&display,
                   glium::index::PrimitiveType::TrianglesList,
		   &[0u16, 1, 2, 2, 0, 3,]).unwrap();

    let vertex_shader_src = r#"
        #version 140

        in vec2 position;

        void main() {
            gl_Position = vec4(position, 0.0, 1.0);
	}
    "#;

    let fragment_shader_src = r#"
        #version 140

        out vec4 color;

        void main() {
            color = vec4(1.0, 0.0, 0.0, 1.0);
        }
    "#;

    let program = glium::Program::from_source(&display, vertex_shader_src,
		  fragment_shader_src, None).unwrap();

    loop {
        let mut target = display.draw();
	target.clear_color(0.0, 0.0, 1.0, 1.0);
	// triangle
	target.draw(&vertex_buffer1, &indices1, &program,
                    &glium::uniforms::EmptyUniforms, &Default::default()).unwrap();
        // square
        target.draw(&vertex_buffer2, &indices2, &program,
                    &glium::uniforms::EmptyUniforms, &Default::default()).unwrap();
	target.finish().unwrap();

	for ev in display.poll_events() {
	    match ev {
	        glium::glutin::Event::Closed => return,
		_ => ()
            }
        }
    }
}


Cargo.toml

[package]
name = "triangle"
version = "0.1.0"
authors = ["xxxxx"]

[dependencies]
glium = "*"


vertex, VertexBuffer, IndexBufferの設定
三角形と四角形のvertex, VertexBuffer, IndexBufferをそれぞれ準備します。
IndexBufferの設定では、三角形では頂点indexを使用しないのでNoIndicesを
使います。
四角形では頂点indexを使用しているので、IndexBufferを使います。

描画
target.draw()文を2回使って、三角形と四角形を描画します。

Rust OpenGL gliumライブラリ

今回は、RustでOpenGLを扱うライブラリの一つであるgliumについて
まとめています。(glium([https://github.com/tomaka/glium/)
examplesにあるtutorial-02.rsを使用しています。)

gliumでは、OpenGLAPIとは異なる独自のAPIを導入しています。
以下は、gliumでOpenGLを描画する際必要となる文です。

1 windowとOpenGL contextの準備

use glium::{DisplayBuild, Surface};
let display = glium::glutin::WindowBuilder::new()
    .with_dimensions(800, 600)
    .with_title(format!("Glium Triangle Test"))
    .build_glium().unwrap();

window操作のライブラリとして、glutinを利用しています。
この文は、windowとOpenGL contextを生成します。その際、windowサイズ、
windowタイトルも設定しています。


2 vertexの設定

#[derive(Copy, Clone)]
struct Vertex {
    position: [f32; 2],
}

implement_vertex!(Vertex, position);

let vertex1 = Vertex { position: [-0.5, -0.5] };
let vertex2 = Vertex { position: [ 0.0,  0.5] };
let vertex3 = Vertex { position: [ 0.5, -0.5] };
let shape = vec![vertex1, vertex2, vertex3];

#[derive]は継承のアトリビュートです。Vertex structureは、Copy, Clone
トレイトの機能を継承することになります。
頂点座標は、Vertex structureのベクトルとして設定します。


3 VertexBufferの設定

let vertex_buffer = glium::VertexBuffer::new(&display, &shape).unwrap();

頂点座標を、VertexBufferに設定します。


4 IndexBufferの設定

let indices = glium::index::NoIndices(glium::index::PrimitiveType::TrianglesList);

IndexBufferの設定に相当する文です。
この例では、indexを使用しません。このような場合は、primitive typeを
設定するため、NoIndicesを使います。


5 shaderの設定

let vertex_shader_src = r#"
    #version 140

    in vec2 position;

    void main() {
        gl_Position = vec4(position, 0.0, 1.0);
    }
"#;

let fragment_shader_src = r#"
    #version 140

    out vec4 color;

    void main() {
        color = vec4(1.0, 0.0, 0.0, 1.0);
    }
"#;


6 shderのリンク

let program = glium::Program::from_source(&display, vertex_shader_src,
    fragment_shader_src, None).unwrap();

shaderプログラムのリンクには、Program文を使用します。
最後の引数Noneは、geometry shaderを使用しないことを示しています。


7 windowのクリア、描画、終了

let mut target = display.draw();
    target.clear_color(0.0, 0.0, 1.0, 1.0);
    target.draw(&vertex_buffer, &indices, &program, &glium::uniforms::EmptyUniforms,
                &Default::default()).unwrap();
    target.finish().unwrap();

display.draw()で、描画用フレームバッファ(バックバッファ)を生成します。
target.draw()で、フレームバッファに描画します。その際、VertexBuffer, IndexBuffer,
Programを設定します。
この例では、uniform変数を使用していないので、EmptyUniformsを設定しています。
また、depth、stencil等の描画パラメータも使用していないので、DrawParametersの
設定にはDefaule::default()を設定しています。
target.finish()で、フロントバッファに描画します。


8 windowのクローズ

for ev in display.poll_events() {
    match ev {
        glium::glutin::Event::Closed => return,
        _ => ()
    }
}

display.poll_events()で、ウィンドウのイベントリストを生成します。
「glium::glutin::Event::Closed => return」文で、ウィンドウのClosedイベントを
検出するとreturn文を用いて、ループ(loop{})を抜けるようにしています。