c++ - 吴晓林圆算法渲染内有孔的圆
问题描述
我已经从这里实现了小林吴圆算法:https ://create.stephan-brumme.com/antialiased-circle/ in c++:
float radiusX = endRadius;
float radiusY = endRadius;
float radiusX2 = radiusX * radiusX;
float radiusY2 = radiusY * radiusY;
float maxTransparency = 127;
float quarter = roundf(radiusX2 / sqrtf(radiusX2 + radiusY2));
for(float _x = 0; _x <= quarter; _x++) {
float _y = radiusY * sqrtf(1 - _x * _x / radiusX2);
float error = _y - floorf(_y);
float transparency = roundf(error * maxTransparency);
int alpha = transparency;
int alpha2 = maxTransparency - transparency;
setPixel4(x, y, _x, floorf(_y), r, g, b, alpha, data, areasData, false);
setPixel4(x, y, _x, floorf(_y) - 1, r, g, b, alpha2, data, areasData, false);
}
quarter = roundf(radiusY2 / sqrtf(radiusX2 + radiusY2));
for(float _y = 0; _y <= quarter; _y++) {
float _x = radiusX * sqrtf(1 - _y * _y / radiusY2);
float error = _x - floorf(_x);
float transparency = roundf(error * maxTransparency);
int alpha = transparency;
int alpha2 = maxTransparency - transparency;
setPixel4(x, y, floorf(_x), _y, r, g, b, alpha, data, areasData, false);
setPixel4(x, y, floorf(_x) - 1, _y, r, g, b, alpha2, data, areasData, false);
}
x, y 是圆心的坐标。
在我看来,它看起来不错:
但是,我需要填充圆圈。也许我错了,但我开发了一个简单的算法:从 1 迭代到半径,然后画一个圆。它看起来像这样:
奇怪的。所以,为了解决这个问题,我还将透明度设置为最大,直到我到达最后一个半径(所以它是一个外圆):
如您所见,外层和其他层之间存在奇怪的孔。我试过制作两个外层和类似的东西,但没有得到正确的结果。
这是代码的最终版本:
for(int cradius = startRadius; cradius <= endRadius; cradius++) {
bool last = cradius == endRadius;
float radiusX = cradius;
float radiusY = cradius;
float radiusX2 = radiusX * radiusX;
float radiusY2 = radiusY * radiusY;
float maxTransparency = 127;
float quarter = roundf(radiusX2 / sqrtf(radiusX2 + radiusY2));
for(float _x = 0; _x <= quarter; _x++) {
float _y = radiusY * sqrtf(1 - _x * _x / radiusX2);
float error = _y - floorf(_y);
float transparency = roundf(error * maxTransparency);
int alpha = transparency;
int alpha2 = maxTransparency - transparency;
if(!last) {
alpha = maxTransparency;
alpha2 = maxTransparency;
}
setPixel4(x, y, _x, floorf(_y), r, g, b, alpha, data, areasData, false);
setPixel4(x, y, _x, floorf(_y) - 1, r, g, b, alpha2, data, areasData, false);
}
quarter = roundf(radiusY2 / sqrtf(radiusX2 + radiusY2));
for(float _y = 0; _y <= quarter; _y++) {
float _x = radiusX * sqrtf(1 - _y * _y / radiusY2);
float error = _x - floorf(_x);
float transparency = roundf(error * maxTransparency);
int alpha = transparency;
int alpha2 = maxTransparency - transparency;
if(!last) {
alpha = maxTransparency;
alpha2 = maxTransparency;
}
setPixel4(x, y, floorf(_x), _y, r, g, b, alpha, data, areasData, false);
setPixel4(x, y, floorf(_x) - 1, _y, r, g, b, alpha2, data, areasData, false);
}
}
我怎样才能解决这个问题?
编辑:
因为我不能使用洪水填充来填充圆圈(我绘制的区域可能不是单色背景,我需要混合这些颜色)我已经实现了将点与线连接起来的简单方法:
我在 setPixel4 方法中添加了 2 个 drawLine 调用:
void setPixel4(int x, int y, int deltaX, int deltaY, int r, int g, int b, int a, unsigned char* data, unsigned char* areasData, bool blendColor) {
drawLine(x - deltaX, y - deltaY, x + deltaX, y + deltaY, r, g, b, 127, data, areasData); //maxTransparency
drawLine(x + deltaX, y - deltaY, x - deltaX, y + deltaY, r, g, b, 127, data, areasData); //maxTransparency
setPixelWithCheckingArea(x + deltaX, y + deltaY, r, g, b, a, data, areasData, blendColor);
setPixelWithCheckingArea(x - deltaX, y + deltaY, r, g, b, a, data, areasData, blendColor);
setPixelWithCheckingArea(x + deltaX, y - deltaY, r, g, b, a, data, areasData, blendColor);
setPixelWithCheckingArea(x - deltaX, y - deltaY, r, g, b, a, data, areasData, blendColor);
}
它看起来与第三张图片完全相同。我认为里面的这些白色像素是由外圈(来自小林吴算法)本身引起的。
编辑2:
感谢@JaMiT,我改进了我的代码,它适用于一个圈子,但是当我有更多的相互重叠时会失败。首先,新代码:
void drawFilledCircle(int x, int y, int startRadius, int endRadius, int r, int g, int b, int a, unsigned char* data, unsigned char* areasData, int startAngle, int endAngle, bool blendColor) {
assert(startAngle <= endAngle);
assert(startRadius <= endRadius);
dfBufferCounter = 0;
for(int i = 0; i < DRAW_FILLED_CIRCLE_BUFFER_SIZE; i++) {
drawFilledCircleBuffer[i] = -1;
}
for(int cradius = endRadius; cradius >= startRadius; cradius--) {
bool last = cradius == endRadius;
bool first = cradius == startRadius && cradius != 0;
float radiusX = cradius;
float radiusY = cradius;
float radiusX2 = radiusX * radiusX;
float radiusY2 = radiusY * radiusY;
float maxTransparency = 127;
float quarter = roundf(radiusX2 / sqrtf(radiusX2 + radiusY2));
for(float _x = 0; _x <= quarter; _x++) {
float _y = radiusY * sqrtf(1 - _x * _x / radiusX2);
float error = _y - floorf(_y);
float transparency = roundf(error * maxTransparency);
int alpha = last ? transparency : maxTransparency;
int alpha2 = first ? maxTransparency - transparency : maxTransparency;
setPixel4(x, y, _x, floorf(_y), r, g, b, alpha, cradius, endRadius, data, areasData, blendColor);
setPixel4(x, y, _x, floorf(_y) - 1, r, g, b, alpha2, cradius, endRadius, data, areasData, blendColor);
}
quarter = roundf(radiusY2 / sqrtf(radiusX2 + radiusY2));
for(float _y = 0; _y <= quarter; _y++) {
float _x = radiusX * sqrtf(1 - _y * _y / radiusY2);
float error = _x - floorf(_x);
float transparency = roundf(error * maxTransparency);
int alpha = last ? transparency : maxTransparency;
int alpha2 = first ? maxTransparency - transparency : maxTransparency;
setPixel4(x, y, floorf(_x), _y, r, g, b, alpha, cradius, endRadius, data, areasData, blendColor);
setPixel4(x, y, floorf(_x) - 1, _y, r, g, b, alpha2, cradius, endRadius, data, areasData, blendColor);
}
}
}
如果没有 setPixel4 中的 drawLine 调用,它看起来像这样:
我改进了 setPixel4 方法以避免再次重绘相同的像素:
void setPixel4(int x, int y, int deltaX, int deltaY, int r, int g, int b, int a, int radius, int maxRadius, unsigned char* data, unsigned char* areasData, bool blendColor) {
for(int j = 0; j < 4; j++) {
int px, py;
if(j == 0) {
px = x + deltaX;
py = y + deltaY;
} else if(j == 1) {
px = x - deltaX;
py = y + deltaY;
} else if(j == 2) {
px = x + deltaX;
py = y - deltaY;
} else if(j == 3) {
px = x - deltaX;
py = y - deltaY;
}
int index = (px + (img->getHeight() - py - 1) * img->getWidth()) * 4;
bool alreadyInBuffer = false;
for(int i = 0; i < dfBufferCounter; i++) {
if(i >= DRAW_FILLED_CIRCLE_BUFFER_SIZE) break;
if(drawFilledCircleBuffer[i] == index) {
alreadyInBuffer = true;
break;
}
}
if(!alreadyInBuffer) {
if(dfBufferCounter < DRAW_FILLED_CIRCLE_BUFFER_SIZE) {
drawFilledCircleBuffer[dfBufferCounter++] = index;
}
setPixelWithCheckingArea(px, py, r, g, b, a, data, areasData, blendColor);
}
}
}
然后,最后:
这几乎是完美的。然而,我挣扎了很多时间来摆脱这个白色的轮廓,但我做不到。
解决方案
因为您将圆离散化,所以必然会丢失一些像素。您获得的图片显示了众所周知的莫尔效应。
最好的解决方案是使用任何洪水填充算法或合成将在相同水平线(或垂直线,如果您愿意)上的圆点之间画线的简单算法。
推荐阅读
- python - 根据创建的列按两列分组
- mongodb - 如何按猫鼬中相关对象的数量排序
- vim - vim 使用 vim-surround 更改多行 html 标签
- django - 在 Django 中创建新用户后如何自动创建新配置文件?
- azure - 为什么我的同意管理员权限请求要求所有权限?
- python - 如果不在条件内,程序将永远运行
- c# - 如何在不为每种情况使用 if 和 switch 语句的情况下重构链中的数百个条件?
- python - AWS Elastic Inference,Tensorflow 2 - 加速器没有变量支持,退出
- python - Python - 如果我增加列表的大小,为什么我的 for 循环的长度函数不会改变
- c# - 数据库和数据集中日期时间格式的区别