Создайте плоскость с треугольными полосами

Каким будет лучший алгоритм для создания списка вершин для рисования плоскости с использованием треугольных полос?

Я ищу функцию, которая получает ширину и высоту плоскости и возвращает массив с плавающей запятой, содержащий правильно проиндексированные вершины.

ширина представляет количество вершин в строке.

высота представляет количество вершин в столбце.

float* getVertices( int width, int height ) {
    ...
}

void render() {
    glEnableClientState(GL_VERTEX_ARRAY);
    glVertexPointer(3, GL_FLOAT, 0, getVertices(width,heigth));
    glDrawArrays(GL_TRIANGLE_STRIP, 0, width*height);
    glDisableClientState(GL_VERTEX_ARRAY);
}

person NIGO    schedule 06.05.2011    source источник
comment
Есть ли ограничения? Я имею в виду, вы можете создать плоскость с 4 вершинами. :)   -  person Bart    schedule 06.05.2011
comment
Нужно больше информации. Ромб, параллелограмм, прямоугольник, трапеция и квадрат определяют плоскость, но каждая из них состоит из разных треугольных полос. Необходима информация об угле или возможность вычислить угол, чтобы рисовать треугольники. Есть предположения, о которых вы нам не сказали?   -  person Thomas Matthews    schedule 06.05.2011
comment
Это просто квадрат, но он должен быть разделен на множество треугольников, чтобы я мог играть с положением вершины и изменять форму плоскости. WIDTH представляет количество вершин в строке, а HEIGHT — количество вершин в столбце.   -  person NIGO    schedule 06.05.2011
comment
Вы можете посмотреть на stackoverflow.com/search?q=tesselation и flipcode.net/archives/Efficient_Polygon_Triangulation.shtml   -  person AShelly    schedule 06.05.2011
comment
Кроме того, взгляните на это: chadvernon. com/blog/resources/directx9/ Не OpenGl, но все же полезно, я думаю   -  person Bart    schedule 07.05.2011


Ответы (4)


Спасибо вам всем. Я закодировал это. Это правильно? Или сгенерированная полоса как-то не так?

int width;
int height;
float* vertices = 0;
int* indices = 0;

int getVerticesCount( int width, int height ) {
    return width * height * 3;
}

int getIndicesCount( int width, int height ) {
    return (width*height) + (width-1)*(height-2);
}

float* getVertices( int width, int height ) {
    if ( vertices ) return vertices;

    vertices = new float[ getVerticesCount( width, height ) ];
    int i = 0;

    for ( int row=0; row<height; row++ ) {
        for ( int col=0; col<width; col++ ) {
            vertices[i++] = (float) col;
            vertices[i++] = 0.0f;
            vertices[i++] = (float) row;
        }
    }

    return vertices;
}

int* getIndices( int width, int height ) {
    if ( indices ) return indices;

    indices = new int[ iSize ];
    int i = 0;

    for ( int row=0; row<height-1; row++ ) {
        if ( (row&1)==0 ) { // even rows
            for ( int col=0; col<width; col++ ) {
                indices[i++] = col + row * width;
                indices[i++] = col + (row+1) * width;
            }
        } else { // odd rows
            for ( int col=width-1; col>0; col-- ) {
                indices[i++] = col + (row+1) * width;
                indices[i++] = col - 1 + + row * width;
            }
        }
    }
    if ( (mHeight&1) && mHeight>2 ) {
        mpIndices[i++] = (mHeight-1) * mWidth;
    }

    return indices;
}

void render() {
    glEnableClientState( GL_VERTEX_ARRAY );
    glVertexPointer( 3, GL_FLOAT, 0, getVertices(width,height) );
    glDrawElements( GL_TRIANGLE_STRIP, getIndicesCount(width,height), GL_UNSIGNED_INT, getIndices(width,height) );
    glDisableClientState( GL_VERTEX_ARRAY );
}

С width=4 и height=4 вот что у меня получилось: введите здесь описание изображения

И здесь я изменяю высоту некоторых вершин: введите здесь описание изображения

person NIGO    schedule 07.05.2011
comment
Что ж, возможно, вы сами сможете ответить на этот вопрос. Работает ли он так, как вы ожидаете, или есть какие-то проблемы? Если вы визуализируете не каркас, а сплошные треугольники, все равно будет нормально выглядеть? - person Bart; 07.05.2011
comment
Примечание. Я исправил функцию getIndices, она не создавала правильные полосы для нечетной высоты. - person NIGO; 10.05.2011
comment
Примечание: спасибо за репутацию, я загрузил фотографии! - person NIGO; 28.05.2011
comment
Вы уверены, что проблема не в краю поверхности? - person Luca; 14.05.2013
comment
да, есть артефакты по краям. - person Luca; 14.05.2013
comment
Можете ли вы также объяснить эту часть? if ( (mHeight&1) && mHeight>2 ) { mpIndices[i++] = (mHeight-1) * mWidth; } - person Steve M. Bay; 29.03.2015
comment
Не могли бы вы объяснить, что такое iSize? - person Danny; 08.03.2016
comment
Следуя моему мыслительному процессу, я обнаружил, что функция getIndicesCount() отличается ... может кто-нибудь сказать мне, правильно ли это? int getIndicesCount( int width, int height ) { return (2*width) + ((2*width)*(height-2)); } - person VVZen; 19.07.2016
comment
ВВЗен, это не так. Используя вашу формулу, например, для сетки 4x3 вы получите 16 индексов, тогда как на самом деле вам нужно только 15. Для сетки 3x4 вы получите 18 индексов, когда вам действительно нужно только 16. Я использую эту формулу: numStripes = height - 1, numIndices = numStripes * (2 * ширина - 1) + 1;. (p.s. Формула NIGO тоже верна) - person Gediminas; 24.12.2016
comment
Стив М. Бэй, потому что способ обработки столбцов (нечетных строк) в коде NIGO (цикл for справа налево) пропускает один индекс. Он будет добавлен позже, когда/если будут обработаны столбцы (четные строки). Эта последняя строка просто гарантирует, что отсутствующий индекс добавлен. Если значение высоты сетки нечетное, это означает, что нам будет не хватать одного индекса, так что просто добавьте его. - person Gediminas; 24.12.2016
comment
С этим подходом есть проблема. На любом конце «ряда» (на вертикальной границе) индексы повторяются по прямой линии, поэтому, если это не плоская поверхность по краям, это создаст дополнительные треугольники. @NIGO: вы должны увидеть это, добавив некоторую высоту к некоторым граничным вершинам, а не к внутренним. - person Piyush Soni; 07.06.2017
comment
Извините, что такое mHeight, mWidth, mpIndices? - person Ares91; 14.09.2017
comment
Пожалуйста, сообщите нам о mHeight и mWidth - person Tajuddin Khandaker; 29.08.2019

Вот некоторый код, который делает это (не тестировался, но вы хотя бы поняли идею):

void make_plane(int rows, int columns, float *vertices, int *indices) {
    // Set up vertices
    for (int r = 0; r < rows; ++r) {
        for (int c = 0; c < columns; ++c) {
            int index = r*columns + c;
            vertices[3*index + 0] = (float) c;
            vertices[3*index + 1] = (float) r;
            vertices[3*index + 2] = 0.0f;
        }
    }

    // Set up indices
    int i = 0;
    for (int r = 0; r < rows - 1; ++r) {
        indices[i++] = r * columns;
        for (int c = 0; c < columns; ++c) {
            indices[i++] = r * columns + c;
            indices[i++] = (r + 1) * columns + c;
        }
        indices[i++] = (r + 1) * columns + (columns - 1);
    }
 }

Первый цикл устанавливает массив вершин в стандартной прямоугольной сетке. Есть вершины R*C.

Второй цикл устанавливает индексы. Как правило, на квадрат сетки приходится две вершины. Каждая вершина вызовет рисование нового треугольника (с двумя предыдущими вершинами), поэтому каждый квадрат рисуется двумя треугольниками.

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

person Jay Conrod    schedule 06.05.2011

ни один из приведенных выше кодов не дает правильной генерации сетки. Очень хорошая статья о том, как сделать полосу треугольников на простой плоскости: http://www.learnopengles.com/android-lesson-eight-an-introduction-to-index-buffer-objects-ibos/

Вот мой тестовый код, который действительно протестирован и полностью работает:

int plane_width = 4; // amount of columns
int plane_height = 2; // amount of rows

int total_vertices = (plane_width + 1) * (plane_height + 1);
planeVert = new CIwFVec2[total_vertices];
memset(planeVert, 0, sizeof(CIwFVec2) * total_vertices);

int numIndPerRow = plane_width * 2 + 2;
int numIndDegensReq = (plane_height - 1) * 2;
int total_indices = numIndPerRow * plane_height + numIndDegensReq;

planeInd = new uint16[total_indices];

make_plane(plane_width, plane_height, planeVert, planeInd);

...

void make_plane(int width, int height, CIwFVec2 *vertices, uint16 *indices)
{
width++;
height++;

int size = sizeof(CIwFVec2);
// Set up vertices
for(int y = 0; y < height; y++)
{
    int base = y * width;
    for(int x = 0; x < width; x++)
    {
        int index = base + x;
        CIwFVec2 *v = vertices + index;
        v->x = (float) x;
        v->y = (float) y;
        Debug::PrintDebug("%d: %f, %f", index, v->x, v->y);
    }
}

Debug::PrintDebug("-------------------------");

// Set up indices
int i = 0;
height--;
for(int y = 0; y < height; y++)
{
    int base = y * width;

    //indices[i++] = (uint16)base;
    for(int x = 0; x < width; x++)
    {
        indices[i++] = (uint16)(base + x);
        indices[i++] = (uint16)(base + width + x);
    }
    // add a degenerate triangle (except in a last row)
    if(y < height - 1)
    {
        indices[i++] = (uint16)((y + 1) * width + (width - 1));
        indices[i++] = (uint16)((y + 1) * width);
    }
}

for(int ind=0; ind < i; ind++)
    Debug::PrintDebug("%d ", indices[ind]);
}
person Dima    schedule 09.06.2013
comment
также, если вам нужны UV-координаты: CIwFVec2 *uv = pUV + index; uv-›x = v-›x / (ширина-1); uv-›y = v-›y/(высота-1); - person Dima; 09.06.2013
comment
В коде NIGO нет ничего плохого. Что касается An Introduction to Index Buffer Objects (IBOs) Обратите внимание на ошибку! Генерация треугольных стержней не будет работать для сетки w‹h. (Есть некоторые места, где вам нужно заменить yLength на xLength, прочитайте комментарии к сообщению о том, как это исправить) Кроме того, этот метод потребует гораздо большего количества индексов, потому что там используются более вырожденные треугольники только для сохранения того же направления треугольника. Кроме того, некоторые вырожденные треугольники будут видны в каркасе, если ваша сетка не плоская. - person Gediminas; 24.12.2016

Я делал что-то подобное и, используя первые два ответа, я придумал это (проверено, С#, XNA)

        // center x,z on origin
        float offset = worldSize / 2.0f, scale = worldSize / (float)vSize;

        // create local vertices
        VertexPositionColor[] vertices = new VertexPositionColor[vSize * vSize];

        for (uint z = 0; z < vSize; z++) {
            for (uint x = 0; x < vSize; x++) {
                uint index = x + (z * vSize);
                vertices[index].Position = new Vector3((scale*(float)x) - offset, 
                                                       heightValue, 
                                                       (scale*(float)z) - offset);
                vertices[index].Color = Color.White;
            }
        }

        // create local indices
        var indices = new System.Collections.Generic.List<IndexType>();

        for (int z = 0; z < vSize - 1; z++) {
            // degenerate index on non-first row
            if (z != 0) indices.Add((IndexType)(z * vSize));

            // main strip
            for (int x = 0; x < vSize; x++) {
                indices.Add((IndexType)(z * vSize + x));
                indices.Add((IndexType)((z + 1) * vSize + x));
            }

            // degenerate index on non-last row
            if (z != (vSize-2)) indices.Add((IndexType)((z + 1) * vSize + (vSize - 1)));
        }

Это легко преобразовать в С++, просто сделайте indices std::vector.

Примечательные особенности моего решения заключаются в следующем: а) Не нужно менять порядок намотки для каждой подполосы - добавление двух точек создает два вырожденных треугольника, поэтому порядок правильный для следующей подполосы. б) Вы должны условно добавить первую и последнюю вершины треугольника dg.

person Justicle    schedule 12.09.2011