Я хотів створити серію уроків про графіку в OpenGL по слідах NeHe, але отримав іншу пропозицію, і пріоритети змінились. Ну й графіка в наш час людей не так цікавить. Але так як задачу я почав робити, просто витирати її з списку проектів буде не цікаво, краще опублікувати те що є і перенести в список закінчених проектів. Чим я зараз й займусь.
Ідея програми – намалювати атоми сферами різних кольорів і розмістити їх в різних місцях простору, таким чином отримавши молекулу. Для цього нам треба знати координати. Для цього ми використаємо Open Babel – хімічну експертну систему. Ось інструкції з інсталяції, apt-get install python-openbabel
якщо кому лінь їх читати.
Користуючись нею, ми можемо перетворити формулу SMILES, на список координат атомів:
import pybel smile = raw_input('Enter SMILE molecule:') molecule = pybel.readstring('smi', smile) molecule.make3D() for atom in molecule.atoms: print atom.type, ' '.join(map(str, atom.coords))
SMILES можна знайти в статтях вікіпедії про різні речовини. Ось наприклад глюкоза: OC[C@H]1OC(O)[C@H](O)[C@@H](O)[C@@H]1O
. І її координати:
O3 3.08232699168 1.41136753836 1.97383867659 C3 2.63605783234 0.116724346362 2.37092125466 C3 2.87897272901 -0.854338624684 1.21070216538 H 2.47296741411 -0.36351143938 0.319191621385 O3 4.29331227545 -1.03408976253 1.03052197614 C3 2.18198168708 -2.2062426467 1.4123437219 H 2.52686439483 -2.70828022178 2.32628341407 O3 0.757046712076 -2.07375697965 1.48816465135 C3 2.4834103428 -3.09613160782 0.198042289604 H 2.03158506981 -2.66605342385 -0.704362586236 O3 1.84366255476 -4.36331585678 0.373985237387 C3 3.99532722341 -3.24522583591 0.00523555683618 H 4.41250004827 -3.77444425583 0.871020029013 O3 4.29626886035 -4.03252960027 -1.15195954921 C3 4.62954125722 -1.84273135567 -0.0947112115177 H 5.71887138656 -1.95252927548 -0.108137148969 O3 4.3079363412 -1.18316983652 -1.31789119536 HO 2.91473185309 2.02688340774 2.71060639162 H 1.56956736943 0.19992674022 2.5985040442 H 3.17829013287 -0.18365904297 3.27321534863 HO 0.532102901531 -1.64213847492 2.33074495499 HO 0.907383263524 -4.15051515566 0.55676359202 HO 3.89297302828 -3.59188323274 -1.91963488602 HO 3.3506978517 -1.05986229308 -1.37211748251
Ок, залишилось написати програму що бере оці координати і створює таку картинку як у цій публікації (увага, ввесь код звідси і аж до кінця публікації – це одна програма):
#! /usr/bin/python3 from random import random import pyglet from pyglet.window import key, Window from pyglet.gl import * from pyglet.gl.glu import * window = Window()
Об’єкт наступного класу просто буде повертати кортеж з кольором для кожної назви атома. Деякі атоми ми задамо вручну, щодо правильної палітри – дивіться статтю CPK coloring.
class Palette(object): def __init__(self): self.colors = { 'H': (0.0, 0.5, 0.5), 'HO': (1.0, 0.5, 0.5), 'C3': (0.1, 0.1, 0.1), 'Car': (0.1, 0.1, 0.1), 'O3': (1.0, 0.0, 0.0), } def get_color(self, name): if name not in self.colors: print(name) self.colors[name] = (random(), random(), random()) return self.colors[name] palette = Palette()
Молекула – це по суті список атомів (кожен з яких четвірка з назви і трьох координат), що буде завантажувати себе з файлу при створенні екземпляру класу, і вміє малювати себе:
class Molecule(object): def __init__(self, fn): self.atoms = [] with open(fn) as f: for l in f: el, x, y, z = l.split() self.atoms.append( (el, float(x), float(y), float(z)) ) def draw(self): for atom in self.atoms: # для кожного атома glPushMatrix() # зберегти матрицю моделі glTranslatef(*atom[1:]) # змістити матрицю моделі в координати атома # намалювати сферу радіусу 1 і кольору відповідного типу атома draw_sphere(1, palette.get_color(atom[0])) glPopMatrix() # завантажити збережену матрицю моделі molecule = Molecule('glucose.dat') # створити молекулу глюкози def draw_sphere(radius, color): glPolygonMode(GL_FRONT_AND_BACK, GL_FILL) # довго пояснювати що таке колір матеріалу, я й сам не до кінця знаю. glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, (GLfloat * 3)(*color)) glMaterialfv(GL_FRONT_AND_BACK, GL_EMISSION, (GLfloat * 3)(*map(lambda x: x/2, color)) ) # glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, (GLfloat * 3)(*color)) sphere = gluNewQuadric() gluSphere(sphere, radius, 50, 50) # 50, 50 - це кількість меридіанів та паралелей. # якщо потрібно багато атомів - зменшіть їх кількість для збільшення продуктивності.
Тепер займемось власне перемальовуванням екрану:
@window.event def on_draw(): update_frame(0) rotation = 0 # Глобальна зміна з поточним поворотом моделі def update_frame(dt): glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) glLoadIdentity() # завантажити матрицю ідентичності global rotation rotation += dt * 10 # чим більше часу пройшло - тим більше повертаємо glRotatef(rotation, 0, 1, 0) # навколо осі y molecule.draw() # і малюємо нашу молекулу.
При зміні розмірів вікна (і при його створенні) ініціалізуємо всілякі налаштування OpenGL:
@window.event def on_resize(width, height): glClearColor(0.0, 0.3, 0.0, 0.0) # задаємо колір фону glEnable(GL_DEPTH_TEST) # вмикаємо буфер глибини glEnable(GL_LIGHTING) glEnable(GL_LIGHT0) glLightf(GL_LIGHT0, GL_POSITION, 1, 5, 4) # ставимо одне світло glViewport(0, 0, width, height) glMatrixMode(GL_PROJECTION) glLoadIdentity() gluPerspective(45, width / height, .1, 1000) # перспективна проекція з кутом 45 gluLookAt( # ставимо камеру і націлюємо її в цент сцени 1, 4, 15, # eye 0, 0, 0, # target 0, 1, 0 # up ) glMatrixMode(GL_MODELVIEW) return pyglet.event.EVENT_HANDLED
При натисканні клавіш “вліво” і “вправо” оновлюємо кадр, повернувши трішки модель. А також оновлюємо 50 разів на секунду. І запускаємо цикл подій:
@window.event def on_key_press(symbol, modifiers): if symbol == key.LEFT: update_frame(-1) elif symbol == key.RIGHT: update_frame(1) pyglet.clock.schedule_interval(update_frame, 0.02) pyglet.app.run()
На цьому і все. Можна було звісно написати набагато краще, без глобальних змінних, з кращими поворотами камери і кращим освітленням і т.п. Але поки що є важливіші речі. (Хоча, якщо ви захочете онлайн курс, і зможете зробити так що мені не треба буде ходити на роботу – можемо щось придумати ;) ).
Ах, і стаття з якої взято інформацію про те як отримати координати для атомів молекули: Patrick Fuller – Molecules in Blender
Filed under: Графіка, Кодерство Tagged: освіта, OpenGL, Python
