|
Главная
Статьи
Ссылки
Скачать
Скриншоты
Юмор
Почитать
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.