SourceForge.net Logo
Home
Documentation
Tutorials
    Beginning
    Gears
    Noise
    Bumpmapping
    Point sprites
    Hello World
    Armature
    Geometryshader
    Toolscripting
    Keyframe animation
    Instancing
    Bezier Surface
    Deferred shading
    Depth peeling
    Particle System
Links
Support This Project
OpenGL
Bezier Surfaces

Bezier surfaces are supported in opengl with evaluators, a more or less deprecated part
of the pipeline. This tutorial descibes a faster and more flexible solution, based on
vertex shaders and instances (requires a GF8), that allows to draw bezier surfaces like
primitives.

This tutorial requires Lumina 0.3.0-r3

The best known bezier object is the teapot, other are the teacup, spoon and the
flowerbox screensaver.

The first step is to create new node with a Buffer.
Console:

Node = World.addNode();
Node.addBuffer("Teapot");

The "Teapots" context menu contains a script named "Bezier Objects" execute it and
select the Teapot and check the "Create Vertexshader" checkbox. The "Teapot" should
store 512 float 3 vectors now. That are 32 bezier surfaces with each 16 controll points.
The script will also create a vertexshader.

Now the other required stuff should be created: A Fragmentshader a script and a Quadric
mesh. Console:

Node.addFragmentShader();
Node.addScript();

The Quadric mesh can be created with the updated "Test Object" script in the Nodes
contextmenu.
Now write the Script:

shader = gl.Shader(Vertexshader,Fragmentshader);
shader.Bind()
Teapot.Uniform(shader,"Bezier");

function render(){
shader.Bind()
Quadric.Vertex.Bind();
Quadric.Index.DrawInstanced(32);
}

and Fragmentshader:

varying vec3 T,B,N;

void main (void){
gl_FragColor.rgb = normalize(N) * 0.5 + 0.5;
}


The Vertexshader should contain code like this:

#extension GL_EXT_gpu_shader4 : enable
uniform vec3 Bezier[512]; //pass the bezier data to this uniform
varying vec3 T,B,N;

void main (void){
vec2 p = gl_Vertex.xy;
vec2 p2 = p * p;
vec2 p3 = p2 * p;

vec2 q = vec2 (1.0, 1.0) - p;
vec2 q2 = q * q;
vec2 q3 = q2 * q;

vec4 wx = vec4(q3.x, 3.0 * q2.x * p.x , 3.0 * p2.x * q.x, p3.x);
vec4 wy = vec4(q3.y, 3.0 * q2.y * p.y , 3.0 * p2.y * q.y, p3.y);

vec4 tx = vec4(q2.x, 2.0 * q.x * p.x - q2.x, p2.x - 2.0 * q.x * p.x, -p2.x);
vec4 ty = vec4(q2.y, 2.0 * q.y * p.y - q2.y, p2.y - 2.0 * q.y * p.y, -p2.y);

vec3 pos = vec3(0.0, 0.0, 0.0);
vec3 tangent = vec3(0.0, 0.0, 0.0);
vec3 bitangent = vec3(0.0, 0.0, 0.0);

for (int x = 0; x < 4 ; x++){
for (int y = 0; y < 4 ; y++){
vec3 point = Bezier[x + y * 4 + gl_InstanceID * 16];
pos += point * wx[x] * wy[y];
bitangent += point * tx[x] * wy[y];
tangent += point * wx[x] * ty[y];
}
}
gl_Position = gl_ModelViewProjectionMatrix * vec4(pos,1.0);
T = gl_NormalMatrix * tangent;
B = gl_NormalMatrix * bitangent;
N = cross(T,B);
gl_TexCoord[0].xy = gl_Vertex.xy;
}


The first 6 lines in main() are required to create the factors for the coefficients.
wx and wy are the coefficients, for the cubic bezier surface.
tx and ty are the tangential coefficients.

The two loops in two dimensions weighting the 4x4 Points and finally the calculated
points and vectors are projected into the ModelViewspace.
The shader creates Normal, Tangent and Bitangent, so that bumpmapping or other
anisotropic lighting models shouldn't be a problem.
The only required per vertex inputs are the texcoords stored in gl_Vertex.

EDIT