Понимание OpenGL через Python

3 min


Введение

Следующий эта статья Мухаммед Джунаид Халид, где основной OpenGL Понятия и настройки были объяснены, теперь мы рассмотрим, как сделать больше сложный объекты и как одушевленный их.

OpenGL очень старый, и вы не найдете много онлайн-уроков о том, как правильно его использовать и понять, потому что все лучшие собаки уже по колено в новых технологиях.

Чтобы понять современный код OpenGL, вы должны сначала понять древний концепции, которые были написаны на каменных табличках мудрыми разработчиками игр майя.

В этой статье мы рассмотрим несколько фундаментальных тем, которые вам нужно знать:

В последнем разделе мы рассмотрим, как на самом деле использовать OpenGL с библиотеками Python. PyGame а также PyOpenGL,

В следующей статье (скоро будет!) мы более подробно рассмотрим, как использовать OpenGL с Python и библиотеками, упомянутыми выше.

Основные матричные операции

Чтобы правильно использовать многие функции в OpenGL, нам понадобится некоторая геометрия.

Каждый точка в пространстве может быть представлен с Декартовы координаты, Координаты представляют местоположение любой данной точки, определяя ее Икс, Y а также Z ценности.

Мы будем практически использовать их как 1×3 матрицыточнее 3-х мерный векторы (подробнее о матрицах позже).

Вот примеры некоторых координат:

знак равно(5,3,4) бзнак равно(9,1,2)

a а также b будучи точками в пространстве, их х-координаты 5 а также 9 соответственно, координаты у 3 а также 1, и так далее.

В компьютерной графике, чаще всего, гомогенный координаты используются вместо обычных старых декартовых координат. Они в основном одно и то же, только с дополнительным служебным параметром, который для простоты мы будем говорить всегда 1,

Так что, если правильные координаты a находятся (5,3,4)соответствующие однородные координаты будут (5,3,4,1), За этим стоит много геометрических теорий, но в этой статье это не обязательно.

Далее, важным инструментом для представления геометрических преобразований являются матрицы, Матрица в основном двумерный массив (в данном случае размер п * пдля них очень важно иметь одинаковое количество строк и столбцов).

Матричные операции чаще всего довольно просты, такие как сложение, вычитание и т. Д. Но, конечно, самая важная операция должна быть самой сложной – умножение. Давайте посмотрим на примеры основных операций с матрицами:

знак равно(125619552)Пример матрицы (125619552)+(25101221810104)знак равно(37151832715156)Сложение матрицы (24101221810104)(125619552)знак равно(125619552)Матричное вычитание

Теперь, когда все математика имеет тенденцию делать, это становится относительно сложным, когда вы действительно хотите что-то практическое из этого.

Формула для умножения матриц выглядит следующим образом:

$$
c (i, j) = sum_ {k = 1} ^ {n} a (i, k) * b (k, j)
$$

c будучи полученной матрицей, a а также b будучи мультипликатором и множителем.

На самом деле, есть простое объяснение этой формулы. Каждый элемент может быть построен путем суммирования произведений всех элементов в iая строка и jстолбец Это причина, почему в a(i,k), i фиксируется и k используется для перебора элементов соответствующей строки. Тот же принцип может быть применен к b(k,j),

Зная это, мы должны выполнить дополнительное условие, чтобы мы могли использовать матричное умножение. Если мы хотим умножить матрицы A а также B размеров a*b а также c*d, Количество элементов в одной строке в первой матрице (b) должно быть таким же, как количество элементов в столбце во второй матрице (c), так что формула выше может быть использована правильно.

Очень хорошим способом визуализации этой концепции является выделение строк и столбцов, элементы которых будут использоваться при умножении для данного элемента. Представьте себе два выделенных линии друг над другом, как будто они находятся в одной матрице.

Элемент, в котором они пересекаются, является позицией результирующего элемента суммирования их произведений:

Умножение матриц очень важно, потому что, если мы хотим объяснить следующее выражение простыми словами: A*B (А и В – матрицы), мы бы сказали:

Мы трансформируем А, используя Б.

Вот почему матричное умножение является наиболее существенным инструментом для преобразования любого объекта в OpenGL или геометрии в целом.

Последнее, что вам нужно знать о матричном умножении, это то, что он имеет нейтральный, Это означает, что существует уникальный элемент (в данном случае матрица) E который при умножении на любой другой элемент A не меняется Aзначение, то есть:

$$
(! Существует {E} forall {A}) E * A = A
$$

Восклицательный знак в сочетании с существующим символом означает: Существует уникальный элемент E, который …

В случае умножения с нормальными целыми числами, E имеет значение 1, В случае матриц E имеет следующие значения в нормальном картезианский (E1) а также однородные координаты (E2) соответственно:

Е1знак равно(100010001)Е2знак равно(1000010000100001)

Каждое геометрическое преобразование имеет свою уникальную матрицу преобразования, которая имеет своего рода шаблон, из которых наиболее важными являются:

  • Перевод
  • пересчет
  • отражение
  • вращение
  • Sheering

Перевод

Перевод – это акт буквального перемещения объекта по заданному вектору. Объект, на который влияет преобразование, никак не меняет свою форму и не меняет ориентацию – он просто перемещается в пространстве (поэтому перевод классифицируется как движение преобразование).

Перевод может быть описан в следующей матричной форме:

Tзнак равно(100TИкс010TY001TZ0001)

t-s представляет, насколько объект x,y а также z значения местоположения будут изменены.

Итак, после того, как мы преобразуем любые координаты с матрицей перевода T, мы получили:

$$
(Х, у, г) * Т = (T_x + х, t_y + у, t_z + Z)
$$

Перевод осуществляется с помощью следующей функции OpenGL:

void glTranslatef(GLfloat tx, GLfloat ty, GLfloat tz);

Как вы можете видеть, если мы знаем форму матрицы перевода, понять функцию OpenGL очень просто, это относится ко всем преобразованиям OpenGL.

Не против GLfloatЭто просто умный тип данных для OpenGL для работы на нескольких платформах, вы можете посмотреть на это так:

typedef float GLfloat;
typedef double GLdouble;
typedef someType GLsomeType;

Это необходимая мера, потому что не все системы имеют одинаковое пространство для хранения char, например.

вращение

Вращение – немного более сложное преобразование, потому что оно зависит от двух факторов:

  • Pivot: Вокруг какой линии в 3D-пространстве (или в 2D-пространстве) мы будем вращаться
  • Количество: На сколько (в градусах или радианах) мы будем вращаться

Из-за этого нам сначала нужно определить вращение в 2D-пространстве, и для этого нам понадобится немного тригонометрии.

Вот краткий справочник:

Эти тригонометрические функции могут использоваться только внутри прямоугольного треугольника (один из углов должен быть 90 градусов).

Базовая матрица вращения для вращения объекта в 2D пространстве вокруг вершины (0,0) под углом A идет следующим образом:

(соssяN0sяNсоs0001)

Опять же, 3-я строка и 3-й столбец – на тот случай, если мы хотим разместить трансляционные преобразования поверх других трансформаций (что мы будем делать в OpenGL), это нормально, если вы не до конца понимаете, почему они там сейчас. Все должно проясниться в примере составного преобразования.

Это все было в 2D-пространстве, теперь давайте перейдем к 3D-пространству. В трехмерном пространстве нам нужно определить матрицу, которая может вращать объект вокруг Любые линия.

Как однажды сказал мудрый человек: «Держи это просто и глупо!» К счастью, математические волшебники на этот раз сделали это просто и глупо.

Каждый поворот вокруг линии можно разбить на несколько преобразований:

  • Вращение вокруг оси х
  • Вращение вокруг оси Y
  • Вращение вокруг оси z
  • Сервисные переводы (которые будут затронуты позже)

Таким образом, единственные три вещи, которые нам нужно построить для любого трехмерного вращения, это матрицы, которые представляют вращение вокруг x, y, а также z ось под углом A:

рИксзнак равно(10000соssяN00sяNсоs00001)рYзнак равно(соs0sяN00100sяN0соs00001)рZзнак равно(соssяN00sяNсоs0000100001)

3D вращение реализовано с помощью следующей функции OpenGL:

void glRotatef(GLfloat angle, GLfloat x, GLfloat y, GLfloat z);
  • angle: угол поворота в градусах (0-360)
  • x,y,z: вектор, вокруг которого выполняется вращение

пересчет

Масштабирование – это умножение любого измерения целевого объекта на скаляр, Этот скаляр может быть <1 если мы хотим уменьшить объект, и это может быть >1 если мы хотим увеличить объект.

Масштабирование может быть описано в следующей матричной форме:

Sзнак равно(sИкс0000sY0000sZ00001)

sИкс, sY, sZ скаляры, которые умножаются на x, y, а также z значения целевого объекта.

После того, как мы преобразуем любые координаты с помощью матрицы масштабирования S мы получили:

(Икс,Y,Z)*Sзнак равно(sИкс*Икс,sY*Y,sZ*Z)

Это преобразование особенно полезно при масштабировании объекта по фактору К (это означает, что результирующий объект в два раза больше), это достигается установкой sИксзнак равноsYзнак равноsZзнак равноК:

(Икс,Y,Z)*Sзнак равно(sИкс*Икс,sY*Y,sZ*Z)

Особый случай масштабирования известен как отражение, Это достигается путем установки либо sИкс, sY, или же sZ в -1, Это просто означает, что мы инвертируем знак одной из координат объекта.

Проще говоря, мы помещаем объект на другую сторону x, y, или же z ось.

Это преобразование можно изменить, чтобы оно работало для любого отражения, но нам оно пока не нужно.

void glScalef(GLfloat sx, GLfloat sy, GLfloat sz);

Композитные преобразования

Составные преобразования - это преобразования, которые состоят из более чем 1 базового преобразования (перечислены выше). Трансформации A а также B объединяются матрицей, умножающей соответствующие матрицы преобразования M_a а также M_b,

Это может показаться очень простой логикой, однако есть некоторые вещи, которые могут сбить с толку. Например:

  • Умножение матриц не коммутируемо:
*ВВ* А и В являются матрицами
  • Каждое из этих преобразований имеет обратное преобразование. Обратное преобразование - это преобразование, которое отменяет исходное:
Tзнак равно(100010б001с0001)T-1знак равно(100-010-б001-с0001)Езнак равно(1000010000100001) T*T-1знак равноЕ
  • Когда мы хотим сделать обратное для составного преобразования, мы должны изменить порядок используемых элементов:
(*В*С)-1знак равноС-1*В-1*-1

Дело в том, что топологический порядок использования матрицы очень важен, так же как и подъем на определенный этаж здания.

Если вы находитесь на первом этаже и хотите попасть на четвертый этаж, сначала вам нужно пройти на третий этаж, а затем на четвертый.

Но если вы хотите спуститься обратно на второй этаж, вам нужно будет перейти на третий этаж, а затем на второй этаж (в обратном топологическом порядке).

Преобразования, включающие реферальную точку

Как упоминалось ранее, когда необходимо выполнить преобразование относительно определенной точки в пространстве, например, вращение вокруг реферальной точки. A=(a,b,c) в трехмерном пространстве, а не в начале координат O=(0,0,0)нам нужно превратить эту реферальную точку A в O переводя все T(-a,-b,-c),

Тогда мы можем сделать любое преобразование, которое нам нужно сделать, и когда мы закончим, перевести все обратно T(a,b,c), так что оригинальное происхождение O снова имеет координаты (0,0,0),

Матричная форма этого примера:

T*M*T-1знак равно(100-010-б001-с0001)*M*(100010б001с0001)

где M это преобразование, которое мы хотим сделать на объекте.

Весь смысл изучения этих матричных операций заключается в том, чтобы вы могли полностью понять, как работает OpenGL.

Демонстрация моделирования

После всего этого давайте рассмотрим простую демонстрацию моделирования.

Чтобы что-то делать с OpenGL через Python, мы будем использовать два модуля - PyGame и PyOpenGL:

$ python3 -m pip install -U pygame --user
$ python3 -m pip install PyOpenGL PyOpenGL_accelerate

Поскольку излишне загружать на себя теорию графики на 3 книги, мы будем использовать библиотеку PyGame. Это существенно сократит процесс от инициализации проекта до фактического моделирования и анимации.

Для начала нам нужно импортировать все необходимое из OpenGL и PyGame:

import pygame as pg
from pygame.locals import *

from OpenGL.GL import *
from OpenGL.GLU import *

В следующем примере мы можем видеть, что для моделирования нетрадиционного объекта нам нужно знать только то, как сложный объект можно разбить на более мелкие и простые части.

Поскольку мы до сих пор не знаем, что делают некоторые из этих функций, я дам некоторые определения уровня поверхности в самом коде, чтобы вы могли увидеть, как можно использовать OpenGL. В следующей статье все они будут подробно рассмотрены - это просто для того, чтобы дать вам общее представление о том, как выглядит работа с OpenGL:

def draw_gun():
    # Setting up materials, ambient, diffuse, specular and shininess properties are all
    # different properties of how a material will react in low/high/direct light for
    # example.
    ambient_coeffsGray = (0.3, 0.3, 0.3, 1)
    diffuse_coeffsGray = (0.5, 0.5, 0.5, 1)
    specular_coeffsGray = (0, 0, 0, 1)
    glMaterialfv(GL_FRONT, GL_AMBIENT, ambient_coeffsGray)
    glMaterialfv(GL_FRONT, GL_DIFFUSE, diffuse_coeffsGray)
    glMaterialfv(GL_FRONT, GL_SPECULAR, specular_coeffsGray)
    glMateriali(GL_FRONT, GL_SHININESS, 1)

    # OpenGL is very finicky when it comes to transformations, for all of them are global,
    # so it's good to seperate the transformations which are used to generate the object
    # from the actual global transformations like animation, movement and such.
    # The glPushMatrix() ----code----- glPopMatrix() just means that the code in between
    # these two functions calls is isolated from the rest of your project.
    # Even inside this push-pop (pp for short) block, we can use nested pp blocks,
    # which are used to further isolate code in it's entirety.
    glPushMatrix()

    glPushMatrix()
    glTranslatef(3.1, 0, 1.75)
    glRotatef(90, 0, 1, 0)
    glScalef(1, 1, 5)
    glScalef(0.2, 0.2, 0.2)
    glutSolidTorus(0.2, 1, 10, 10)
    glPopMatrix()

    glPushMatrix()
    glTranslatef(2.5, 0, 1.75)
    glScalef(0.1, 0.1, 1)
    glutSolidCube(1)
    glPopMatrix()

    glPushMatrix()
    glTranslatef(1, 0, 1)
    glRotatef(10, 0, 1, 0)
    glScalef(0.1, 0.1, 1)
    glutSolidCube(1)

    glPopMatrix()

    glPushMatrix()
    glTranslatef(0.8, 0, 0.8)
    glRotatef(90, 1, 0, 0)
    glScalef(0.5, 0.5, 0.5)
    glutSolidTorus(0.2, 1, 10, 10)
    glPopMatrix()

    glPushMatrix()
    glTranslatef(1, 0, 1.5)
    glRotatef(90, 0, 1, 0)
    glScalef(1, 1, 4)
    glutSolidCube(1)
    glPopMatrix()

    glPushMatrix()
    glRotatef(8, 0, 1, 0)
    glScalef(1.1, 0.8, 3)
    glutSolidCube(1)
    glPopMatrix()

    glPopMatrix()

def main():
    # Initialization of PyGame modules
    pg.init()
    # Initialization of Glut library
    glutInit(sys.argv)
    # Setting up the viewport, camera, backgroud and display mode
    display = (800,600)
    pg.display.set_mode(display, DOUBLEBUF|OPENGL)
    glClearColor(0.1,0.1,0.1,0.3)
    gluPerspective(45, (display(0)/display(1)), 0.1, 50.0)
    gluLookAt(5,5,3,0,0,0,0,0,1)

    glTranslatef(0.0,0.0, -5)
    while True:
        # Listener for exit command
        for event in pg.event.get():
            if event.type == pg.QUIT:
                pg.quit()
                quit()

        # Clears the screen for the next frame to be drawn over
        glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT)
        ############## INSERT CODE FOR GENERATING OBJECTS ##################
        draw_gun()
        ####################################################################
        # Function used to advance to the next frame essentially
        pg.display.flip()
        pg.time.wait(10)

Вся эта куча кода дает нам:

Заключение

OpenGL очень старый, и вы не найдете много онлайн-уроков о том, как правильно его использовать и понять, потому что все лучшие собаки уже по колено в новых технологиях.

Чтобы правильно использовать OpenGL, нужно понять основные понятия, чтобы понять реализации через функции OpenGL.

В этой статье мы рассмотрели основные матричные операции (перемещение, вращение и масштабирование), а также составные преобразования и преобразования, включающие реферальную точку.

В следующей статье мы будем использовать PyGame а также PyOpenGL инициализировать проект, рисовать объекты, анимировать их и т. д.


0 Comments

Ваш e-mail не будет опубликован. Обязательные поля помечены *