Я делаю растрирующий процессор программного обеспечения, и я столкнулся с чем-то вроде препятствия: Я, может казаться, не заставляю корректное перспективой отображение текстур работать.
Мой алгоритм должен сначала отсортировать координаты для графического изображения y
. Это возвращает самое высокое, самое низкое и центральную точку. Я затем иду через строки развертки с помощью дельты:
// ordering by y is put here
order[0] = &a_Triangle.p[v_order[0]];
order[1] = &a_Triangle.p[v_order[1]];
order[2] = &a_Triangle.p[v_order[2]];
float height1, height2, height3;
height1 = (float)((int)(order[2]->y + 1) - (int)(order[0]->y));
height2 = (float)((int)(order[1]->y + 1) - (int)(order[0]->y));
height3 = (float)((int)(order[2]->y + 1) - (int)(order[1]->y));
// x
float x_start, x_end;
float x[3];
float x_delta[3];
x_delta[0] = (order[2]->x - order[0]->x) / height1;
x_delta[1] = (order[1]->x - order[0]->x) / height2;
x_delta[2] = (order[2]->x - order[1]->x) / height3;
x[0] = order[0]->x;
x[1] = order[0]->x;
x[2] = order[1]->x;
И затем мы представляем от order[0]->y
кому: order[2]->y
, увеличение x_start
и x_end
дельтой. При рендеринге верхней части дельта x_delta[0]
и x_delta[1]
. При рендеринге нижней части дельта x_delta[0]
и x_delta[2]
. Затем мы линейно интерполируем между x_start и x_end на нашей строке развертки. Ультрафиолетовые координаты интерполированы таким же образом, заказаны y, запускающийся в начинаются и заканчиваются, к которой дельте применяются каждый шаг.
Это хорошо работает кроме тех случаев, когда я пытаюсь сделать, перспектива исправляет ультрафиолетовое отображение. Основной алгоритм состоит в том, чтобы взять UV/z
и 1/z
для каждой вершины и интерполируют между ними. Для каждого пикселя ультрафиолетовая координата становится UV_current * z_current
. Однако это - результат:
inversed часть говорит Вам, где дельта зеркально отражается. Как Вы видите, эти два треугольника, оба, кажется, идут к различным точкам в горизонте.
Вот то, что я использую для вычисления Z в точке в пространстве:
float GetZToPoint(Vec3 a_Point)
{
Vec3 projected = m_Rotation * (a_Point - m_Position);
// #define FOV_ANGLE 60.f
// static const float FOCAL_LENGTH = 1 / tanf(_RadToDeg(FOV_ANGLE) / 2);
// static const float DEPTH = HALFHEIGHT * FOCAL_LENGTH;
float zcamera = DEPTH / projected.z;
return zcamera;
}
Действительно ли я прав, действительно ли это - буферная проблема z?
После долгих размышлений и экспериментов у меня есть ответ!
Сначала мы добавляем 4-ю точку к каждому треугольнику, чтобы сделать их тетраэдрами с объемным центроидом. Мы рассчитываем объемы и центры масс и умножаем их друг на друга, чтобы получить наши моменты. Мы суммируем моменты и разделим на общий объем, чтобы получить наш общий центроид.
Мы рассчитываем объемы, используя детерминированный метод, показанный здесь (уравнение 32): http://mathworld.wolfram.com/Tetrahedron.html
Центроиды каждого из тетраэдров являются просто средним значением точек 4.
Хитрость заключается в том, что из-за способа создания STL-файла треугольники имеют нормаль, которая указывает наружу от поверхности детали, следуя правому правилу 3 вершин, используемых для создания треугольника. мы можем использовать это в своих интересах, позволяя нам иметь согласованное соглашение, в котором можно определить, должен ли объем тетраэдра быть добавлен или вычтен из нашей чистой части (это связано с тем, что выбранная нами опорная точка может не обязательно находиться внутри детали, и общая деталь не обязательно является выпуклой, однако является замкнутым объектом).
Используя метод определения для вычисления объема, первые три точки координат будут представлять три точки нашего треугольника. Четвертый пункт будет нашим общим происхождением. Если нормаль создана треугольником (следуя правому правилу, идущему из пункта 1 , 2 , 3) указывает на нашу общую точку отсчета, этот объем будет рассчитан как не часть нашего общего твердого тела, или отрицательный объем (указывая на, я имею в виду вектор, созданный нормалью треугольника, указывает на ту же сторону, что и нормальная плоскость, созданная вектором от нашей опорной точки до центроида тетраэдра). Если вектор направлен в сторону от опорной точки, то это положительный объём или внутри детали. Если он нормальный, то объём переходит в ноль, так как треугольник находится в той же плоскости, что и опорная точка.
Нам не нужно беспокоиться о том, чтобы на самом деле отслеживать что-либо из этого, как если бы мы были совместимы с нашими входами (как в треугольниках следовать правому правилу с нормальным лицом наружу от части), определение даст нам правильный знак.
В любом случае, рассматривает код (его даже более простой, чем объяснение).
class data // 3 vertices of each triangle
{
public:
float x1,y1,z1;
float x2,y2,z2;
float x3,y3,z3;
};
int main ()
{
int numTriangles; // pull in the STL file and determine number of triangles
data * triangles = new triangles [numTriangles];
// fill the triangles array with the data in the STL file
double totalVolume = 0, currentVolume;
double xCenter = 0, yCenter = 0, zCenter = 0;
for (int i = 0; i < numTriangles; i++)
{
totalVolume += currentVolume = (triangles[i].x1*triangles[i].y2*triangles[i].z3 - triangles[i].x1*triangles[i].y3*triangles[i].z2 - triangles[i].x2*triangles[i].y1*triangles[i].z3 + triangles[i].x2*triangles[i].y3*triangles[i].z1 + triangles[i].x3*triangles[i].y1*triangles[i].z2 - triangles[i].x3*triangles[i].y2*triangles[i].z1) / 6;
xCenter += ((triangles[i].x1 + triangles[i].x2 + triangles[i].x3) / 4) * currentVolume;
yCenter += ((triangles[i].y1 + triangles[i].y2 + triangles[i].y3) / 4) * currentVolume;
zCenter += ((triangles[i].z1 + triangles[i].z2 + triangles[i].z3) / 4) * currentVolume;
}
cout << endl << "Total Volume = " << totalVolume << endl;
cout << endl << "X center = " << xCenter/totalVolume << endl;
cout << endl << "Y center = " << yCenter/totalVolume << endl;
cout << endl << "Z center = " << zCenter/totalVolume << endl;
}
Чрезвычайно быстрое вычисление центров масс для файлов STL.
-121--3909862-Вы можете начать с просмотра trie . Хотя они в основном используются в качестве деревьев префиксов, сама структура данных может быть адаптирована для более быстрого общего поиска.
-121--4690918-ZBuffer не имеет к этому никакого отношения.
THe ZBuffer полезен только в том случае, если треугольники перекрываются и необходимо убедиться, что они нарисованы правильно (например, правильно упорядочены в Z). ZBuffer для каждого пикселя треугольника определяет, находится ли ранее размещенный пиксель ближе к камере, и если да, то не рисует пиксель треугольника.
Так как вы - треугольники рисунка 2, которые не накладываются, это не может быть проблемой.
я сделал растрирующий процессор программного обеспечения в фиксированной точке однажды (для мобильного телефона), но у меня нет источников на моем ноутбуке. Таким образом позволить мне проверить сегодня вечером, как я сделал это. В сущности то, что вы имеете, не плохо! Вещь как это могла быть вызвана очень небольшой ошибкой
Общие советы в отладке, это должно иметь несколько тестовых треугольников (наклонная левая сторона, наклонная правая сторона, углы на 90 градусов, и т.д. и т.д.) и шаг через нее с отладчиком и видеть, как ваша логика имеет дело с чехлами.
РЕДАКТИРОВАНИЕ:
peudocode моего растрирующего процессора (только U, V и Z приняты во внимание..., если вы также хотите сделать gouraud, также необходимо сделать все для R G и B подобный относительно того, что вы делаете для U и V и Z:
идея состоит в том, что треугольник может быть сломан на 2 части. Верхняя часть и нижняя часть. Вершина от y [0] к y[1], и нижняя часть от y[1] до y[2]. Для обоих наборов необходимо вычислить переменные шага, с которыми вы интерполируете. Ниже примера показывает вам, как сделать верхнюю часть. В случае необходимости я могу предоставить нижнюю часть также.
Обратите внимание на то, что я действительно уже вычисляю необходимые смещения интерполяции для нижней части в ниже фрагмента 'псевдокода'
фрагмент кода:
if (leftDeltaX < rightDeltaX)
{
leftDeltaX2 = (x[2]-x[1]) / (y[2]-y[1])
rightDeltaX2 = rightDeltaX
leftDeltaU = (u[1]-u[0]) / (y[1]-y[0]) //for texture mapping
leftDeltaU2 = (u[2]-u[1]) / (y[2]-y[1])
leftDeltaV = (v[1]-v[0]) / (y[1]-y[0]) //for texture mapping
leftDeltaV2 = (v[2]-v[1]) / (y[2]-y[1])
leftDeltaZ = (z[1]-z[0]) / (y[1]-y[0]) //for texture mapping
leftDeltaZ2 = (z[2]-z[1]) / (y[2]-y[1])
}
else
{
swap(leftDeltaX, rightDeltaX);
leftDeltaX2 = leftDeltaX;
rightDeltaX2 = (x[2]-x[1]) / (y[2]-y[1])
leftDeltaU = (u[2]-u[0]) / (y[2]-y[0]) //for texture mapping
leftDeltaU2 = leftDeltaU
leftDeltaV = (v[2]-v[0]) / (y[2]-y[0]) //for texture mapping
leftDeltaV2 = leftDeltaV
leftDeltaZ = (z[2]-z[0]) / (y[2]-y[0]) //for texture mapping
leftDeltaZ2 = leftDeltaZ
}
фрагмент кода:
float tv = startV / startZ
float tu = startU / startZ;
tv %= texturePitch; //make sure the texture coordinates stay on the texture if they are too wide/high
tu %= texturePitch; //I'm assuming square textures here. With fixed point you could have used &=
unsigned int *textPtr = textureBuf+tu + (tv*texturePitch); //in case of fixedpoints one could have shifted the tv. Now we have to multiply everytime.
int destColTm = *(textPtr); //this is the color (if we only use texture mapping) we'll be needing for the pixel
} конца y-контура
//это конец первой части. Теперь мы нарисовали половину треугольника. от верхней до средней координаты Y. //Мы сейчас в основном делаем точно то же самое, но теперь для нижней половины треугольника (с использованием другого набора интерполяторов)
извините по поводу «фиктивных линий».. они были необходимы для синхронизации кодов уценки. (заняло время, чтобы все выглядело так, как задумано)
дайте мне знать, если это поможет вам решить проблему, с которой вы сталкиваетесь!
Если вы интерполируете 1 / z
, вам нужно умножить UV / Z
Z
, не 1 / Z
. Предполагая, что у вас есть это:
UV = UV_CURRENT * Z_CURRENT
и Z_CURRENT
интерполяция 1 / z
, вы должны изменить его на:
UV = UV_CURRENT / Z_CURRENT
И тогда вы можете переименовать z_current
к чему-то вроде one_over_z_current
.
Я не знаю, что могу помочь с вашим вопросом, но одним из лучших книг по рендерингу программного обеспечения, что я прочитал в то время, доступен в Интернете Черная книга графики Майклом Абраш.