Having problems using circular textures to draw AA lines
I'm working on a drawing application (similar to a 2d cad app) in Linux and one of the first things I discovered while working with OpenGL is that my Intel I5 Core HD GMA (built in) graphics hardware does not support AA lines or Multisample AA methods. So, I've spent the last week or so researching how to do antialiasing by using textures. I came across the arekkusu Invariance Texture AA article and have picked up quite a bit from it, but the problem I'm having is that when I use a blended circle just like those in many examples, my lines come out as really wide ellipses. It does blend correctly and the ellipse looks great. :) However, this isn't quite what I'm looking for. It appears the texture is not being "smeared" horizontally but is instead being stretched instead. I wind up with an ellipse that is the height of the "width" of my line and an elleptical width a开发者_运维技巧bout 1/2 the length of my line.
In the mean time, I've tried something slightly different and created a texture that is 1 pixel wide and X pixels in height. As expected, the height and varying alpha values do change the outcome of my lines but I'm actually getting pretty good results except for very thin lines.
I'm including the code for a small test application I'm using (GLUT app) in hopes that someone can point out the bone-headed mistake I'm making. It is using the above 1 pixel wide texture but obviously I want to replace this by a circular texture instead. It isn't intended to be the most efficient code, but is easy to play with until I can get everything worked out.
#include <GL/glut.h>
#include <stdlib.h>
#include <stdio.h>
float centerX = 320.0;
float centerY = 240.0;
GLuint textureId = 0;
static GLuint createTexture(int width, int height)
{
GLuint id;
glGenTextures( 1, &id );
glBindTexture( GL_TEXTURE_2D, id );
int bpp = 4; // Bytes per pixel
GLubyte* bitmap = new GLubyte[width * height * bpp];
for (int y = 0; y < height; y++) {
printf("%03d: ",y);
for (int x = 0; x < width; x++) {
if ((y == 0) || (y == height - 1)) {
bitmap[y * width * bpp + x + 3] = 21;
} else if ((y == 1) || (y == height - 2)) {
bitmap[y * width * bpp + x + 3] = 80;
} else if ((y == 2) || (y == height - 3)) {
bitmap[y * width * bpp + x + 3] = 166;
} else {
bitmap[y * width * bpp + x + 3] = 255;
}
printf("%03d ", bitmap[y * width * bpp + x + 3]);
// Fill color with white so we can blend in our
// own color when we draw the line.
bitmap[y * width * bpp + x + 0] = 255;
bitmap[y * width * bpp + x + 1] = 255;
bitmap[y * width * bpp + x + 2] = 255;
}
printf("\n");
}
glTexImage2D(GL_TEXTURE_2D, 0, bpp, width, height, 0,
GL_RGBA, GL_UNSIGNED_BYTE, bitmap);
glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
// Doesn't seem to make much difference for how this is currently
// working.
// gluBuild2DMipmaps( GL_TEXTURE_2D, 4, width, height,
// GL_RGBA, GL_UNSIGNED_BYTE, bitmap);
delete [] bitmap;
return id;
}
static void resize(int width, int height)
{
glViewport(0, 0, width, height);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0,width,height,0,1.0,-1.0);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity() ;
centerX = (float)width / 2.0;
centerY = (float)height / 2.0;
}
static void drawLine(float x0, float y0, float x1, float y1, float width)
{
glBindTexture(GL_TEXTURE_2D, textureId);
// Would be faster using arrays, but this is easier to tweak for now
glBegin(GL_QUADS);
glTexCoord2f(0.0, 0.0); glVertex2f( x0, y0);
glTexCoord2f(0.0, 1.0); glVertex2f(x0, y0 + width);
glTexCoord2f(1.0, 1.0); glVertex2f(x1, y1 + width);
glTexCoord2f(1.0, 0.0); glVertex2f(x1, y1);
glEnd();
}
static void display(void)
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
for(float angle = 0.0; angle < 360.0; angle += 2.0) {
glPushMatrix();
glTranslatef(centerX, centerY, 0.0);
glRotatef(angle, 0.0, 0.0, 1.0);
glColor3f(0.0, 0.3, 0.9);
drawLine(0.0, 0.0, centerY, 0.0, 5.0);
glPopMatrix();
}
glFlush();
glutSwapBuffers();
}
int main(int argc, char *argv[])
{
glutInit(&argc, argv);
glutInitWindowSize(640,480);
glutInitWindowPosition(10,10);
glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH);
glutCreateWindow("Texture Antialiasing");
glutReshapeFunc(resize);
glutDisplayFunc(display);
glClearColor(1,1,1,1);
glEnable(GL_BLEND);
glEnable(GL_TEXTURE_2D);
glEnable(GL_COLOR_MATERIAL);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glDisable(GL_DEPTH_TEST);
glDepthFunc(GL_LEQUAL);
// Create a texture 1 pixel wide by 8 pixels high. This
// texture will get stretched horizontally when we
// draw the textured line. Ideally, we would want to use
// a circlular texture here, but the code currently causes
// the line to consist of a very wide ellipse with "width"
// height and a elliptical width of about 1/2 our line length.
textureId = createTexture(1,8);
glutMainLoop();
return EXIT_SUCCESS;
}
I've tried different options for the glTexParameterf calls like trying to use GL_NEAREST for the filters and GL_REPEAT for the wrap, but that didn't work well at all for circle tectures. I've also tried using the GL_MODULATE setting as shown in the OpenGL FAQ for doing this type of AA but that made no difference.
Any help would be much appreciated. Thanks.
It is not necessary that you use the entire texture at once. Let us assume that your circle texture has tex vertices from 0 to 1.0 in x axis and in y axis. When you need the curved edge or a round cap, you could map (0,0) (0,0.5) (0.5,0.5) (0.5,0) of the texture to the vertices. Or when you need just the line strip, you could map (0.5,0) (0.5,0) (0,0.5) (0,0.5) to the entire rect.
The basic implementation for this is to draw 1 rectangle (your line) and 2 quads (you line extremities).
You put the half-circles textures in the quads, and a simple gradient in your rectangle.
A better solution is to draw a rectangle, and use the length of the segment as your V coordinate.
In your fragment shader,
- when V € [0,0.5], use your normal circular texture (left extremity)
- when V € [length-0.5,length], use your normal circular texture, but with v-length+1 (rightextremity)
- else (v € [0.5, length-0.5]) use v=0.5 (the real line segment)
精彩评论