Я пытаюсь отобразить текст в моем приложении, используя атлас глифа, представленный в виде текстуры поля со знаком, то есть текстуры, которая хранит расстояние до ближайшего контура в каждом пикселе.
Эта текстура поля расстояния генерируется из исходной текстуры атласа двоичного глифа (0 — вне контура глифа, 1 — внутри контура глифа) с помощью этого алгоритма, который ищет инкрементный радиус вокруг каждого пикселя в текстуре до пикселя с противоположным состоянием. найден, затем сохраняет расстояние, на котором был найден этот пиксель. Позже все расстояния отображаются между 0 и 1.
//set size of signed distance field atlas
atlas.width = binaryAtlasWidth* pDistanceFieldResolution;
atlas.height = binaryAtlasHeight * pDistanceFieldResolution;
const unsigned int atlasPixelCount = (atlas.width * atlas.height);
atlas.buffer.resize(atlasPixelCount);
//temporary buffer for the distances of each pixel
std::vector<float> unmappedBuffer;
unmappedBuffer.resize(atlasPixelCount);
//support multisampling
unsigned int samplesPerOutPixel = ceil(1.0 / pDistanceFieldResolution);
//for mapping the values between 0 and 1 later
float maxDistance = 0.0;
float minDistance = 0.0;
for (unsigned int outPixel = 0; outPixel < atlasPixelCount; ++outPixel) {
//coordinate of the input sample
unsigned int outPixelX = outPixel%atlas.width;
unsigned int outPixelY = outPixel/atlas.width;
float distanceSum = 0.0f;
for (unsigned int sampleY = 0; sampleY < samplesPerOutPixel; ++sampleY) {
for (unsigned int sampleX = 0; sampleX < samplesPerOutPixel; ++sampleX) {
glm::uvec2 sampleCoord = glm::uvec2(outPixelX * samplesPerOutPixel+ sampleX, outPixelY * samplesPerOutPixel+ sampleY);
unsigned int samplePos = sampleCoord.x + sampleCoord.y*binaryAtlasWidth;
unsigned char sampleVal = buffer[samplePos];
//inital distance is maximum search radius(outside of glyph)
float dist = spread;
int found = 0;
unsigned int rad = 0;
while(!found && (rad*(!sampleVal)) < spread_pixels) {
//if sampleVal is 1(inside), search until found
float radius = (float)rad + 1.0f;
unsigned int compareCount = round(2.0f*radius*M_PI);
float step = 1.0 / (float)compareCount;
for (unsigned int t = 0; t < compareCount && !found; ++t) {
float theta = step*(float)t*360.0f;
glm::vec2 compareLocalCoord = glm::vec2(std::cos(theta), std::sin(theta))*radius;
glm::uvec2 compareCoord = sampleCoord + glm::uvec2(compareLocalCoord);
int comparePos = compareCoord.x + compareCoord.y*binaryAtlasWidth;
if (compareCoord.x >= 0 && compareCoord.x < binaryAtlasWidth&& compareCoord.y >= 0 && compareCoord.y < binaryAtlasHeight) {
unsigned char compareVal = buffer[comparePos];
if (compareVal != sampleVal ) {
float distance = sqrt(pow(compareLocalCoord.x, 2) + pow(compareLocalCoord.y, 2));
found = 1;
dist = std::min(distance * (1 - (sampleVal * 2)) , dist) ;
}
}
}
++rad;
}
distanceSum += dist;
}
}
float avgDistance = distanceSum / (float)(samplesPerOutPixel*samplesPerOutPixel);
printf("pixel %i of %i has %f distance\n", outPixel, atlasPixelCount, avgDistance);
unmappedBuffer[outPixel] = avgDistance;
maxDistance = std::max(maxDistance, avgDistance);
minDistance = std::min(minDistance, avgDistance);
}
minDistance *= -1.0;
float diff = maxDistance + minDistance;
//map all values between 0 and 255
for(unsigned int p = 0; p < atlasPixelCount; ++p) {
float toMap = unmappedBuffer[p];
float mappedDistance = 1.0f - (toMap + minDistance) / diff;
atlas.buffer[p] = mappedDistance * 255;
}
этот алгоритм создает эти результаты:
Входная текстура 266 x 183
Результат SDF без понижающей дискретизации (по-прежнему 266 x 183)
Результат SDF с понижением частоты дискретизации (106 x 73)
Отображать результаты при включенном альфа-тестировании (проходить, когда альфа-значение больше 0,5):
без понижающей дискретизации, ближайшая фильтрация
пониженная дискретизация, ближайшая фильтрация
без понижающей дискретизации, линейная фильтрация
пониженная дискретизация, линейная фильтрация
Я имею в виду, я добираюсь туда, но на самом деле я ожидал точных краев, как показано на клапаны бумажные. Что мне не хватает для точных краев?
PS: мой фрагментный шейдер в настоящее время использует только значение текстуры расстояния как альфа-значение. (color = vec4(1, 1, 1, distance);
)
Задача ещё не решена.
Других решений пока нет …