Главная Статьи Ссылки Скачать Скриншоты Юмор Почитать Tools Проекты Обо мне Гостевая |
Благодаря своей гибкости и простоте язык Python позволяет легко и быстро писать целый ряд приложений и утилит. При этом для работы с "непитоновскими" библиотеками (например OpenGL) необходимы модули, обеспечивающие возможность вызова функций библиотеки непосредственно из программы на языке Python.
Библиотека PyOpenGL как раз и является таким модулем, позволяющим в программах на языке Python легко работать с функциями OpenGL, GLU и GLUT, а также с рядом расширений OpenGL.
При этом простейшая программа, использующая PyOpenGL очень похожа на аналогичную программу на С++.
from OpenGL.GL import * from OpenGL.GLU import * from OpenGL.GLUT import * import Image # PIL import sys import math angle = 0.0 angle2 = 0.0 texture = 0 object = 0 d = 3.0 / math.sqrt ( 3 ) def drawBox ( x1, x2, y1, y2, z1, z2 ): glBindTexture ( GL_TEXTURE_2D, texture ) glBegin ( GL_POLYGON ) # front face glNormal3f ( 0.0, 0.0, 1.0 ) glTexCoord2f ( 0, 0 ) glVertex3f ( x1, y1, z2 ) glTexCoord2f ( 1, 0 ) glVertex3f ( x2, y1, z2 ) glTexCoord2f ( 1, 1 ) glVertex3f ( x2, y2, z2 ) glTexCoord2f ( 0, 1 ) glVertex3f ( x1, y2, z2 ) glEnd () glBegin ( GL_POLYGON ) # back face glNormal3f ( 0.0, 0.0, -1.0 ) glTexCoord2f ( 1, 0 ) glVertex3f ( x2, y1, z1 ) glTexCoord2f ( 0, 0 ) glVertex3f ( x1, y1, z1 ) glTexCoord2f ( 0, 1 ) glVertex3f ( x1, y2, z1 ) glTexCoord2f ( 1, 1 ) glVertex3f ( x2, y2, z1 ) glEnd () glBegin ( GL_POLYGON ) # left face glNormal3f ( -1.0, 0.0, 0.0 ) glTexCoord2f ( 0, 0 ) glVertex3f ( x1, y1, z1 ) glTexCoord2f ( 0, 1 ) glVertex3f ( x1, y1, z2 ) glTexCoord2f ( 1, 1 ) glVertex3f ( x1, y2, z2 ) glTexCoord2f ( 1, 0 ) glVertex3f ( x1, y2, z1 ) glEnd () glBegin ( GL_POLYGON ) # right face glNormal3f ( 1.0, 0.0, 0.0 ) glTexCoord2f ( 0, 1 ) glVertex3f ( x2, y1, z2 ) glTexCoord2f ( 0, 0 ) glVertex3f ( x2, y1, z1 ) glTexCoord2f ( 1, 0 ) glVertex3f ( x2, y2, z1 ) glTexCoord2f ( 1, 1 ) glVertex3f ( x2, y2, z2 ) glEnd () glBegin ( GL_POLYGON ) # top face glNormal3f ( 0.0, 1.0, 0.0 ) glTexCoord2f ( 0, 1 ) glVertex3f ( x1, y2, z2 ) glTexCoord2f ( 1, 1 ) glVertex3f ( x2, y2, z2 ) glTexCoord2f ( 1, 0 ) glVertex3f ( x2, y2, z1 ) glTexCoord2f ( 0, 0 ) glVertex3f ( x1, y2, z1 ) glEnd () glBegin ( GL_POLYGON ) # bottom face glNormal3f ( 0.0, -1.0, 0.0 ) glTexCoord2f ( 1, 1 ) glVertex3f ( x2, y1, z2 ) glTexCoord2f ( 1, 0 ) glVertex3f ( x1, y1, z2 ) glTexCoord2f ( 0, 0 ) glVertex3f ( x1, y1, z1 ) glTexCoord2f ( 0, 1 ) glVertex3f ( x2, y1, z1 ) glEnd () def loadTexture ( fileName ): image = Image.open ( fileName ) width = image.size [0] height = image.size [1] image = image.tostring ( "raw", "RGBX", 0, -1 ) texture = glGenTextures ( 1 ) glBindTexture ( GL_TEXTURE_2D, texture ) # 2d texture (x and y size) glPixelStorei ( GL_UNPACK_ALIGNMENT,1 ) glTexParameterf ( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT ) glTexParameterf ( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT ) glTexParameteri ( GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR ) glTexParameteri ( GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR_MIPMAP_LINEAR ) gluBuild2DMipmaps ( GL_TEXTURE_2D, 3, width, height, GL_RGBA, GL_UNSIGNED_BYTE, image ) return texture def init (): glClearColor ( 0.0, 0.0, 0.0, 0.0 ) glClearDepth ( 1.0 ) glDepthFunc ( GL_LEQUAL ) glEnable ( GL_DEPTH_TEST ) glEnable ( GL_TEXTURE_2D ) glHint ( GL_POLYGON_SMOOTH_HINT, GL_NICEST ) glHint ( GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST ) def reshape ( width, height ): glViewport ( 0, 0, width, height ) glMatrixMode ( GL_PROJECTION ) glLoadIdentity () gluPerspective ( 60.0, float(width)/float (height), 1.0, 60.0 ) glMatrixMode ( GL_MODELVIEW ) glLoadIdentity () gluLookAt ( 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 0.0, 1.0, 0.0 ) def display (): glClear ( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ) glMatrixMode ( GL_MODELVIEW ) glLoadIdentity () gluLookAt ( 0, 0, 0, 1, 1, 1, 0, 1, 0 ) glTranslatef ( 4, 4, 4 ) glRotatef ( angle2, 1, -1, 0 ) glTranslatef ( d - 1.5, d - 1.5, d - 1.5 ) glTranslatef ( 1.5, 1.5, 1.5 ) # move cube from the center glRotatef ( angle, 1.0, 1.0, 0.0 ) glTranslatef ( -1.5, -1.5, -1.5 ) # move cube into the center drawBox ( 1, 2, 1, 2, 1, 2 ) glutSwapBuffers () def keyPressed ( *args ): if args [0] == '\033': sys.exit () def animate (): global angle, angle2 angle = 0.04 * glutGet ( GLUT_ELAPSED_TIME ) angle2 = 0.01 * glutGet ( GLUT_ELAPSED_TIME ) glutPostRedisplay () def main (): global texture glutInit ( sys.argv ) glutInitDisplayMode ( GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH ) glutInitWindowSize ( 500, 500 ) glutInitWindowPosition ( 0, 0 ) glutCreateWindow ( "Simple PyOpenGL example" ) glutDisplayFunc ( display ) glutIdleFunc ( animate ) glutReshapeFunc ( reshape ) glutKeyboardFunc ( keyPressed ) init () texture = loadTexture ( "../../Textures/block.bmp" ) glutMainLoop() print "Hit ESC key to quit." main()
Обратите внимание , что для загрузки текстур из файла была использована еще одна библиотека для Python'а - PIL.
Крайне просто осуществляется и использование расширений OpenGL, ниже приводится листинг программы, использующей расширение ARB_texture_cubemap.
from OpenGL.GL import * from OpenGL.GLU import * from OpenGL.GLUT import * from OpenGL.GL.ARB.texture_cube_map import * import Image # PIL import sys angle = 0.0 angle2 = 0.0 texture = 0 object = 0 reflMode = GL_REFLECTION_MAP_ARB def loadTexture ( fileName ): image = Image.open ( fileName ) width = image.size [0] height = image.size [1] image = image.tostring ( "raw", "RGBX", 0, -1 ) texture = glGenTextures ( 1 ) glBindTexture ( GL_TEXTURE_2D, texture ) # 2d texture (x and y size) glPixelStorei ( GL_UNPACK_ALIGNMENT,1 ) glTexParameterf ( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT ) glTexParameterf ( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT ) glTexParameteri ( GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR ) glTexParameteri ( GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR_MIPMAP_LINEAR ) gluBuild2DMipmaps ( GL_TEXTURE_2D, 3, width, height, GL_RGBA, GL_UNSIGNED_BYTE, image ) return texture def loadCubemap ( faces, path = "" ): texture = glGenTextures ( 1 ) target = GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB glBindTexture ( GL_TEXTURE_CUBE_MAP_ARB, texture ) # 2d texture (x and y size) glPixelStorei ( GL_UNPACK_ALIGNMENT,1 ) glTexParameteri ( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT ) glTexParameteri ( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT ) glTexParameteri ( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ) glTexParameteri ( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR ) glEnable ( GL_TEXTURE_CUBE_MAP_ARB ) for f in faces: if path != "": file = path + "/" + f else: file = f image = Image.open ( file ) width = image.size [0] height = image.size [1] image = image.tostring ( "raw", "RGBX", 0, -1 ) gluBuild2DMipmaps ( target, 3, width, height, GL_RGBA, GL_UNSIGNED_BYTE, image ) target = target + 1 return texture def extensionInit (): """ Determine if the fog coord extentsion is availble """ # After calling this, we will be able to invoke glFogCoordEXT () if not glInitTextureCubeMapARB (): print "ARB_texture_cubemap not supported !" sys.exit ( 1 ) def init (): glClearColor ( 0.0, 0.0, 0.0, 0.0 ) glClearDepth ( 1.0 ) glDepthFunc ( GL_LEQUAL ) glEnable ( GL_DEPTH_TEST ) glHint ( GL_POLYGON_SMOOTH_HINT, GL_NICEST ) glHint ( GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST ) def reshape ( width, height ): glViewport ( 0, 0, width, height ) glMatrixMode ( GL_PROJECTION ) glLoadIdentity () gluPerspective ( 60.0, float(width)/float (height), 1.0, 60.0 ) glMatrixMode ( GL_MODELVIEW ) glLoadIdentity () gluLookAt ( 0.0, 0.0, 0.0, 5.0, 5.0, 5.0, 0.0, 1.0, 0.0 ) def display (): global texture, reflMode glClear ( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ) glEnable ( GL_TEXTURE_CUBE_MAP_ARB ) glEnable ( GL_TEXTURE_GEN_S ) glEnable ( GL_TEXTURE_GEN_T ) glEnable ( GL_TEXTURE_GEN_R ) glTexGeni ( GL_S, GL_TEXTURE_GEN_MODE, reflMode ) glTexGeni ( GL_T, GL_TEXTURE_GEN_MODE, reflMode ) glTexGeni ( GL_R, GL_TEXTURE_GEN_MODE, reflMode ) glBindTexture ( GL_TEXTURE_CUBE_MAP_ARB, texture ) glPushMatrix () glTranslatef ( 2, 2, 2 ) glRotatef ( angle, 1, 1, 0 ) glRotatef ( angle2, 0, 1, 1 ) if object == 0: glutSolidTeapot ( 1 ) elif object == 1: glutSolidSphere ( 1, 30, 30 ) elif object == 2: glutSolidCube ( 1 ) elif object == 3: glutSolidCone ( 0.7, 1.2, 30, 30 ) elif object == 4: glutSolidTorus ( 0.5, 1, 40, 40 ) elif object == 5: glutSolidDodecahedron () elif object == 6: glutSolidOctahedron () elif object == 7: glutSolidTetrahedron () elif object == 8: glutSolidIcosahedron (); glPopMatrix () glutSwapBuffers () def keyPressed ( *args ): global object, reflMode key = args [0] if key == '\033': sys.exit () elif key == 'n' or key == 'N': reflMode = GL_NORMAL_MAP_ARB elif key == 'r' or key == 'R': reflMode = GL_REFLECTION_MAP_ARB elif key == '0': object = 0 elif key == '1': object = 1 elif key == '2': object = 2 elif key == '3': object = 3 elif key == '4': object = 4 elif key == '5': object = 5 elif key == '6': object = 6 elif key == '7': object = 7 elif key == '8': object = 8 elif key == '+': object = object + 1 if object > 8: object = 0 elif key == '-': object = object-1 if object < 0: object = 8 def animate (): global angle, angle2 angle = 0.04 * glutGet ( GLUT_ELAPSED_TIME ) angle2 = 0.01 * glutGet ( GLUT_ELAPSED_TIME ) glutPostRedisplay () def main (): global texture glutInit ( sys.argv ) glutInitDisplayMode ( GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH ) glutInitWindowSize ( 500, 500 ) glutInitWindowPosition ( 0, 0 ) glutCreateWindow ( "ARB_texture_cubemap demo" ) glutDisplayFunc ( display ) glutIdleFunc ( animate ) glutReshapeFunc ( reshape ) glutKeyboardFunc ( keyPressed ) init () extensionInit (); texture = loadCubemap ( ( "cm_left.tga", "cm_right.tga", "cm_top.tga", "cm_bottom.tga", "cm_back.tga", "cm_front.tga" ), "../../Textures/Cubemaps" ) print texture glutMainLoop() print "Hit ESC key to quit." main()
Обратите внимание на использование конструкции global - без нее соответствующие переменные будут считаться локальными, т.е. доступа к правильным значениям, определенным вне функций, не произойдет.
К сожалению используемая мною версия библиотеки PyOpenGL обладает двумя недостатками - в ней отсутствует поддержка расширения EXT_framebuffer_object и мне так и не удалось найти способ как передать текст шейдера - по стандарту он должен передаваться как указатель на массив строк, т.е. иметь тип char **.
Поэтому я написал два модуля расширения для Python'а, позволяющее использовать в программах на Python'е классы GlslProgram и FrameBuffer.
Ниже на примере первого из этих классов мы рассмотрим каким образом можно расширить Python при помощи классов, написанных на С++.
Посмотрим на начало файла, "заворачивающего" класс GlslProgram (на С++) для его использования в программах на языке Python.
#include <Python.h> #include "GlslProgram.h" #include "libExt.h" #include "Data.h" #include "Vector4D.h" #include "Vector3D.h" #include "Vector2D.h"
Файл Python.h подключает определения необходимых типов и структур, а также описания функций, убедитесь, что у вас установлен путь к этому файлу в списке путей для поиска заголовочных файлов.
Каждому вводимому типу соответствует описывающая его структура, всегда начинающееся с одного и того же заданного фрагмента. Этот фрагмент обычно определяют как макрос и описание структуры типа в нашем случае выглядит следующим образом:
struct GlslProgramObject { PyObject_HEAD GlslProgram * p; };
Далее обычно помещаются предварительные описания вводимых функций. Большинство из них на вход получают указатель на сам объект (self), а также на объект - список всех параметров.
static PyObject * loadShaders ( GlslProgramObject * self, PyObject * args ); static PyObject * clear ( GlslProgramObject * self, PyObject * args ); static PyObject * getLog ( GlslProgramObject * self, PyObject * args ); static PyObject * isOk ( GlslProgramObject * self, PyObject * args ); static PyObject * bindFunc ( GlslProgramObject * self, PyObject * args ); static PyObject * unbind ( GlslProgramObject * self, PyObject * args ); static PyObject * isSupported ( PyObject * self, PyObject * args ); static PyObject * setUniform ( GlslProgramObject * self, PyObject * args ); static PyObject * setTexture ( GlslProgramObject * self, PyObject * args ); static PyObject * setUniformMatrix ( GlslProgramObject * self, PyObject * args ); static PyObject * getattr ( GlslProgramObject * self, char * name ); static PyObject * repr ( GlslProgramObject * self ); static void GlslProgram_dealloc ( GlslProgramObject * self );
Каждый вводимый тип (класс) должен быть задан при помощи структуры PyTypeObject, содержащей всю необходимую информацию о данном классе:
static PyTypeObject GlslProgram_Type = { PyObject_HEAD_INIT(NULL) 0, /*ob_size*/ "GlslProgram", /*tp_name*/ sizeof(GlslProgramObject), /*tp_basicsize*/ 0, /*tp_itemsize*/ /* methods */ (destructor)GlslProgram_dealloc, /*tp_dealloc*/ 0, /*tp_print*/ (getattrfunc)getattr, /*tp_getattr*/ 0, /*tp_setattr*/ 0, /*tp_compare*/ (reprfunc)repr, /*tp_repr*/ 0, /*tp_as_number*/ 0, /*tp_as_sequence*/ 0, /*tp_as_mapping*/ 0, /*tp_hash*/ };
Наиболее важными полями этой структуры являются имя типа (поле tp_name), размер объекта данного типа (tp_basicsize), указатель на деструктор для экземпляров данного класса (tp_dealloc) и функция доступа к атрибутам и методам (tp_getattr).
Также м добавим функцию tp_repr, выдающую строчное представление данного объекта.
Кроме этого нам также необходимо задать два массива указателей на методы - один для методов модуля и один для методов экземпляров данного класса.
static PyMethodDef GlslProgram_Module_methods [] = { { "isSupported", (PyCFunction)isSupported, METH_VARARGS }, { NULL, NULL} /* sentinel */ }; static PyMethodDef GlslProgram_methods [] = { { "loadShaders", (PyCFunction)loadShaders, METH_VARARGS, "" }, { "clear", (PyCFunction) clear, METH_VARARGS, "" }, { "getLog", (PyCFunction) getLog, METH_VARARGS, "" }, { "isOk", (PyCFunction) isOk, METH_VARARGS, "" }, { "bind", (PyCFunction) bindFunc, METH_VARARGS, "" }, { "unbind", (PyCFunction) unbind, METH_VARARGS, "" }, { "setUniform", (PyCFunction) setUniform, METH_VARARGS, "" }, { "setUniformMatrix", (PyCFunction) setUniformMatrix, METH_VARARGS, "" }, { "setTexture", (PyCFunction) setTexture, METH_VARARGS, "" }, { NULL, NULL} /* sentinel */ };
Как методы экземпляров класса, так и методы модуля задаются в виде массива структур PyMethodDef, последний элемент массива должен начиняться в двух NULL'ов.
Каждая структура PyMethodDef состоит из следующих полей:
Поле ml_flags поддерживает следующие битовые значения (соединенные побитовым ИЛИ - |):
Рассмотрим реализацию нескольких методов класса GlslProgram:
static PyObject * getLog ( GlslProgramObject * self, PyObject * args ) { return PyString_FromString ( self -> p -> getLog ().c_str () ); } static PyObject * setTexture ( GlslProgramObject * self, PyObject * args ) { char * name; int unit; if ( !PyArg_ParseTuple ( args, "si", &name, &unit ) ) return NULL; self -> p -> setTexture ( name, unit ); return Py_BuildValue ( "i", true ); }
При передаче аргументов в виде массива (tuple) для извлечения из массива аргументов заданных типов удобно использовать функцию PyArg_ParseTuple.
На вход этой функции передается указатель на список аргументов (как PyObject *), строку с форматом (определяющим какие значения и каким именно типов следует извлечь) и указатели на на переменные, куда будут помещены значения, извлеченные из списка.
В целом структура функции очень напоминает функцию sscanf, только данные читаются из объекта-списка и форматная строка имеет другой формат.
Наиболее часто используемыми символами типов в строке формата являются "O" (объект языка Python), "s" (строка), "i" (целое число) и "f" (float-число). Также допустимо использование символа "|" обозначающего, что все следующие далее аргументы являются необязательными (в этом случае соответствующие им переменные не инициализируются). Полный список всех допустимых конструкцией в строке формата можно найти в описании функции PyArg_ParseTuple.
В случае успешного извлечения всех обязательных аргументов (и полного соответствия типов) функция возвращает ненулевое значение, при ошибках возвращается ноль.
Есть очень похожая на нее функция Py_BuildValue, берущая в качестве первого параметра строку формата и набор значений. Строка формата определяет способ построения объекта языка Python по переданным значениям.
При этом данная функция может принимать на вход сразу много значений и строить из них массивы, списки и словари.
Py_BuildValue ( "[sii]", "abc", 1, 3 );
Так приведенный выше вызов возвращает следующий объект ["abc", 1, 3].
Помимо реализации всех обычных методов необходимо реализовать еще несколько специальных функций - создание и уничтожение экземпляров класса и регистрация данного модуля.
Ниже приводится код для функция создания и уничтожения экземпляров класса GlslProgram.
static PyObject * GlslProgram_new ( PyTypeObject * type, PyObject * args, PyObject * kwds ) { initExtensions (); GlslProgramObject * object = PyObject_NEW ( GlslProgramObject, &GlslProgram_Type ); if ( object == NULL ) return NULL; object -> p = new GlslProgram (); return (PyObject *) object; } static void GlslProgram_dealloc ( GlslProgramObject * self ) { delete ((GlslProgramObject *) self) -> p; PyObject_DEL ( self ); }
Важной частью любого объекта является метод getattr - фактически обращение ко всем методам и instance-переменным объекта идет именно через этот метод (есть еще парный ему метод setattr, позволяющий устанавливать значения instance-переменных и заменять методы).
Этот метод получает на вход указатель на сам объект (self) и имя атрибута/метода и возвращает значение атрибута или вызываемый (callable) объект для методов.
Проще всего реализовать его через функцию Py_FindMethod, осуществляющей поиск метода по таблице методов. Ниже приводится его реализация для класса FrameBuffer, где данный метод используется также для создания псевдо-переменных width и height (вместо соответствующих методов класса на С++).
static PyObject * getattr ( FrameBufferObject * self, char * name ) { PyObject * res = Py_FindMethod ( FrameBuffer_methods, (PyObject*) self, name ); if ( res ) return res; PyErr_Clear (); if ( !strcmp ( name, "width" ) ) // attributes return PyInt_FromLong ( self -> fb -> getWidth () ); else if ( !strcmp ( name, "height" ) ) return PyInt_FromLong ( self -> fb -> getHeight () ); else if ( !strcmp ( name, "hasStencil" ) ) return PyInt_FromLong ( self -> fb -> hasStencil () ); else if ( !strcmp ( name, "hasDepth" ) ) return PyInt_FromLong ( self -> fb -> hasDepth () ); return NULL; }
Для регистрации модуля (чтобы его можно было загружать из Python-программы) он должен быть скомпилирован в dll (so)-файл с расширением .pyd и обязательно содержать экспортируемую функцию с именем вида init<имя модуля> (для нашего примера это будет initGlslProgram).
При использовании Visual C++ этот метод должен объявлен как extern "C" __declspec(dllexport), для Linux и Mac OS X никакого дополнительного описателя не требуется). Ниже приводится реализация данного метода для "заворачивания" класса GlslProgram.
extern "C" void __declspec(dllexport) initGlslProgram () { GlslProgram_Type.ob_type = &PyType_Type; GlslProgram_Type.tp_new = GlslProgram_new; GlslProgram_Type.tp_flags |= Py_TPFLAGS_CHECKTYPES; // Create the module and add the functions PyObject * m = Py_InitModule ( "GlslProgram", GlslProgram_Module_methods ); Py_INCREF ( &GlslProgram_Type ); PyModule_AddObject ( m, "GlslProgram", (PyObject *)&GlslProgram_Type ); }
Обратите внимание на инициализацию полей ob_type, tp_new и tp_flags, а также сам код, непосредственно создающий модуль при помощи вызова Py_InitModule, увеличивающий число ссылок на него (иначе он будет сразу же уничтожен сборщиком мусора) и добавляющий новый тип (класс) в данный модуль.
После того, как модуль успешно скомпилирован и слинкован и получен файл GlslProgram.pyd его можно использовать из программ на Python как обычный модуль языка Python.
from OpenGL.GL import * from OpenGL.GLU import * from OpenGL.GLUT import * from OpenGL.GL.ARB.vertex_shader import * from OpenGL.GL.ARB.fragment_shader import * from OpenGL.GL.ARB.shader_objects import * from OpenGL.GL.ARB.shading_language_100 import * import Image # PIL import sys import math import GlslProgram from utils import * angle = 0.0 angle2 = 0.0 texture = 0 object = 0 d = 3.0 / math.sqrt ( 3 ) program = None def initExtensions (): if not glInitShaderObjectsARB (): sys.exit ( 1 ) if not glInitShadingLanguage100ARB (): sys.exit ( 1 ) if not glInitFragmentShaderARB (): print "Fragment shaders not supported" sys.exit ( 1 ) if not glInitVertexShaderARB (): print "Vertex shaders not supported" sys.exit ( 1 ) def init (): glClearColor ( 0.0, 0.0, 0.0, 0.0 ) glClearDepth ( 1.0 ) glDepthFunc ( GL_LEQUAL ) glEnable ( GL_DEPTH_TEST ) glEnable ( GL_TEXTURE_2D ) glHint ( GL_POLYGON_SMOOTH_HINT, GL_NICEST ) glHint ( GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST ) def reshape ( width, height ): glViewport ( 0, 0, width, height ) glMatrixMode ( GL_PROJECTION ) glLoadIdentity () gluPerspective ( 60.0, float(width)/float (height), 1.0, 60.0 ) glMatrixMode ( GL_MODELVIEW ) glLoadIdentity () gluLookAt ( 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 0.0, 1.0, 0.0 ) def display (): glClear ( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ) glMatrixMode ( GL_MODELVIEW ) glLoadIdentity () program.bind () gluLookAt ( 0, 0, 0, 1, 1, 1, 0, 1, 0 ) glTranslatef ( 4, 4, 4 ) glRotatef ( angle2, 1, -1, 0 ) glTranslatef ( d - 1.5, d - 1.5, d - 1.5 ) glTranslatef ( 1.5, 1.5, 1.5 ) # move cube from the center glRotatef ( angle, 1.0, 1.0, 0.0 ) glTranslatef ( -1.5, -1.5, -1.5 ) # move cube into the center drawBox ( 1, 2, 1, 2, 1, 2, texture ) program.unbind () glutSwapBuffers () def keyPressed ( *args ): if args [0] == '\033': sys.exit () def animate (): global angle, angle2 angle = 0.04 * glutGet ( GLUT_ELAPSED_TIME ) angle2 = 0.01 * glutGet ( GLUT_ELAPSED_TIME ) glutPostRedisplay () def main (): global texture, program glutInit ( sys.argv ) glutInitDisplayMode ( GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH ) glutInitWindowSize ( 500, 500 ) glutInitWindowPosition ( 0, 0 ) glutCreateWindow ( "Simple PyOpenGL example" ) glutDisplayFunc ( display ) glutIdleFunc ( animate ) glutReshapeFunc ( reshape ) glutKeyboardFunc ( keyPressed ) init () initExtensions () vSource = open ( "simplest.vsh" ).read () fSource = open ( "simplest.fsh" ).read () program = GlslProgram.GlslProgram () program.loadShaders ( vSource, fSource ); texture = loadTexture ( "../../Textures/block.bmp" ) print program.getLog () glutMainLoop() print "Hit ESC key to quit." main()
Ниже приводится пример использования как модуля GlslProgram, так и модуля FrameBuffer для реализации эффекта постообработки sepia.
from OpenGL.GL import * from OpenGL.GLU import * from OpenGL.GLUT import * from OpenGL.GL.ARB.vertex_shader import * from OpenGL.GL.ARB.fragment_shader import * from OpenGL.GL.ARB.shader_objects import * from OpenGL.GL.ARB.shading_language_100 import * from OpenGL.GL.ARB.texture_cube_map import * import Image # PIL import sys import math import GlslProgram import FrameBuffer from utils import * decalMap = None stoneMap = None teapotMap = None screenMap = None angle = 0.0 rot = 0.0 angle = 0.0 useFilter = 1 program = None buffer = None def initExtensions (): if not glInitShaderObjectsARB (): sys.exit ( 1 ) if not glInitShadingLanguage100ARB (): sys.exit ( 1 ) if not glInitFragmentShaderARB (): print "Fragment shaders not supported" sys.exit ( 1 ) if not glInitVertexShaderARB (): print "Vertex shaders not supported" sys.exit ( 1 ) def displayBoxes (): global angle, rot, stoneMap, decalMap, teapotMap glMatrixMode ( GL_MODELVIEW ) glPushMatrix () glRotatef ( rot, 0, 0, 1 ) drawBoxCull ( -5, -5, 0, 10, 10, 3, stoneMap, None ) drawBoxCull ( 3, 2, 0.5, 1, 2, 2, decalMap, 1 ) glBindTexture ( GL_TEXTURE_2D, teapotMap ) glTranslatef ( 0.2, 1, 1.5 ) glRotatef ( angle * 45.3, 1, 0, 0 ) glRotatef ( angle * 57.2, 0, 1, 0 ) glutSolidTeapot ( 0.3 ) glPopMatrix () def startOrtho (): glMatrixMode ( GL_PROJECTION ) # select the projection matrix glPushMatrix () # store the projection matrix glLoadIdentity () # reset the projection matrix # set up an ortho screen glOrtho ( 0, 512, 0, 512, -1, 1 ) glMatrixMode ( GL_MODELVIEW ) # select the modelview matrix glPushMatrix () # store the modelview matrix glLoadIdentity () # reset the modelview matrix def endOrtho (): glMatrixMode ( GL_PROJECTION ) # select the projection matrix glPopMatrix () # restore the old projection matrix glMatrixMode ( GL_MODELVIEW ) # select the modelview matrix glPopMatrix () # restore the old projection matrix def init (): glClearColor ( 0.0, 0.0, 0.0, 0.0 ) glClearDepth ( 1.0 ) glDepthFunc ( GL_LEQUAL ) glEnable ( GL_DEPTH_TEST ) glEnable ( GL_TEXTURE_2D ) glHint ( GL_POLYGON_SMOOTH_HINT, GL_NICEST ) glHint ( GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST ) def reshape ( width, height ): glViewport ( 0, 0, width, height ) glMatrixMode ( GL_PROJECTION ) glLoadIdentity () gluPerspective ( 60.0, float(width)/float (height), 1.0, 60.0 ) glMatrixMode ( GL_MODELVIEW ) glLoadIdentity () gluLookAt ( -0.5, -0.5, 1.5, 3.0, 3.0, 1.0, 0.0, 0.0, 1.0 ) def display (): global screenMap, program, useFilter renderToBuffer () glClear ( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ) startOrtho () glBindTexture ( GL_TEXTURE_2D, screenMap ) if useFilter: program.bind () glBegin ( GL_QUADS ) glTexCoord2f ( 0, 0 ) glVertex2f ( 0, 0 ) glTexCoord2f ( 1, 0 ) glVertex2f ( 512, 0 ) glTexCoord2f ( 1, 1 ) glVertex2f ( 512, 512 ) glTexCoord2f ( 0, 1 ) glVertex2f ( 0, 512 ) glEnd () if useFilter: program.unbind () endOrtho () glutSwapBuffers () def keyPressed ( *args ): global useFilter if args [0] == '\033' or args [0] == 'q' or args [0] == 'Q': sys.exit () if args [0] == 'f' or args [0] == 'F': useFilter = not useFilter def specialKey ( key, x, y ): global rot if key == GLUT_KEY_LEFT: rot -= 5.0 elif key == GLUT_KEY_RIGHT: rot += 5.0 else: return glutPostRedisplay() def animate (): global angle, program angle = 0.004 * glutGet ( GLUT_ELAPSED_TIME ) glutPostRedisplay () def renderToBuffer (): global buffer glBindTexture ( GL_TEXTURE_2D, 0 ) buffer.bind () glClearColor ( 0, 0, 1, 1 ) glClear ( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ) reshape ( buffer.width, buffer.height ) displayBoxes () buffer.unbind () def main (): global decalMap, stoneMap, teapotMap, screenMap, program, buffer glutInit ( sys.argv ) glutInitDisplayMode ( GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH ) glutInitWindowSize ( 512, 512 ) glutInitWindowPosition ( 0, 0 ) glutCreateWindow ( "PyOpenGL sepia example" ) glutDisplayFunc ( display ) glutIdleFunc ( animate ) glutReshapeFunc ( reshape ) glutKeyboardFunc ( keyPressed ) glutSpecialFunc ( specialKey ) init () initExtensions () vSource = open ( "sepia.vsh" ).read () fSource = open ( "sepia.fsh" ).read () program = GlslProgram.GlslProgram () program.loadShaders ( vSource, fSource ); decalMap = loadTexture ( "../../Textures/oak.bmp" ) stoneMap = loadTexture ( "../../Textures/block.bmp" ) teapotMap = loadTexture ( "../../Textures/Oxidated.jpg" ) buffer = FrameBuffer.FrameBuffer ( 512, 512, 4 ) screenMap = buffer.createColorTexture (); buffer.create () buffer.bind () buffer.attachColorTexture ( GL_TEXTURE_2D, screenMap ) program.bind () program.setTexture ( "mainTex", 0 ) program.unbind () glutMainLoop() print "Hit ESC key to quit." main()
По этой ссылке можно скачать весь исходный код к этой статье. Код является кроссплатформенным и расширения компилируются под Linux и Mac OS X.