Перспектива корректное отображение текстур; z расчет расстояния могло бы быть неправильным

Я делаю растрирующий процессор программного обеспечения, и я столкнулся с чем-то вроде препятствия: Я, может казаться, не заставляю корректное перспективой отображение текстур работать.

Мой алгоритм должен сначала отсортировать координаты для графического изображения 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. Однако это - результат:

alt text

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?

6
задан tshepang 15 May 2014 в 19:13
поделиться

3 ответа

После долгих размышлений и экспериментов у меня есть ответ!

Сначала мы добавляем 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]. Для обоих наборов необходимо вычислить переменные шага, с которыми вы интерполируете. Ниже примера показывает вам, как сделать верхнюю часть. В случае необходимости я могу предоставить нижнюю часть также.

Обратите внимание на то, что я действительно уже вычисляю необходимые смещения интерполяции для нижней части в ниже фрагмента 'псевдокода'

  • первый порядок провода (x, y, z, u, v) в порядке так, чтобы coord [0].y
  • затем проверил, идентичны ли какие-либо 2 набора координат (только проверяют X и Y). Раз так не тяните
  • исключение: треугольник имеет стрижку под ежика? если так, первый наклон будет бесконечен
  • exception2: делает треугольник, имеют плоскую нижнюю часть (да, треугольники могут иметь их также; ^)) затем последний наклон также будет бесконечен
  • , вычисляют 2 наклона (левая сторона и правая сторона)
    leftDeltaX = (x [1] - x [0]) / (y[1]-y[0]) и rightDeltaX = (x [2] - x [0]) / (y[2]-y[0])
  • вторая часть треугольника вычисляется зависящий от: если левая сторона треугольника находится теперь действительно на leftside (или свопинг потребностей)

фрагмент кода:

 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
  }
  • устанавливает currentLeftX, и currentRightX и на x [0]
  • устанавливают currentLeftU на leftDeltaU, currentLeftV на leftDeltaV и currentLeftZ на leftDeltaZ
  • calc запускаются и конечная точка для первого диапазона Y: startY = перекрывают (y [0]); endY = перекрывают (y[1])
  • предварительный шаг x, u, v и z для дробной части y для субпиксельной точности (я предполагаю, что это также необходимо для плаваний), Для моих fixedpoint алгоритмов это было необходимо для создания строк, и структуры дают иллюзию перемещения в намного более прекрасные шаги затем разрешение дисплея)
  • , вычисляют, где x должен быть в y[1]: halfwayX = (x [2]-x [0]) * (y[1]-y[0]) / (y[2]-y[0]) + x [0] и то же для U и V и z: halfwayU = (u[2]-u[0]) * (y[1]-y[0]) / (y[2]-y[0]) + u [0]
  • и использование halfwayX вычисляют степпер для U и V и z: если (halfwayX - x [1] == 0) {slopeU=0, slopeV=0, slopeZ=0} еще {slopeU = (halfwayU - U[1]) / (halfwayX - x [1])}//(и то же для v и z)
  • делают отсечение для вершины Y (так рассчитать, где мы начнем рисовать в случае, если верхняя часть треугольника находится вне экрана (или с подрезки прямоугольника))
  • для y = startY; y < endY; y++) {
    • Y позади нижней части экрана? прекратить рендеринг!
    • calc startX и endX для первой горизонтальной линии leftCurX = ceil (startx); leftCurY = ceil (endy);
    • подрезать линию к левой горизонтальной границе экрана (или области подрезки)
    • подготовить указатель на буфер назначения (делать это через индексы массива каждый раз слишком медленно) неподписанная int buf = destbuf + (y pitch) + startX; (неподписанная int в случае выполнения 24-битового или 32-битного рендеринга) также подготовьте указатель ZBuffer здесь (если вы используете это)
    • для (x = startX; x < endX; x++) {
      • теперь для перспективного отображения текстуры (без интерполяции bilineair вы делаете следующее):

фрагмент кода:

         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
  • фиктивная линия
    • фиктивная линия
      • фиктивная линия
      • необязательно: проверьте zbuffer, является ли ранее нанесенный пиксель в этой координате выше или ниже, чем наш.
      • постройте график пикселя
      • startZ + = slopsZ; startU + = откос U; startV + = slopeV ;//обновить все интерполяторы
    • } конца цикла x
    • leftCurX + = leftDeltaX; reyCurX + = reyDeltaX; leftCurU + = reyDeltaU; leftCurV + = reyDeltaV; leftCurZ + = reyDeltaZ ;//обновить Y-интерполяторы
  • } конца y-контура

    //это конец первой части. Теперь мы нарисовали половину треугольника. от верхней до средней координаты Y. //Мы сейчас в основном делаем точно то же самое, но теперь для нижней половины треугольника (с использованием другого набора интерполяторов)

извините по поводу «фиктивных линий».. они были необходимы для синхронизации кодов уценки. (заняло время, чтобы все выглядело так, как задумано)

дайте мне знать, если это поможет вам решить проблему, с которой вы сталкиваетесь!

4
ответ дан 17 December 2019 в 07:04
поделиться

Если вы интерполируете 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 .

0
ответ дан 17 December 2019 в 07:04
поделиться

Я не знаю, что могу помочь с вашим вопросом, но одним из лучших книг по рендерингу программного обеспечения, что я прочитал в то время, доступен в Интернете Черная книга графики Майклом Абраш.

0
ответ дан 17 December 2019 в 07:04
поделиться
Другие вопросы по тегам:

Похожие вопросы: