Rust glium Tessellation (3) Teapot
glium テッセレーション
Teapot ( Phong Model )
実行結果
tessellation level = 2
tessellation level = 4
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を有効にする方法
- OpenGL contextを生成する文で、「.with_depth_buffer(24)」を指定します。
- 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, ¶ms).unwrap();
この例では、uniform変数と描画のパラメータ(DrawParameters)を使用して
います。
これらは、target.draw()文で、定義する変数束縛(uniformsとparams)を
設定します。
プロブラム(http://yahoo.jp/box/j6YVBz)