LINUX.ORG.RU

[openGL] Быстрый способ вычисления нормалей

 


0

0

Решил я понемногу начать 3D в своей fits-смотрелке реализовывать. Для начала - без выпендрежа, тупыми страшными треугольниками. Посмотрел примеры (terrain, например), крутить 3D получается довольно быстро (60fps для изображения 3000x3000, 1500fps для 500x500), а вот нормали вычисляются медленно (~5с для 3000x3000).

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

/* вычисление нормалей для точки (x,y) изображения
 * сначала заполняются массивы нормалей для правого нижнего и левого верхнего
 * треугольников, с одной из вершин в данной точке,
 * затем для каждой точки вычисляется общая нормаль
 *
 * 				ПРИНЦИП
 *  *---B---*---*		(rd - прав.ниж. треугольник, ul - лев. верхний)
 *  | / | / | / |	Нормаль в точке А при отображении треугольников, как на рис.,
 *  E---A---C---*	складывается из нормалей Aul+Bdr+Cul+Adr+Dul+Edr
 *  | / | / | / | 	Нормали для отдельных треугольников: (X - интенсивность в точке)
 *  *---D---*---*		(нормаль = верт. вектор x гор. вектор)
 * 			не нормализованная	Aul = (E-A, A-B, 1)
 * 								Adr = (A-C, D-A, 1)
 */

Но вот с распараллеливанием (для CUDA'ы) пока не могу придумать, что делать (т.к. на границах областей придется влезать в соседние области => использовать разделяемую память для ускорения вычислений вряд ли получится).

☆☆☆☆☆
Ответ на: комментарий от vertexua

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

Да ладно, сейчас у меня нормали для изображения 3Кх3К считаются ~1.5с, а список строится 10с. Попробую VBO - может, быстрее получится. Ну, а всякие триангуляции и оптимизации «на лету» я еще буду делать, так же как и сглаживание с помощью NURBS. Все это в планах есть.

если ты сделаешь на OpenCL (ибо CUDA в пролете, не кроссвидеокартно)

Я использую CUDA, а если у кого-то ATI, считать будет процессор.

Eddy_Em ☆☆☆☆☆
() автор топика
Ответ на: комментарий от Eddy_Em

Да ладно, сейчас у меня нормали для изображения 3Кх3К считаются ~1.5с, а список строится 10с. Попробую VBO - может, быстрее получится. Ну, а всякие триангуляции и оптимизации «на лету» я еще буду делать, так же как и сглаживание с помощью NURBS. Все это в планах есть.

VBO не ускорит вычисление нормалей если ты об этом. Это для скорости рисования и только. И если у тебя не fillrate bound по самые помидоры, то переход на VBO всегда быстрее

Я использую CUDA, а если у кого-то ATI, считать будет процессор.

Facepalm.opencl.c. Пожалуйста, не говорите такого вслух, плакать хочется. И это то на форуме о Linux. Может на .NET и WPF перепишете. Там все классно с графикой тоже, и достаточно удобно. Для графики есть хорошее API - XNA, очень удобная штука, юзал, советую.

vertexua ★★★★★
()
Ответ на: комментарий от vertexua

А ну да, если тормоза в списке, то да, на VBO будет быстрее. Там оно с указаной области памяти по шине прямо в видеопамять зафигачит, без миллионов вызовов функций glVertex, которые еще нужно специально в список загнать заранее неизвестного размера )

vertexua ★★★★★
()
Ответ на: комментарий от vertexua

А относительно NURBS, то это тоже устаревшая технология. Ей на замену пришли два решения

1) Ъ. Передать обычную сетку, а потом на основе текстуры карты высот ландшафта их сдвинуть. Гугол: displacement mapping

2) Совсем Ъ. Geometric shaders. Это тоже самое только сетку передавать не надо, оно ее само прямо в видях на лету нагенерит. Чистейшее решение всех проблем кроме проблемы старых видях

vertexua ★★★★★
()
Ответ на: комментарий от vertexua

Вы не понимаете, зачем мне это надо: есть изображение, зашумленное. Для более «красивого» отображения его можно сгладить, например, при помощи NURBS (т.е. в сетку надо внести еще несколько доп. узлов, чтобы переходы были более гладкими, а потом аппроксимировать данные в основных узлах).

Eddy_Em ☆☆☆☆☆
() автор топика
Ответ на: комментарий от vertexua

Что-то не получается у меня с VBO: я не понимаю, как мне сделать H-1 вызов glDrawArrays(GL_TRIANGLE_STRIP, ...) из буферов. Инициализацию буфера вершин я делаю так:

		GLfloat *Verts = window->image->data;
		GLfloat *vertexes = malloc(sz);
		GLfloat *ptr = vertexes;
		for(y = 0; y < H; y++)
			for(x = 0; x < W; x++, Verts++){
				*ptr++ = dX + x;
				*ptr++ = dY + y;
				*ptr++ = *Verts;
			}
		glBufferSubDataARB(GL_ARRAY_BUFFER_ARB, 0, sz, vertexes);
Рисовал я при помощи списков так:
		glNewList(texture->tex, GL_COMPILE);
		H--;
		for(y = 0 ; y < H; y++){
			glBegin(GL_TRIANGLE_STRIP);
			for(x = 0; x < W; x++, Verts++, Norms+=3){
				glNormal3fv(Norms);
				glVertex3f(dX + x, dY + y, *Verts);
				glNormal3fv(&Norms[stride]);
				glVertex3f(dX + x, dY + (y + 1), Verts[W]);
			}
			glEnd();

А как сделать то же самое с VBO? Мне что, нужно переделывать буфер точек и нормалей, чтобы точки (x,y) и (x,y+1) шли последовательно?

Т.е. размер буфера придется удвоить, так я понимаю?

Eddy_Em ☆☆☆☆☆
() автор топика
Ответ на: комментарий от Eddy_Em

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

vertexua ★★★★★
()
Ответ на: комментарий от vertexua

Хорошо то, что можно один буфер индексов применить и к вершинам, и нормалям, и текстурным координатам. Или в терминах 2011 года - пользовательских атрибутам шейдеров, так как фиксированный конвеер уже практически не в тему, совсем не универсален )

vertexua ★★★★★
()
Ответ на: комментарий от vertexua

Что-то не рисуется у меня ничего с VBO (точнее - рисуется, но какая-то жесть). Инициализирую буфер индексов:

		GLint szi = W * (H-1) * 2 * sizeof(GLuint);
		GLuint *indexes = malloc(szi), *iptr = indexes, idx = 0;
		for(y = 0; y < H; y++)
			for(x = 0; x < W; x++, idx++){
				*iptr++ = idx;
				*iptr++ = idx + W;
			}
		glBufferDataARB(GL_ELEMENT_ARRAY_BUFFER, szi, 0, GL_STATIC_DRAW_ARB);
		glBufferSubDataARB(GL_ELEMENT_ARRAY_BUFFER, 0, szi, indexes);
А потом рисую так:
		GLint w = window->image->width, h = window->image->height;
		GLint sz = w * h * 3 * sizeof(GLfloat), pts = w*(h-1)*2;
		glBindBufferARB(GL_ARRAY_BUFFER_ARB, texture->tex);
		glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, texture->w);
		glEnableClientState(GL_NORMAL_ARRAY);
		glEnableClientState(GL_VERTEX_ARRAY);
		glEnableClientState(GL_INDEX_ARRAY);
		glNormalPointer(GL_FLOAT, 0, (void*)sz);
		glIndexPointer(GL_UNSIGNED_INT, 0, 0 );
		glVertexPointer(3, GL_FLOAT, 0, 0);
		glDrawArrays(GL_TRIANGLE_STRIP, 0, pts);
		glDisableClientState(GL_VERTEX_ARRAY);
		glDisableClientState(GL_NORMAL_ARRAY);
		glDisableClientState(GL_INDEX_ARRAY);
		glBindBufferARB(GL_ARRAY_BUFFER_ARB, 0);
Похоже, что-то у меня с индексами не то.

Eddy_Em ☆☆☆☆☆
() автор топика

Все, разобрался. Заполняю буферы так:

		GLfloat *Verts = window->image->data;
		GLint sz = W*H*3*sizeof(GLfloat), szi = W*(H-1)*2*sizeof(GLuint);
		glBindBufferARB(GL_ARRAY_BUFFER_ARB, texture->tex);
		glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, texture->w);
		glBufferDataARB(GL_ARRAY_BUFFER_ARB, sz*2,
						0, GL_STATIC_DRAW_ARB);
		GLfloat *vertexes = malloc(sz);
		GLuint *indexes = malloc(szi), *iptr = indexes, idx = 0;
		GLfloat *ptr = vertexes;
		for(y = 0; y < H; y++)
			for(x = 0; x < W; x++, Verts++){
				*ptr++ = dX + x;
				*ptr++ = dY + y;
				*ptr++ = *Verts;
			}
		H--;
		for(y = 0; y < H; y++)
			for(x = 0; x < W; x++, idx++){
				*iptr++ = idx;
				*iptr++ = idx + W;
			}
		glBufferDataARB(GL_ELEMENT_ARRAY_BUFFER, szi, 0, GL_STATIC_DRAW_ARB);
		glBufferSubDataARB(GL_ELEMENT_ARRAY_BUFFER, 0, szi, indexes);
		glBufferSubDataARB(GL_ARRAY_BUFFER_ARB, 0, sz, vertexes);
		glBufferSubDataARB(GL_ARRAY_BUFFER_ARB, sz,
							sz, window->image_transformed->data);
		free(vertexes);
		free(indexes);
		free(window->image->data); window->image->data = NULL;
		free(window->image_transformed->data);
		free(window->image_transformed); window->image_transformed = NULL;
Рисую вот так:
		GLint y, w = window->image->width, h = window->image->height;
		GLint sz = w * h * 3 * sizeof(GLfloat), pts = w*2;
		glBindBufferARB(GL_ARRAY_BUFFER_ARB, texture->tex);
		glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, texture->w);
		glEnableClientState(GL_NORMAL_ARRAY);
		glEnableClientState(GL_VERTEX_ARRAY);
		glEnableClientState(GL_INDEX_ARRAY);
		glNormalPointer(GL_FLOAT, 0, (void*)sz);
		glIndexPointer(GL_UNSIGNED_INT, 0, 0 );
		glVertexPointer(3, GL_FLOAT, 0, 0);
		h--;
		sz = 0; w = pts * sizeof(GLfloat);
		for(y = 0; y < h; y++, sz += w)
			glDrawElements(GL_TRIANGLE_STRIP, pts, GL_UNSIGNED_INT, (GLvoid*)(sz));
		glDisableClientState(GL_VERTEX_ARRAY);
		glDisableClientState(GL_NORMAL_ARRAY);
		glDisableClientState(GL_INDEX_ARRAY);
		glBindBufferARB(GL_ARRAY_BUFFER_ARB, 0);
Скорость заметно возросла по сравнению со списками, но почему-то, несмотря на все эти free, отжирается неплохое количество оперативки. Разве эти буферы не должны в видеопамяти храниться?

Eddy_Em ☆☆☆☆☆
() автор топика
Ответ на: комментарий от Eddy_Em

Если заранее построить функцию угла поворота без просмотра соседних граней, то всё просто.

unnamed
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.