Vala プログラミング

Rust & Vala プログラミング

おながのブログ

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],
]; // マトリックス