PDA

View Full Version : Trouble using tessellation to draw cubic Bézier curves



ehoops
06-02-2016, 09:57 AM
Hey,

as stated in the topic title, I'm trying to render cubic Bézier curves using a tessellation shader, but can't identify the problem(s) that are causing nothing at all to be drawn (or at least, seen) on the screen. Also, on my Radeon HD 5770, the program segfaults on glDrawArrays.

Basically, the thing I'm trying to accomplish here is to give the program four control points and draw a curve with different tessellation levels based on the zoom level.

Here's the basic OpenGL initialization code I'm using:



#define WIN_W 1280
#define WIN_H 960

glClearColor(0.0, 0.0, 0.0, 1.0);
glEnable(GL_DEPTH_TEST);

glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glEnable(GL_BLEND);

glViewport(0, 0, WIN_W, WIN_H);

glPatchParameteri(GL_PATCH_VERTICES, 4);

<shader loading code> // this shouldn't be the problem here, since it works on a lot of other projects.
// incl. glBindAttribLocation(programID, 0, "Position_VS_in")

float patch_buffer[4 * 2] = { 0.0, 0.0, 0.2, 1.0, 0.8, 1.0, 1.0, 0.0 };

#define ATTRIB_POSITION 0

glGenVertexArrays(1, &VAOid);
glBindVertexArray(VAOid);

glEnableVertexAttribArray(ATTRIB_POSITION);

glGenBuffers(1, &VBOid);
glBindBuffer(GL_ARRAY_BUFFER, VBOid);
glBufferData(GL_ARRAY_BUFFER, 8*sizeof(float), patch_buffer, GL_DYNAMIC_DRAW);

glVertexAttribPointer(ATTRIB_POSITION, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(float), 0);

glBindVertexArray(0);

glDisableVertexAttribArray(ATTRIB_POSITION);


The shaders:



// VERTEX SHADER

#version 400

layout(location = 0) in vec2 Position_VS_in;
out vec3 vPos;
void main() {
vPos = vec3(Position_VS_in, 0.0);
}




// TESSELLATION CONTROL SHADER

#version 400

layout(vertices = 4) out; // 4 points per patch
in vec3 vPos[];
out vec3 tcPos[];
void main() {
tcPos[gl_InvocationID] = vPos[gl_InvocationID];
if(gl_InvocationID == 0) { // levels only need to be set once per patch
gl_TessLevelOuter[0] = 1; // we're only tessellating one line
gl_TessLevelOuter[1] = 100; // tessellate the line into 100 segments
}
}




// TESSELLATION CONTROL SHADER

#version 400

vec3 bezier2(vec3 a, vec3 b, float t) {
return mix(a, b, t);
}
vec3 bezier3(vec3 a, vec3 b, vec3 c, float t) {
return mix(bezier2(a, b, t), bezier2(b, c, t), t);
}
vec3 bezier4(vec3 a, vec3 b, vec3 c, vec3, d, float t) {
return mix(bezier3(a, b, c, t), bezier3(b, c, d, t), t);
}

layout(isolines) in;
in vec3 tcPos[];
uniform mat4 uMVP;

void main() {
float t = gl_TessCoord.x;
vec3 ePos = bezier4(tcPos[0], tcPos[1], tcPos[2], tcPos[3], t);
gl_Position = uMVP * vec4(ePos, 1);
}




// FRAGMENT SHADER

#version 400

out vec4 frag_color;

void main() {
frag_color = vec4(1.0, 1.0, 1.0, 1.0);
}


Drawing code:



glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

glUseProgram(wave_shader->getProgramHandle());

mat4 mvp = mat4::proj_ortho(0.0, WIN_W, WIN_H, 0.0, -1.0, 1.0) * mat4::scale(100, 100, 100);
wave_shader->update_uniform_mat4("uMVP", mvp);

glBindVertexArray(VAOid);
glDrawArrays(GL_PATCHES, 0, 4);

// later: SwapBuffers



I'm not absolutely sure about the TCS/TES part, but at least mathematically speaking, it seems fine as far as I can tell.

Haven't been able to figure out what the problem is. Any pointers on this will be much appreciated!

Thanks.

Alfonse Reinheart
06-02-2016, 10:19 AM
Debug it.

Start by seeing if the non-tessellation parts are working. That is, use the TES to draw a line between the two end-points, using `t` as a simple linear interpolation between them.

If that doesn't work, then you know your problem is somewhere else. Perhaps the position data or matrix is wrong or something. Remove all tessellation and try to get things to where you can draw a line between those points.

If the line does work, try removing the TCS and setting the tessellation levels directly (https://www.opengl.org/wiki/Tessellation#Tessellation_control_shader). And so on.

BTW:



gl_TessLevelOuter[1] = 100; // tessellate the line into 100 segments


There is no hardware (http://opengl.gpuinfo.org/gl_stats_caps_single.php?listreportsbycap=GL_MAX_T ESS_GEN_LEVEL) which permits more than 64 tessellation levels along any one axis. That's not a bug here, since the value will be clamped to the max. I'm just pointing that out so that you don't assume it'll be finer than that.

Which means that, if you want to get more tessellation that that for isolines, then you'll need to use outer tessellation level 0. That is, your TES will then have to convert a two-dimensional tessellation level into a single `t` value. But that's something to think about when you get basic tessellation working.

GClements
06-02-2016, 01:18 PM
vec3 bezier4(vec3 a, vec3 b, vec3 c, vec3, d, float t) {


If this is the actual code (not re-typed or paraphrased), the above will fail to compile because of an extraneous comma between "vec3" and "d".

Other than that, a test program based upon your code works as expected.

ehoops
06-02-2016, 02:20 PM
Thank you guys!

Pointing out the syntax error in the TES was crucial here, as it turned out my TCS/TES weren't being compiled at all, thanks to a total brainfart in my extension handler code. This is why the shader compiler never complained about that syntax error.

Other than that, the code is indeed in a working state - the curve is visible in the upper left corner of the screen.

Now onto other challenges, i.e. how to use tessellation/geometry shaders to produce a neat anti-aliased line with beveled joins :)