Vala プログラミング

Rust プログラミング

おながのブログ

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)