Создать контурный график в opengl, используя данные сетки и 1D текстурную карту - PullRequest
3 голосов
/ 11 января 2012

У меня есть набор значений X, Y, Z на регулярной разнесенной сетке, из которой мне нужно создать цветной контурный график с использованием C ++.Я уже несколько дней гуглю на это, и, похоже, все согласны с тем, что этого можно достичь, используя 1D текстурную карту в openGL.Однако я не нашел ни одного примера того, как на самом деле это сделать, и я никуда не денусь, просто читая документацию openGL.Моя путаница сводится к одному основному вопросу:

Мои данные не содержат значения X, Y для каждого пикселя - это равномерно распределенная сетка с данными каждые 4 единицы по осям X и Y, с положительным целым числомЗначение Z.

Например: (0, 0, 1), (4, 0, 1), (8, 0, 2), (0, 4, 2), (0, 8, 4), (4, 4, 3) и т. Д.

Поскольку контуры будут основаны на значении Z, и между точками данных будут промежутки, как применение одномерной текстуры обеспечивает контурную обработку этих данных (то есть как применение 1D текстуры интерполирует между точками сетки?)

Наиболее близким примером, который я нашел, является онлайн-версия Redbook (http://fly.cc.fer.hr/~unreal/theredbook/chapter09.html) в примере с чайником).но я предполагаю, что в модели чайника есть данные для каждого пикселя, и поэтому не требуется интерполяция между точками данных.

Если кто-то может пролить свет на мой вопрос или, что еще лучше, указать конкретный пример работы с 1Dкарта текстуры таким образом, я буду вечно благодарен, так как я сжег 2 дняв этом проекте с небольшим, чтобы показать это.

РЕДАКТИРОВАТЬ:

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

//Create a 1D image - for this example it's just a red line
int stripeImageWidth = 32;
GLubyte   stripeImage[3*stripeImageWidth];
for (int j = 0; j < stripeImageWidth; j++) {
    stripeImage[3*j] = j < 2 ? 0 : 255;
    stripeImage[3*j+1] = 255;
    stripeImage[3*j+2] = 255;
}
glDisable(GL_TEXTURE_2D);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glTexImage1D(GL_TEXTURE_1D, 0, 3, stripeImageWidth, 0, GL_RGB, GL_UNSIGNED_BYTE, stripeImage);
glTexParameterf(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_CLAMP);
glTexParameterf(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL);

glTexGeni( GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR );
float s[4] = { 0,1,0,0 };
glTexGenfv( GL_S, GL_OBJECT_PLANE, s );
glEnable( GL_TEXTURE_GEN_S );
glEnable( GL_TEXTURE_1D );
glBegin(GL_POINTS);

//_coords contains X,Y,Z data - Z is the value that I'm trying to contour
for (int x = 0; x < _coords.size(); ++x)
{
    glTexCoord1f(static_cast<ValueCoord*>(_coords[x])->GetValue());
    glVertex3f(_coords[x]->GetX(), _coords[x]->GetY(), zIndex);
}
glEnd();

1 Ответ

7 голосов
/ 11 января 2012

Идея заключается в использовании координаты Z в качестве координаты S в текстуре. Линейная интерполяция по координате текстуры затем создает контур. Обратите внимание, что с помощью шейдера вы можете поместить данные XY-> Z в 2D-текстуру и использовать шейдер для перенаправления значения 2D-сэмплера в цветовой шкале 1D-текстуры.

Обновление: пример кода

Сначала нам нужно немного изменить способ использования текстур.

Для этого подготовить текстуру:

//Create a 1D image - for this example it's just a red line
int stripeImageWidth = 32;
GLubyte   stripeImage[3*stripeImageWidth];
for (int j = 0; j < stripeImageWidth; j++) {
    stripeImage[3*j] = j*255/32; // use a gradient instead of a line
    stripeImage[3*j+1] = 255;
    stripeImage[3*j+2] = 255;
}

GLuint texID;
glGenTextures(1, &texID);
glBindTexture(GL_TEXTURE_1D, texID);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glTexImage1D(GL_TEXTURE_1D, 0, 3, stripeImageWidth, 0, GL_RGB, GL_UNSIGNED_BYTE, stripeImage);

// We want the texture to wrap, so that values outside the range [0, 1] 
// are mapped into a gradient sawtooth
glTexParameterf(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameterf(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameterf(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL);

И это, чтобы связать его для использования.

// The texture coordinate comes from the data, it it not
// generated from the vertex position!!!
glDisable( GL_TEXTURE_GEN_S ); 
glDisable(GL_TEXTURE_2D);
glEnable( GL_TEXTURE_1D );
glBindTexture(GL_TEXTURE_1D, texID);

Теперь к вашей концептуальной проблеме: вы не можете напрямую построить контурный график из данных XYZ. XYZ - это просто редкие точки отбора проб. Вам необходимо заполнить пробелы, например, сначала поместив их в 2D-гистограмму. Для этого создайте сетку с определенным количеством бинов в каждом направлении, инициализируемую всем NaN (псевдокод)

float hist2D[bins_x][bins_y] = {NaN, NaN, ...}

затем для каждого XYZ добавьте значение Z в ячейки сетки, если не NaN, в противном случае замените NaN значением Z. После этого используйте фильтр Лапласа на гистограмме, чтобы сгладить ячейки, в которых все еще содержится NaN. Наконец, вы можете отобразить сетку как контурный график, используя

glBegin(GL_QUADS);
for(int y=0; y<grid_height; y+=2) for(int x=0; x<grid_width; x+=2) {
    glTexCoord1f(hist2D[x  ][y  ]]); glVertex2i(x  ,y);
    glTexCoord1f(hist2D[x+1][y  ]]); glVertex2i(x+1,y);
    glTexCoord1f(hist2D[x+1][y+1]]); glVertex2i(x+1,y+1);
    glTexCoord1f(hist2D[x  ][y+1]]); glVertex2i(x  ,y+1);
}
glEnd();

или вы можете загрузить сетку как 2D-текстуру и использовать фрагментный шейдер для косвенного изменения цвета.

Другой способ заполнить пробелы в разреженных данных XYZ - найти 2D диаграмму Вороного из набора XY и использовать ее для создания геометрии выборки. Значения Z для вершин будут средневзвешенными по расстоянию для XYZ, способствующих пересечению клеток Вороного.

...