Rust glium Tessellation (4) 3D surface
glium テッセレーション
David Wolff著「OpenGL 4.0 シェーディング言語 ( OpenGL 4.0 Shading
Language Cookbook )」
第6章「ジオメトリシェーダとテッセレーションシェーダ」
「3Dサーフェイスをテッセレートする」
「シェーディングしたメッシュの上にワイヤフレームを描く」
実行結果
tessellation level = 2
tessellation level = 4
tessellation level = 8
teapot多面体をテッセレーションを使って描画し、シェーディングした
メッシュの上にワイヤフレーム(エッジ)を描きます。
teapotの描画は、前回と同様です。
エッジの描画は次のようにします。
- メッシュ(三角形)の頂点と対辺との距離を求める。
- 描画するフラグメントが、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], ]; // マトリックス