2010-01-14 4 views
6

Я создаю растеризатор программного обеспечения, и я столкнулся с некоторой проблемой: я не могу получить перспективное отображение текстур для работы.Перспективное правильное отображение текстур; 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

инвертированием часть говорит вам, где дельты перевернуты. Как вы можете видеть, оба треугольника, похоже, идут в разные точки на горизонте.

Вот что я использую, чтобы вычислить 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; 
} 

Я прав, это вопрос г буфера?

ответ

4

ZBuffer не имеет к этому никакого отношения.

Этот ZBuffer полезен только тогда, когда треугольники перекрываются, и вы хотите убедиться, что они правильно нарисованы (например, правильно упорядочены по Z). ZBuffer будет для каждого пикселя треугольника определить, находится ли ранее размещенный пиксель ближе к камере, и если да, не нарисуйте пиксель вашего треугольника.

Поскольку вы рисуете 2 треугольника, которые не пересекаются, это не может быть проблемой.

Я сделал растеризатор программного обеспечения в фиксированной точке один раз (для мобильного телефона), но у меня нет источников на моем ноутбуке. Поэтому позвольте мне проверить сегодня, как я это сделал. По сути, у вас есть неплохо! Подобная вещь может быть вызвана очень маленькой ошибкой

Общие советы по отладке - это иметь несколько тестовых треугольников (угол наклона слева, угол наклона справа, угол 90 градусов и т. Д.) И пройти через него с отладчиком и посмотреть, как ваша логика касается дел.

EDIT:

peudocode моей растеризации (только U, V и Z принимаются во внимание ...если вы также хотите сделать gouraud, вам также нужно сделать все для R G и B, аналогично тому, что вы делаете для U и V и Z:

Идея состоит в том, что треугольник может быть разбит на две части. Верхняя часть и нижняя часть. Верхняя часть от y [0] до y [1], а нижняя часть - от y [1] до y [2]. Для обоих наборов вам нужно вычислить переменные шага, с которыми вы интерполируете. В приведенном ниже примере показано, как сделать верхнюю часть. При необходимости я также могу поставить нижнюю часть.

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

  • первого порядка в Coords (х, у, г, и, v) в порядке так что координата [0] .y < coord [1] .y < coord [2] .y
  • Далее проверьте, совпадают ли какие-либо 2 набора координат (только проверка x и y). Если так не нарисовать
  • исключение: имеет треугольник плоский верх? если это так, первый наклон будет бесконечным
  • исключение2: имеет ли треугольник плоское дно (да, треугольники тоже могут их содержать; ^)), тогда последний наклон тоже будет бесконечным
  • вычислить 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 как на х [0]
  • множество currentLeftU на leftDeltaU, currentLeftV на leftDeltaV и currentLeftZ на leftDeltaZ
  • известково начальную и конечную точки для первого диапазона Y: startY = CEIL (у [0]); endY = ceil (y [1])
  • prestep x, u, v и z для дробной части y для субпиксельной точности (думаю, это также необходимо для поплавков) Для моих алгоритмов с фиксированной точкой это было необходимо для создания линии и текстуры дают иллюзию перемещения в гораздо более тонких шагах, чем разрешение дисплея)
  • вычислить, где 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} else {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 ЬиХ = destbuf + (y pitch) + startX; (Беззнаковое целочисленное значение в случае, если вы делаете 24bit или 32 бит рендеринга) также подготовить указатель ZBuffer здесь (если вы используете это)
    • для (х = StartX; х < EndX; х ++) {
      • Теперь для отображения точки зрения текстуры (не используя 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, если ранее нанесены на пиксель этой координаты выше или ниже, то наши.
      • plot the pixel
      • startZ + = slopeZ; startU + = slopeU; startV + = slopeV; // все обновления интерполяторы
    • } конец цикла х
    • leftCurX + = leftDeltaX; rightCurX + = rightDeltaX; leftCurU + = rightDeltaU; leftCurV + = rightDeltaV; leftCurZ + = rightDeltaZ; // обновление Y интерполяторы
  • } конец цикла у

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

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

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

+0

Еще один совет для отладки, вычислить каждую точку текстуры, используя старомодные, неинкрементные U и V координаты для каждого пикселя и сравнить с инкрементными значениями, вычисленными вашим алгоритмом (с учетом некоторого оттока из-за ошибки округления). –

+1

Ничего себе ... это ответ наполовину! : D – Goz

+0

goz: теперь просто надеюсь, что это полезно, поскольку он, вероятно, имеет большую часть этого кода уже на месте, судя по картинке. Я думаю, что, вероятно, он просчитал одно из значений интерполяции. Но, надеюсь, если он заложит свой алгоритм рядом с моим, он сможет выяснить, какой он есть; ^) – Toad

0

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

0

Если вы используете интерполяцию 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

Скорее всего, это ускоритель: ему нужно разделить на z. Но поскольку умножение происходит быстрее, чем деление, он умножается на 1/z. – Toad

+0

Вы не можете избежать разрыва с перспективой правильного отображения текстуры. Вы можете аппроксимировать его по спаму пикселей, но вы не можете его устранить. Предполагая, что вы хотите что-то, что выглядит правильно. – MSN