使用圆形纹理绘制 AA 线时遇到问题
我正在 Linux 中开发一个绘图应用程序(类似于 2d cad 应用程序),在使用 OpenGL 时我发现的第一件事就是我的 Intel I5 Core HD GMA(内置)图形硬件不支持 AA 线或多样本 AA 方法。因此,我花了大约一周的时间研究如何使用纹理进行抗锯齿。我偶然发现了 arekkusu 不变纹理 AA 文章,并从中学到了很多东西,但我遇到的问题是,当我使用像许多示例中那样的混合圆时,我的线条会变成非常宽的椭圆。它确实混合正确并且椭圆看起来很棒。 :) 但是,这并不完全是我想要的。看起来纹理没有被水平“涂抹”,而是被拉伸。我最终得到一个椭圆,其高度等于线条的“宽度”,椭圆宽度约为线条长度的 1/2。
与此同时,我尝试了一些稍微不同的方法,并创建了一个 1 像素宽、X 像素高的纹理。正如预期的那样,高度和变化的 alpha 值确实改变了我的线条的结果,但除了非常细的线条之外,我实际上得到了相当好的结果。
我包含了我正在使用的一个小型测试应用程序(GLUT 应用程序)的代码,希望有人能够指出我所犯的愚蠢错误。它使用上面的 1 像素宽纹理,但显然我想用圆形纹理替换它。它并不是最有效的代码,但很容易使用,直到我能解决所有问题。
#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;
}
我已经为 glTexParameterf 调用尝试了不同的选项,例如尝试使用 GL_NEAREST 作为过滤器,使用 GL_REPEAT 作为换行,但这对于圆形结构来说根本不起作用。我还尝试使用 GL_MODULATE 设置(如 OpenGL FAQ 中所示)来执行此类 AA,但这没有什么区别。
任何帮助将不胜感激。谢谢。
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 about 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.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
没有必要一次使用整个纹理。让我们假设您的圆形纹理在 x 轴和 y 轴上具有从 0 到 1.0 的 tex 顶点。当您需要弯曲边缘或圆帽时,可以将纹理的 (0,0) (0,0.5) (0.5,0.5) (0.5,0) 映射到顶点。或者,当您只需要线条时,可以将 (0.5,0) (0.5,0) (0,0.5) (0,0.5) 映射到整个矩形。
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.
其基本实现是绘制 1 个矩形(您的线)和 2 个四边形(您的线末端)。
您将半圆形纹理放在四边形中,并将简单的渐变放在矩形中。
更好的解决方案是绘制一个矩形,并使用线段的长度作为 V 坐标。
在片段着色器中,
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,