Популярные услуги

Все письменные КМ под ключ за 7 суток! (КМ-1 + КМ-2 + КМ-3 + КМ-4 + КМ-5)
КМ-6. Динамические массивы. Семинар - выполню любой вариант!
КМ-2. Разработка простейших консольных программ с использованием ООП + КМ-4. Более сложные элементы ООП - под ключ!
Оба семинара по программированию под ключ! КМ-2. Разработка циклических алгоритмов + КМ-3. Функции и многофайловые программы в Си
Одно любое задание в mYsql
Любая задача на C/C++
Сделаю ваше задание: Лабораторная работа на Pascal / Lazarus
Любой тест по базам данных максимально быстро на хорошую оценку - или верну деньги!
Любой реферат по объектно-ориентированному программированию (ООП)
Повышение уникальности твоей работе

OpenGL

2021-03-09СтудИзба

· Лекция 4. OpenGL.

Что такое OpenGL – дословно, Open Graphics Library, открытая графическая библиотека. OpenGL включает в свой состав около 200 команд. Набор команд и их интерфейс определяется стандартом, разработанным фирмой Silicon Graphics.

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

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

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

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

Среди наиболее известных: библиотека утилит OpenGL (GLU OpenGL Utility Library), инструментарий утилит библиотеки OpenGL (GLUT – Graphics Library Utility Toolkit) расширение OpenGL для X Window System (GLX – OpenGL Extention to the X Window System), библиотека для работы с оконной системой MS Windows (GlAux – OpenGL Auxilary) и другие.

· Синтаксис команд OpenGL

Обычно команды OpenGL реализуются в виде процедур или функций. Их имена начинаются с префикса gl, затем следует название команды, в котором  каждое слово начинается с большой буквы. Это особенно важно для языков, учитывающих регистр букв в тексте программы, например для Java. После названия может присутствовать суффикс, определяющий количество и тип параметров. Так команда, определяющая вершину, может иметь несколько реализаций. Например:

Рекомендуемые материалы

glVertex2f – задает вершину на плоскости, имеет два параметра каждый из которых представляет собой 32-разрядное число с плавающей точкой;

glVertex3i – задает вершину в пространстве, имеет три параметра каждый из которых представляет собой 32-разрядное целое число.

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

glCommandName[1 2 3 4][b s i f d ub us ui][v](список параметров), где

CommandName – имя команды;

[1 2 3 4] – допустимые значения числа аргументов команды;

[b s i f d ub us ui] – допустимые типы аргументов:

Символ

Тип OpenGL

Описание

b

GLbyte

8 разрядов, целое

s

GLshort

16 разрядов, целое

i

GLint,GLsizei

32 разряда, целое

f

GLfloat

32 разряда, плавающая точка

d

GLdouble,GLclampd

64 разряда, плавающая точка

ub

GLubyte,GLboolean

8 разрядов, беззнаковое целое

us

GLushort

16 разрядов, беззнаковое целое

ui

GLuint,LGenum

32 разряда, беззнаковое целое

Появление в суффиксе символа v означает, что в качестве аргумента используется  указатель на массив. Количество и тип элементов массива определяется другими символами суффикса.

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

Конкретные реализации библиотек могут отклоняться от приведенной схемы использования суффиксов. Некоторые языки программирования поддерживают  перегрузку процедур (overload), родовые функции (generic function) или  иной механизм, позволяющий создавать несколько реализаций одной процедуры, различающихся типом и количеством параметров. В реализации библиотек для этих языков суффиксы могут быть опущены.

Если команды принадлежат библиотекам, расширяющим возможности OpenGL, то они имеют префиксы, отражающие название библиотеки. Например, команды, начинающиеся с префикса glu или glut, находятся, соответственно, в библиотеках GLU или GLUT. Мы будем использовать команды из этих библиотек для сокращения объема вспомогательного кода в примерах.

Константы в OpenGL  начинаются с префикса  GL и пишутся заглавными буквами. Но, в отличие от команд, слова в имени константы разделяются символами подчеркивания. Префикс также отделяется от имени. Примерами могут служить константы GL_LINE_STRIP и GL_COLOR_BUFFER_BIT.

Настройка библиотеки

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

Представим, что в нашем черном ящике скрывается конечный автомат. Следовательно,  исходя из свойств автомата, мы можем сказать следующее:

· Существует множество возможных состояний;

· При изменении  состояния автомата изменяются  выполняемые им преобразования;

· Существует некоторое начальное состояние автомата.

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

Некоторые из переменных принадлежат к  логическому типу и их значения показывают, включен или нет один из режимов. Как правило, изменение значений этих переменных производится с помощью команд glEnable() – разрешить и glDisable() – запретить. Например, команда glEnable(GL_LINE_SMOOTH) переводит систему в состояние (включает режим), в котором производится сглаживание линий.

Другие переменные определяют свойства преобразований. Они хранят числа, вектора и даже матрицы. Значения таких переменных устанавливаются различными специальными процедурами. Так команда glСolor3f(0.5, 0.5, 0.5) изменяет текущее значение переменной, отвечающей за цвет выводимых примитивов.

В любой момент времени можно запросить у системы текущие значения переменных состояния. Для этого используется одна из команд начинающаяся с glGet, например, glGetBoolean(), glGetFloatv(), glGetError(). Полный список переменных состояния, значения по умолчанию и связанные с ними команды можно узнать в справочном руководстве.

Часто мы хотим временно изменить значение переменных. Для этого опрашиваем и запоминаем текущее состояние, устанавливаем новое, затем производим вывод и восстанавливаем исходное состояние. Для облегчения данной процедуры и увеличения эффективности работы в библиотеке предусмотрен стек атрибутов. Занести значения в стек можно при помощи команд glPushAttrib() и glPushClientAttrib(), а для восстановления из стека используются команды glPopAttrib() и glPopClientAttrib.

Отдельно остановимся на переменных, определяющих матричные преобразования. В OpenGL используются четыре матрицы:

· Матрица видового преобразования  - GL_MODELVIEW;

· Матрица проецирования - GL_PROJECTION;

· Матрица текстуры - GL_TEXTURE;

· Матрица цвета – GL_COLOR_MATRIX.

По умолчанию все матрицы являются единичными. С каждым типом матриц связан свой стек. Текущей является матрица, находящаяся в вершине стека соответствующего типа. Количество матриц, которые можно поместить в стек, зависит от типа матрицы и конкретной реализации библиотеки.  Механизм доступа к матрицам единый, следовательно, для того, чтобы изменить конкретную матрицу, нужно установить режим изменения матриц этого типа с помощью команды glMatrixMode(). Так команда glMatrixMode(GL_MODELVIEW) выбирает для изменения матрицу видового преобразования. Рассмотрим некоторые разрешенные операции:

glLoadIdentity() - заменяет текущую матрицу выбранного типа единичной;

glPushMatrix() - заносит в стек копию текущей матрицы.

glPopMatrix()  -  извлекает матрицу из стека, делая ее текущей (можно рассматривать как удаление верхней матрицы из стека).

Задание аффинных преобразований

Начнем с команды, определяющей преобразование масштабирования glScale[f d] (sx, sy, sz). Здесь, может возникнуть недоразумение. Из названия интуитивно кажется, что эта команда должна выполнять масштабирование геометрических примитивов, но мы только настраиваем конвейер и о примитивах даже не упоминали! Все правильно, в OpenGL мы сначала задаем требуемые преобразования, а затем указываем примитивы, которые должны им подвергнуться.

Данная команда формирует матрицу масштабирования относительно начала координат S, перемножает ее с текущей матрицей M,  и заменяет текущую матрицу полученным результатом. То есть выполняет композицию M=M*S. Обратите внимание на то, что схема композиции отличается от рассмотренной нами. Сравним схемы. Наша схема p’= ((p*M1)*M2)*M3. Схема OpenGL p’= M3*(M2*(M1*p)). Очевидно, что основное отличие в порядке включения матриц в композицию. В OpenGL преобразования будут выполняться в порядке обратном порядку их объявления, т.е. трансформация, объявленная первой, будет применена к объекту последней. Такой подход оказывается более удобным при создании сцен, в чем мы убедимся при рассмотрении примера программы.

Еще раз оговорим, что данная команда изменяет текущую матрицу выбранного типа. Обычно это матрица видового преобразования - GL_MODELVIEW, так как мы чаще всего используем геометрические преобразования для сборки модели. Однако если вы выберете текущим типом матрицу проецирования, то произойдет композиция преобразования масштабирования с преобразованием проецирования. Необходимо обладать глубокими знаниями принципов работы геометрического конвейера, чтобы получить осмысленный результат от такого действия.

Остальные команды работают по той же схеме.

glRotate[f d](angle, x, y, z) - рассчитывает матрицу поворота на угол angle относительно вектора, определяемого тройкой (x, y, z), с точкой привязки в начале координат.

glTranslate[f d] (x, y, z) – рассчитывает матрицу переноса на вектор, определяемый тройкой (x, y, z), с точкой привязки в начале координат.

Координатные системы

OpenGL безусловно предназначена для работы с трехмерными моделями. Но знакомиться с принципами моделирования проще, манипулируя объектами на плоскости (мы без труда распространим полученные знания на пространственные модели). Поэтому, по возможности, отложим рассмотрение работы с 3D графикой до изучения принципов проецирования.

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

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

Описания объектов, поступая в конвейер, подвергаются видовому преобразованию, выполняемому с помощью одноименной матрицы. Видовое преобразование в конвейере OpenGL несет двойную нагрузку. Оно включает в себя преобразование модели и собственно видовое преобразование. Остановим наше внимание на преобразовании модели (видовое преобразование будет рассмотрено позже, согласно договоренности). С помощью данного преобразования мы можем произвести сборку сцены. Матрица видового преобразования (в данном контексте можно читать как матрица модельного преобразования) формируется не только перед выводом объектов, но и в его ходе. Следовательно, разные объекты или группы объектов могут преобразовываться по-разному. Другими словами, мы можем определить фрагмент сцены в собственных координатных системах, а за счет данного преобразования свести их вместе в единую координатную систему. В этой координатной системе определяется окно конвейера, задающее область видимости. Только попадающие в окно объекты или их фрагменты пройдут обработку в конвейере и будут отображены в поле вывода. Обычно собственные координатные системы фрагментов носят название модельных координат, а единая координатная система называется мировой системой координат. В OpenGL за счет двойной нагрузки на видовое преобразование мировые координаты названы видовыми.

 Задание векторных примитивов

В OpenGL векторными примитивами являются точки, линии и многоугольники. Они описываются в терминах их вершин – совокупностей координат. Как мы знаем, в разных координатных системах вершина может описываться парой, тройкой или четверкой координат. Смысл используемых понятий точки, линии и многоугольника отличается от математического. Это связано с ограниченными возможностями машинных вычислений, в частности с округлением и растеризацией.

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

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

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

glVertex[2 3 4][s i f d](координаты)

glVertex[2 3 4][s i f d][v](координаты).

Набор вершин ограничивается операторными скобками glBegin(mode) и glEnd(), а тип объекта, который он определяет, задается параметром  mode.

glBegin();

Команды задания вершин и их атрибутов, например цвета

glEnd;

mode – вид примитива, может принимать следующие значения:

· GL_POINTS – каждая вершина задает точку. Вершина n задает точку n. Всего будет выведено N точек;

· GL_LINES – две вершины образуют отрезок прямой линии. Вершины 2n-1 и 2n задают линию n. Всего будет выведено N/2 линий. Если число вершин нечетно, то последняя игнорируется;

· GL_LINE_STRIP - последовательность соединенных отрезков прямых, называемых полилинией. Вершины n и n+1 задают линию n. Всего будет выведено N-1 линий;

· GL_LINE_LOOP - замкнутая полилиния. Вершины n и n+1 задают ли­нию n. Вершины N и 1 задают последний отрезок полилинии. Всего бу­дет выведено N линий;

· GL_TRIANGLES - тройка вершин образует независимый треугольник. Вершины 3n-2, 3n-1 и Зn задают треугольник n. Если число вершин не кратно 3, то оставшиеся (одна или две) вершины игнорируются. Всего отобразится N/3 треугольников;

· GL_TRIANGLE_STRIP - связанные треугольники. Треугольник задается каждой вершиной, начиная с третьей. Для нечетного n вершины n, n+1 и n+2 задают треугольник n. Для четной вершины n вершины n+1, n и n+2 задают треугольник n. Всего будет выведено N - 2 треугольников;

· GL_TRIANGLE_FAN - веер треугольников. Треугольник задается каждой вершиной, начиная с третьей. Вершины n, n+1 и n+2 задают треугольник n. Всего будет выведено N-2 треугольников;

· GL_QUADS - четверка вершин задает независимый четырехугольник. Вершины 4n-3, 4n-2, 4n-1 и 4n задают четырехугольник n. Если число вершин не кратно 4, то оставшиеся (одна. две или три) вершины игнорируется. Всего нарисуется N/4 четырехугольников;

· GL_QUAD_STRIP - связанные четырехугольники. Четырехугольник задается  для  каждой, кроме двух первых, пары вершин. Вершины 2n-1, 2n, 2n+2 и 2n+1 задают четырехугольник n. Всего отобразится (N-2)/2 четырехугольников. Заметим, что порядок сборки связных четырехугольников отличается от порядка, применяемого для независимых четырехугольников;

· GL_POLYGON – вершины от 1 до N задают один многоугольник.

· Порядок соединения вершин. Вершины

Порядок соединения вершин разного вида треугольников и четырех­угольников приведен на рисунках  ХХХХХХ.

Рисунок - Порядок обхода вершин при построении треугольников: а) – независимые треугольники; б) – связанные треугольники; в) – веер связанных треугольников

Режим формирования независимых треугольников - GL_TRIANGLES (рисунок ХХ, а) очевиден – берутся тройки вершин и соединяются между собой.

В режиме формирования связанных треугольников - GL_TRIANGLE_STRIP (рисунок ХХ, б) входящие в примитив треугольники составляют единую группу, в которой каждая пара соседних треугольников имеет общую сторону.

В режиме формирования веера связанных треугольников - GL_TRIANGLE_FAN (рисунок ХХ, в) входящие в примитив треугольники также образуют единую группу, но при этом все входящие в нее треугольники имеют общую вершину.

Рисунок – Порядок обхода вершин при построении четырехугольников: а – независимые четырехугольники; б – связанные четырехугольники

Общая схема формирования сцены с помощью библиотеки OpenGL будет включать в себя следующие этапы:

1. Инициализация библиотеки;

2. Задание общих параметров сцены;

3. Задание параметров фрагмента сцены;

4. Вывод описания фрагмента;

5. Повторение пунктов 3 и 4 до полного формирования сцены;

6. Закрытие библиотеки.

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

Мы не будем вникать в детали такой настройки и ограничимся использованием процедуры InitOpenGL из библиотеки GLUtils.

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

Другими свойствами, часто устанавливаемыми для всей сцены в целом, являются характеристики источников света, цвет фона и т.п.

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

В лекции "13. Возбудители ВИЧ" также много полезной информации.

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

· Большой объем предварительных вычислений, выполняемых вручную;

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

Рассмотрим реализацию процесса сборки сцены с помощью конвейера OpenGL. Композиция всех геометрических преобразований, определяемых пользователем, накапливается в матрице видового преобразования. Следовательно, можно сказать, что связь между мировой координатной системой и координатной системой, в которой будет описываться вывод в данной точке программы, определяется состоянием матрицы видового преобразования (композицией преобразований накопленных в матрице к моменту достижения данной точки программы). Например, если мы выполним команду glLoadIndentity, предварительно установив текущей матрицу видового преобразования, то можно сказать, что вывод будет производиться в координатной системе, совпадающей с мировой. Если после этого с помощью команды glTranslate сформировать преобразование переноса на вектор T, то вывод будет осуществляться в новой координатной системе, начало которой сдвинуто относительно начала мировой системы координат на вектор Т.

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

Рассмотрим небольшой пример, иллюстрирующий вышесказанное.


Свежие статьи
Популярно сейчас
Как Вы думаете, сколько людей до Вас делали точно такое же задание? 99% студентов выполняют точно такие же задания, как и их предшественники год назад. Найдите нужный учебный материал на СтудИзбе!
Ответы на популярные вопросы
Да! Наши авторы собирают и выкладывают те работы, которые сдаются в Вашем учебном заведении ежегодно и уже проверены преподавателями.
Да! У нас любой человек может выложить любую учебную работу и зарабатывать на её продажах! Но каждый учебный материал публикуется только после тщательной проверки администрацией.
Вернём деньги! А если быть более точными, то автору даётся немного времени на исправление, а если не исправит или выйдет время, то вернём деньги в полном объёме!
Да! На равне с готовыми студенческими работами у нас продаются услуги. Цены на услуги видны сразу, то есть Вам нужно только указать параметры и сразу можно оплачивать.
Отзывы студентов
Ставлю 10/10
Все нравится, очень удобный сайт, помогает в учебе. Кроме этого, можно заработать самому, выставляя готовые учебные материалы на продажу здесь. Рейтинги и отзывы на преподавателей очень помогают сориентироваться в начале нового семестра. Спасибо за такую функцию. Ставлю максимальную оценку.
Лучшая платформа для успешной сдачи сессии
Познакомился со СтудИзбой благодаря своему другу, очень нравится интерфейс, количество доступных файлов, цена, в общем, все прекрасно. Даже сам продаю какие-то свои работы.
Студизба ван лав ❤
Очень офигенный сайт для студентов. Много полезных учебных материалов. Пользуюсь студизбой с октября 2021 года. Серьёзных нареканий нет. Хотелось бы, что бы ввели подписочную модель и сделали материалы дешевле 300 рублей в рамках подписки бесплатными.
Отличный сайт
Лично меня всё устраивает - и покупка, и продажа; и цены, и возможность предпросмотра куска файла, и обилие бесплатных файлов (в подборках по авторам, читай, ВУЗам и факультетам). Есть определённые баги, но всё решаемо, да и администраторы реагируют в течение суток.
Маленький отзыв о большом помощнике!
Студизба спасает в те моменты, когда сроки горят, а работ накопилось достаточно. Довольно удобный сайт с простой навигацией и огромным количеством материалов.
Студ. Изба как крупнейший сборник работ для студентов
Тут дофига бывает всего полезного. Печально, что бывают предметы по которым даже одного бесплатного решения нет, но это скорее вопрос к студентам. В остальном всё здорово.
Спасательный островок
Если уже не успеваешь разобраться или застрял на каком-то задание поможет тебе быстро и недорого решить твою проблему.
Всё и так отлично
Всё очень удобно. Особенно круто, что есть система бонусов и можно выводить остатки денег. Очень много качественных бесплатных файлов.
Отзыв о системе "Студизба"
Отличная платформа для распространения работ, востребованных студентами. Хорошо налаженная и качественная работа сайта, огромная база заданий и аудитория.
Отличный помощник
Отличный сайт с кучей полезных файлов, позволяющий найти много методичек / учебников / отзывов о вузах и преподователях.
Отлично помогает студентам в любой момент для решения трудных и незамедлительных задач
Хотелось бы больше конкретной информации о преподавателях. А так в принципе хороший сайт, всегда им пользуюсь и ни разу не было желания прекратить. Хороший сайт для помощи студентам, удобный и приятный интерфейс. Из недостатков можно выделить только отсутствия небольшого количества файлов.
Спасибо за шикарный сайт
Великолепный сайт на котором студент за не большие деньги может найти помощь с дз, проектами курсовыми, лабораторными, а также узнать отзывы на преподавателей и бесплатно скачать пособия.
Популярные преподаватели
Нашёл ошибку?
Или хочешь предложить что-то улучшить на этой странице? Напиши об этом и получи бонус!
Бонус рассчитывается индивидуально в каждом случае и может быть в виде баллов или бесплатной услуги от студизбы.
Предложить исправление
Добавляйте материалы
и зарабатывайте!
Продажи идут автоматически
5136
Авторов
на СтудИзбе
443
Средний доход
с одного платного файла
Обучение Подробнее