Найти - Пользователи
Полная версия: PyOpenGl, столбчатая диаграма и скорость
Начало » GUI » PyOpenGl, столбчатая диаграма и скорость
1
sinopteek
Есть необходимость нарисовать трёхмерную столбчатую диаграмму.
Первоначально было реализовано на matplotlib с использованием модулей преобразующих плоские графики в трёхмерные. У данного метода есть существенные недостатки - очень медленная отрисовка(порядка 1-2 на один поворот), а также просмотр возможен только с определённого диапазона углов, при выходе угла обзора за границы этого диапазона происходит существенное искажение графиков - похоже на неправильное наложение слоёв - происходит перекрытие информации.
Поэтому было принято решение попробовать реализовать данный функционал на базе OpenGL.
Оказалось, что OpenGL не умеет рисовать параллелограммы - только кубы.
На основе примеров pyOpenGl и информации в интернете о рисовании примитивов в opengl была написана
# -*- coding: utf-8 -*-
'''
Created on 21.10.2009

@author: Sinopteek
'''
# This is statement is required by the build system to query build info
if __name__ == '__build__':
raise Exception

import OpenGL.GL
OpenGL.FORWARD_COMPATIBLE_ONLY = True
from OpenGL.GL import *
from OpenGL.GLUT import *
from OpenGL.GLE import *
from OpenGL.GLU import *
#import maintest

import random

lastx=0
lasty=0

# get notified of mouse motions
def MouseMotion (x, y):
global lastx, lasty
lastx = x
lasty = y
glutPostRedisplay ()

def JoinStyle (msg):
sys.exit(0)

def draw_by_opengl(DrawStuff,x_range=0,y_range=0,heigth = None):
if x_range == 0 or y_range == 0 or not heigth:
print "exit"
exit()
global glutDisplayFunc, glutMotionFunc
# initialize glut
glutInit(sys.argv)
glutInitDisplayMode (GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH)
glutInitWindowSize(640,480) #размер окна
glutCreateWindow("basic demo")
glutDisplayFunc(DrawStuff)
glutMotionFunc(MouseMotion)

# create popup menu */
glutCreateMenu (JoinStyle)
glutAddMenuEntry ("Exit", 99)
glutAttachMenu (GLUT_RIGHT_BUTTON)

# initialize GL */
glClearDepth (1.0)
glEnable (GL_DEPTH_TEST)
glClearColor (0.0, 0.0, 0.0, 0.0)
glShadeModel (GL_SMOOTH)

glMatrixMode (GL_PROJECTION)
# roughly, measured in centimeters */
glFrustum (-25.0, 25.0, -5.0, 5.0, 50.0, 1500.0)
glMatrixMode(GL_MODELVIEW)

glutMainLoop ()

def DrawBox(length=1.,width=1.,heigth=1.):
#рисует коробку по центру с заданными размерами
glBegin(GL_QUADS)
#// левая грань
glVertex3f( -length / 2, -heigth / 2, -width / 2)
glVertex3f( -length / 2, heigth / 2, -width / 2)
glVertex3f( -length / 2, heigth / 2, width / 2)
glVertex3f( -length / 2, -heigth / 2, width / 2)
#// правая грань
glVertex3f( length / 2, -heigth / 2, -width / 2)
glVertex3f( length / 2, -heigth / 2, width / 2)
glVertex3f( length / 2, heigth / 2, width / 2)
glVertex3f( length / 2, heigth / 2, -width / 2)
#// нижняя грань
glVertex3f( -length / 2, -heigth / 2, -width / 2)
glVertex3f( -length / 2, -heigth / 2, width / 2)
glVertex3f( length / 2, -heigth / 2, width / 2)
glVertex3f( length / 2, -heigth / 2, -width / 2)
#// верхняя грань
glVertex3f( -length / 2, heigth / 2, -width / 2)
glVertex3f( -length / 2, heigth / 2, width / 2)
glVertex3f( length / 2, heigth / 2, width / 2)
glVertex3f( length / 2, heigth / 2, -width / 2)
#// задняя грань
glVertex3f( -length / 2, -heigth / 2, -width / 2)
glVertex3f( length / 2, -heigth / 2, -width / 2)
glVertex3f( length / 2, heigth / 2, -width / 2)
glVertex3f( -length / 2, heigth / 2, -width / 2)
#// передняя грань
glVertex3f( -length / 2, -heigth / 2, width / 2)
glVertex3f( length / 2, -heigth / 2, width / 2)
glVertex3f( length / 2, heigth / 2, width / 2)
glVertex3f( -length / 2, heigth / 2, width / 2)
glEnd()

# draw the polycone shape
def DrawStuff():
glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
# set up some matrices so that the object spins with the mouse
glPushMatrix () #переходим к новым координатам

glTranslatef (0, -10.0, -200.0) #положение камеры
glRotatef (10, 1.0, 0.0, 0.0) #угол наклона

#поворачиваем картину в зависимости от координат курсора мыши
glRotatef (lastx, 0.0, 1.0, 0.0)
glRotatef (lasty, 1.0, 0.0, 0.0)

# glLineWidth(5); #толщина линий 5
glColor3f(0.8,0.0,0.0); #красненький

#смещаем начальную точку отрисовки граффика так, чтобы центр граффика совпал с осью вращения
glTranslatef (-x_range/2.0, 0.0, -y_range/2.0)

#рисуем столбики
for x in range(x_range):
for y in range(y_range):
glTranslatef (0.0, heigth[x][y]/2, 0.0); #смещаем в центр параллелипипида
DrawBox(length=0.2,width=0.2,heigth=heigth[x][y]) #рисуем параллелипипид
glTranslatef (0.0, -heigth[x][y]/2, 0.0); #смещаем обратно на ось нуля
glTranslatef (0.0, 0.0, 1.0);
glTranslatef (1.0, 0.0, -1.0*y_range);
glTranslatef (-1.0*x_range, 0.0, 0.0);

#glTranslatef (-x_range/2.0, 0.0, 0.0)

glPopMatrix () #возыращаемся к старым координатам

glutSwapBuffers () #помечаем кадр как готовый к отрисовке

x_range = 158
y_range = 43

#подгатавливаем данные для граффика
heigth = []
for x in range(x_range):
heigth_str = []
for y in range(y_range):
heigth_str.append(random.random())
heigth.append(heigth_str)

draw_by_opengl(DrawStuff,heigth=heigth, x_range=x_range, y_range=y_range)
код представленный здесь ранее был переписан
Данная программа работает существенно быстрее, чем вариант с использованием matplotlib и отсутствуют проблемы с неправильным перекрытием столбцов
Есть желание повысить производительность. В связи с этим возникает несколько вопросов:
может быть есть другие способы отрисовки параллелограммов?
может быть можно как-то по другому реализовать вращение диаграммы?
данный пример нагружает видеокарту слабо, в сравнении с теми же вращающимися шестерёнками из примеров, возможно необходимо как-то дополнительно инициализировать?

или же может быть существуют другие способы решить существующую задачу: необходимо нарисовать кучу столбиков (контроль всех трёх координат, тот же cube из стандартной библиотеки рисуется прямо в центре) и реализовать вращение получившийся диаграммы для удобного просмотра?
pasaranax
http://www.vpython.org простенькая библиотека для рисования 3д, для твоих нужд должно хватить.

Кстати, опенгл умеет рисовать паралеллепипеды, для этого надо отмасштабировать куб по определенным осям.
sinopteek
pasaranax
http://www.vpython.org простенькая библиотека для рисования 3д, для твоих нужд должно хватить.
Бегло смотрел, примеров мало. Насколько помню не нашол реализацию вращения мышью.

pasaranax
Кстати, опенгл умеет рисовать паралеллепипеды, для этого надо отмасштабировать куб по определенным осям.
или я пока не понял как сделать для единичного, или маштабирование происходит для всей картинки сразу, т.е. если я нарисую несколько кубов и применю маштабирование, то у меня вытянутся все кубы сразу.
Андрей Светлов
Во первых - GL_QUAD рисует четырехугольники, а не кубы. Т.е. указывайте любые координаты - и все будет в порядке.
Второе: есть две матрицы: GL_MODELVIEW и GL_PROJECTION
1. Поверните GL_PROJECTION так, как вам нужно - с фруструмом и прочими штучками.
2. Установите GL_MODELVIEW в единичную и отрисуйте свой график.

Чтобы шаг 2 был побыстрее - можно сделать display list. Техника морально устаревшая, но надежная. Если ничего поменялось - вызываем glCallList без его перегенерации.
sinopteek
Андрей Светлов
Во первых - GL_QUAD рисует четырехугольники, а не кубы. Т.е. указывайте любые координаты - и все будет в порядке.
я знаю, там даже комментарии в коде есть. Правда я не понял как это мне должно помочь
Андрей Светлов
Второе: есть две матрицы: GL_MODELVIEW и GL_PROJECTION
1. Поверните GL_PROJECTION так, как вам нужно - с фруструмом и прочими штучками.
2. Установите GL_MODELVIEW в единичную и отрисуйте свой график.

Чтобы шаг 2 был побыстрее - можно сделать display list. Техника морально устаревшая, но надежная. Если ничего поменялось - вызываем glCallList без его перегенерации.
В процессе, правда пока без списков

написал функцию по рисованию параллелипипидов различных цветов
def box(posx,posy,poz,sizex,sizey,sizez):
glPushMatrix() #сохраняем текущие координаты
glLoadIdentity () # clear the matrix
y=random.random()
glColor3f (random.random(), random.random(), random.random())
glTranslatef (posx,posy,poz)
#положение камеры, наведение в точку, установка вектора вверх(поворот камеры)
gluLookAt (0.0, 0.0, 5.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0)
if not sizex == sizey == sizez:
glScalef (sizex,sizey,sizez) #модельная трансформация
glutWireCube (1.0)
else:
glutWireCube (sizex)
glPopMatrix() #возвращаем предыдущие координаты
Понял, что для того, чтобы можно было применять преобразование модельной трансформации для каждого отдельного объекта необходимо загружать их в отдельные матрицы. Странно, но почему-то в примерах и учебниках которые я видел на это не аргументируется внимание
p.s. теперь думаю, что на самом деле мне коробки и не нужны. Понял только после того как научился рисовать коробки быстро.
Андрей Светлов
gluLookAt обычно ставится только один раз, в начале рисования схемы. Это часть задания матрицы GL_PROJECTION. Когда рисуете ящик - работаете только с GL_MODELVIEW.
Технически матрицы всего две. Для преобразования точки в трехмерном пространстве на экранные координаты используется их перемножение. Если объект имеет абсолютные координаты (чайник) - пляшем от единичной матрицы, сдвигая/поворачивая ее куда нужно. Если будем рисовать крышку от чайника вместе с самим - то берем матрицу для чайника и сдвигаем ее так, чтобы получили нулевую точку для крышки.
sinopteek
Андрей Светлов
gluLookAt обычно ставится только один раз, в начале рисования схемы. Это часть задания матрицы GL_PROJECTION. Когда рисуете ящик - работаете только с GL_MODELVIEW.
Технически матрицы всего две. Для преобразования точки в трехмерном пространстве на экранные координаты используется их перемножение. Если объект имеет абсолютные координаты (чайник) - пляшем от единичной матрицы, сдвигая/поворачивая ее куда нужно. Если будем рисовать крышку от чайника вместе с самим - то берем матрицу для чайника и сдвигаем ее так, чтобы получили нулевую точку для крышки.
Если закомментировать строку в функции рисования коробки, написанную мной, то он просто не отрисовывается - сейчас перепроверил специально.

Привожу пример полной программы которая рисует много (10000) коробок различных размеров и цветов. Может кому понадобится
import random

try:
from OpenGL.GLUT import *
from OpenGL.GL import *
from OpenGL.GLU import *
except:
print '''
ERROR: PyOpenGL not installed properly.
'''

def init():
glClearColor (0.0, 0.0, 0.0, 0.0) #фоновый цвет(используется при очищении)
glShadeModel (GL_FLAT)


def box(posx,posy,poz,sizex,sizey,sizez):
glPushMatrix() #сохраняем текущие координаты
glLoadIdentity () #очищаем матрицу
glColor3f (random.random(), random.random(), random.random())
glTranslatef (posx,posy,poz)
#положение камеры, наведение в точку, установка вектора вверх(поворот камеры)
gluLookAt (0.0, 0.0, 5.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0)
if not sizex == sizey == sizez:
glScalef (sizex,sizey,sizez) #модельная трансформация
glutWireCube (1.0)
else:
glutWireCube (sizex)
glPopMatrix() #сохраняем текущие координаты
#если раскомментировать получается забавно(рисуется каждая коробка)
#работает только с прямым буфер(отключённой двойной буферизацией)
#glFlush () #начинаем рисовать немедленно

def display():
glClear (GL_COLOR_BUFFER_BIT) #очищаем буфер, в данном случае цветовой
glColor3f (1.0, 1.0, 1.0) #устанавливаем цвет(белый)
glLoadIdentity () # clear the matrix
# viewing transformation
gluLookAt (0.0, 0.0, 5.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0)
glScalef (1.0, 2.0, 0.5) #модельная трансформация
glutWireCube (1.0)

for i in range(10000):
box(posx=random.random()-0.5,posy=random.random()-0.5,poz=random.random()-0.5,
sizex=random.random(),sizey=random.random(),sizez=random.random())

#можно использовать одну из двух в зависимости установок инициализации
if double_buffer:
glutSwapBuffers () #помечаем кадр как готовый к отрисовке
else:
glFlush () #начинаем рисовать немедленно

def reshape (w, h):
glViewport (0, 0, w, h) #выбираем всю область окна для рисования
glMatrixMode (GL_PROJECTION) #выбираем матрицу проекций
glLoadIdentity () #инициализируем матрицу перед использованием
#задаём плоскости отсечения
glFrustum (-1.0, 1.0, -1.0, 1.0, 1.5, 20.0)
glMatrixMode (GL_MODELVIEW) #выбираем видовую матрицу

glutInit(sys.argv)

#включить режим двойной буфферизации
double_buffer = False

#используется одна из двух моделей отображения
if double_buffer:
glutInitDisplayMode (GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH) #двойная буфферизация
else:
glutInitDisplayMode (GLUT_SINGLE | GLUT_RGB)

glutInitWindowSize (500, 500) #размер окна
glutInitWindowPosition (200, 100) #положение окна
glutCreateWindow (u'cubes тест') #создаём окно с именем
init ()
glutDisplayFunc(display) #задаём функцию которой будем рисовать содержимое окна
glutReshapeFunc(reshape) #функция для перерисовки окна
glutMainLoop() #запускаем рисование
Андрей Светлов
делайте gluLookAt сразу после glFrustrum
Baskervil
А не подскажите диаграммы в matplotlib можно строить?
Андрей Светлов
Откройте новую тему
This is a "lo-fi" version of our main content. To view the full version with more information, formatting and images, please click here.
Powered by DjangoBB