Vala プログラミング

読者です 読者をやめる 読者になる 読者になる

Rust & Vala プログラミング

おながのブログ

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

の式で生成される。