Я хотів створити серію уроків про графіку в 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