Цифровой дифференциальный анализатор с алгоритмом Ву в OpenGL
Я пытаюсь создать алгоритм, который рисует линии, используя DDA (цифровой дифференциальный анализатор), который также использует алгоритм Ву в качестве сглаживания.
Проблема в том, что результат выглядит не очень хорошо. Особенно:
- цвет, который я выбираю, он меняет (я знаю почему, но я хочу знать, должен ли он быть таким)
- цвет пикселя ярче - ярче
Как я могу выбрать цвет, который я хочу для линии? Учитывая, что на это влияет алгоритм?
Вот код:
void dda(int x0, int y0, int x1, int y1, int z, float red, float green, float blue) {
float dy = y1-y0;
float dx = x1-x0;
float m = dy/dx;
if (m<=1) {
int x;
float y;
y = y0;
for (x=x0; x<x1; x++) {
pixel(x, round(y), z, frame, rfpart(red), rfpart(green), rfpart(blue));
pixel(x, round(y)+1, z, frame, fpart(red), fpart(green), fpart(blue));
y = y+m;
}
}
}
int round(float d) {
return floor(d + 0.5);
}
float fpart(float x) {
if (x < 0)
return 1 - (x - floor(x));
return x - floor(x);
}
float rfpart(float x) {
return 1 - fpart(x);
}
2 ответа
ваш код работает только для первого октанта
так что я надеюсь, что вы тестируете только там
Вы забыли смешать цвет фона и цвет линий
поэтому добавьте прозрачность или прочитайте фоновый пиксель напрямую и смешайте цвета самостоятельно.
a,a0
Коэффициенты будут альфа для прозрачности смешения цветов. В этом случае вы не должны менятьr,g,b
значения, но альфа только вместо этого. Также, если вы знаете цвет фона, вы можете игнорировать считывание пикселей, но результат будет немного при пересечении чего-то уже визуализированного.
Я изменяю ваш код, чтобы он был совместим с моим кодированием в C++:
void DDA_line_antialiasing(int x0,int y0,int x1,int y1,int col) // DDA antialiasing
{
int x,y,dx,dy,kx,ky,f,df;
DWORD a,a0;
union
{
DWORD dd;
BYTE db[4];
} c,c0;
dx=x1-x0; kx=0; if (dx>0) kx=+1; else if (dx<0) { kx=-1; dx=-dx; }
dy=y1-y0; ky=0; if (dy>0) ky=+1; else if (dy<0) { ky=-1; dy=-dy; }
if (dx+dy==0)
{
pnt(x0,y0,col);
pnt(x1,y1,col);
return;
}
if (dx>=dy)
for (df=(dy<<8)/dx,x=x0,y=y0,f=0;;f+=df,x+=kx)
{
// fixed point y step
if (f>=256) { f-=256; y+=ky; }
// line color + background color mixing
c.dd=col; c0.dd=pnt(x,y); a=256-f; a0=f;
c.db[0]=DWORD(((DWORD(c.db[0])*a)+(DWORD(c0.db[0])*a0))>>8);
c.db[1]=DWORD(((DWORD(c.db[1])*a)+(DWORD(c0.db[1])*a0))>>8);
c.db[2]=DWORD(((DWORD(c.db[2])*a)+(DWORD(c0.db[2])*a0))>>8);
pnt(x,y ,c.dd);
// line color + background color mixing
c.dd=col; c0.dd=pnt(x,y+ky); a=f; a0=256-f;
c.db[0]=DWORD(((DWORD(c.db[0])*a)+(DWORD(c0.db[0])*a0))>>8);
c.db[1]=DWORD(((DWORD(c.db[1])*a)+(DWORD(c0.db[1])*a0))>>8);
c.db[2]=DWORD(((DWORD(c.db[2])*a)+(DWORD(c0.db[2])*a0))>>8);
pnt(x,y+ky,c.dd);
if (x==x1) break;
}
else
for (df=(dx<<8)/dy,x=x0,y=y0,f=0;;f+=df,y+=ky)
{
// fixed point x step
if (f>=256) { f-=256; x+=kx; }
// line color + background color mixing
c.dd=col; c0.dd=pnt(x,y); a=256-f; a0=f;
c.db[0]=DWORD(((DWORD(c.db[0])*a)+(DWORD(c0.db[0])*a0))>>8);
c.db[1]=DWORD(((DWORD(c.db[1])*a)+(DWORD(c0.db[1])*a0))>>8);
c.db[2]=DWORD(((DWORD(c.db[2])*a)+(DWORD(c0.db[2])*a0))>>8);
pnt(x,y ,c.dd);
// line color + background color mixing
c.dd=col; c0.dd=pnt(x+kx,y); a=f; a0=256-f;
c.db[0]=DWORD(((DWORD(c.db[0])*a)+(DWORD(c0.db[0])*a0))>>8);
c.db[1]=DWORD(((DWORD(c.db[1])*a)+(DWORD(c0.db[1])*a0))>>8);
c.db[2]=DWORD(((DWORD(c.db[2])*a)+(DWORD(c0.db[2])*a0))>>8);
pnt(x+kx,y,c.dd);
if (y==y1) break;
}
}
изменено на фиксированную точку (8-битная дробная часть) f,df
изменил round
в floor
(мои пиксели уже сдвинуты вдвое)
добавлено смешение цветов с цветом фонаpnt(x,y,col);
рисует пиксель x,y
с цветом col
col=pnt(x,y);
считывает пиксель с экрана / изображения в col
col
это 32-битный цвет (0x00RRGGBB), что объединение есть просто r,g,b
доступ
Вот полный исходный код, который у меня работает, с VCL (Embarcadero). Я помещаю изменения ниже, чтобы исправить проблему с длинными линиями, которые делали ошибки положения на чертеже x1,y1 (pb обнаружен путем сравнения со стандартной линией Брезенхема, MoveTo() и LineTo):
- Измените тип переменных с «int» на «float»,
- Приведение значения dy к int в циклах.
Также добавлены некоторые лямбда-выражения и используются типы C++ для 32 и 8 бит. Тесты скорости не проводились(double
иногда быстрее, чемfloat
).
Скорость меня устраивает, так как я работаю в растровом буфере; это необходимо сделать, потому что рисование непосредственно на компоненте слишком медленное, в отличие от рисования в памяти (pC ниже — указатель на растровое полотно). Затем все растровое изображение переносится в Paintbox. Спасибо участникам, @giogix и @Spektre.
void aaLine(int x0, int y0, int x1, int y1, int col)
{
auto pnt = [&](int x, int y, uint32_t c){
pC->Pixels[x][y] = static_cast<TColor>(c);
};//-----------------------------
auto pix_color = [&](uint32_t x, uint32_t y){
return pC->Pixels[x][y];
};//-----------------------------
float x, y, dx, dy, kx, ky, f, df;//Changed 'int' into 'float' to avoid error on x1,y1 drawing
uint32_t a,a0;
union {
uint32_t dd;
BYTE db[4];
} c,c0;
auto mix_colors = [&](){
c.db[0]= uint32_t(( uint32_t(c.db[0])*a + uint32_t(c0.db[0])*a0)>>8);
c.db[1]= uint32_t(( uint32_t(c.db[1])*a + uint32_t(c0.db[1])*a0)>>8);
c.db[2]= uint32_t(( uint32_t(c.db[2])*a + uint32_t(c0.db[2])*a0)>>8);
};
dx=x1-x0; kx=0; if (dx>0) kx=+1; else if (dx<0) { kx=-1; dx=-dx; }
dy=y1-y0; ky=0; if (dy>0) ky=+1; else if (dy<0) { ky=-1; dy=-dy; }
if (dx+dy==0) {
pnt(x0,y0,col);
pnt(x1,y1,col);
return;
}
if (dx>=dy)
for (df=(int(dy)<<8)/dx, x=x0, y=y0, f=0; ; f+=df, x+=kx) //Put int(dy)
{
// fixed point y step
if (f>=256) { f-=256; y+=ky; }
// line color + background color mixing
c.dd=col; c0.dd=pix_color(x,y); a=256-f; a0=f;
mix_colors();
pnt(x,y ,c.dd);
// line color + background color mixing
c.dd=col; c0.dd=pix_color(x,y+ky); a=f; a0=256-f;
mix_colors();
pnt(x,y+ky,c.dd);
if (x==x1) break;
}
else
for (df=(int(dx)<<8)/dy, x=x0, y=y0, f=0; ; f+=df, y+=ky) //Put int(dx)
{
// fixed point x step
if (f>=256) { f-=256; x+=kx; }
// line color + background color mixing
c.dd=col; c0.dd=pix_color(x,y); a=256-f; a0=f;
mix_colors();
pnt(x,y ,c.dd);
// line color + background color mixing
c.dd=col; c0.dd=pix_color(x+kx,y); a=f; a0=256-f;
mix_colors();
pnt(x+kx,y,c.dd);
if (y==y1) break;
}
}