Алгоритм, который рассмотрим сегодня, не имеет нормального названия. Иногда его называют "Shade Bobs", а вообще это один из многочисленных алгоритмов генерации "плазмы". Когда что-то на экране видоизменяется и переливается.

Из множества алгоритмов "плазм", представленный экземпляр самый элементарный.

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

Обычный график функции sin()
Обычный график функции sin()

Из графика функции видно, что ее значения постепенно возрастают и уменьшаются с одинаковым периодом. Теперь представьте, что мы к этому изменению значения функции привяжем какой-нибудь цвет:

Генерируем палитру цвета на основе функции sin()
Генерируем палитру цвета на основе функции sin()

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

Покажу еще раз отдельной картинкой полученную палитру:

Что мы можем с помощью такой палитры нарисовать?

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

  • Прямоугольник имеет координаты левого верхнего угла, из которых он отрисовывается, переменные pX, pY.

  • Скорость движения по осям хранится в переменных sX, sY.

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

  • Прямоугольник не может вылететь за границы экрана.

  • Сам прямоугольник не отрисовывается, но там где он пролетает мы делаем цвет точек экрана на 1 единицу больше, передвигаясь по палитре цветов. Т.е. был цвет точек 0 станет 1, был 2 станет 3 и так далее. Но если мы выходим за пределы нашей палитры, то сбрасываем цвет на 0, т.е. на начало нашей палитры.

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

Анимация плазмы с палитрой на основе sin()
Анимация плазмы с палитрой на основе sin()
Состояние экрана после множества итераций алгоритма
Состояние экрана после множества итераций алгоритма

Картинка на экране формируется медленно и скорее подойдет для хранителя экрана.

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

Палитра с переходом по множеству цветов
Палитра с переходом по множеству цветов
Состояние экрана после какого-то времени работы
Состояние экрана после какого-то времени работы

И собственно сам код:

import pygame
import random
import math
import copy

MX = MY = 512           # Размер массива с плазмой

SizePX = 70             # Размеры размывающего прямоугольника.
SizePY = 60

scale = 1               # Масштаб точек для вывода на экран

SX = MX * scale         # Размер экрана исходя из размера плазмы и ее масштаба
SY = MX * scale

scr = []                # Промежуточный список для хранения экрана

line = [0] * MX         # Создаем список из нулей длиной MX

scr = []                # Создаем список списков из нулей длиной MY, в итоге получится квадратная таблица из нулей.
for y in range(0, MY):
    scr.append(copy.deepcopy(line))

pygame.init()
screen = pygame.display.set_mode((SX, SY))
running = True

pal = []                # Палитра для графического эффекта

                        # Задаем плавный переход от черного к красному, а затем от красного к черному.
# for i in range(0, 255):
#     pal.append([abs(round(math.sin(i * math.pi / 64.0)*200.0)), 0, 0])

for i in range(0, 255, 4):          # Задаем переходы цветов.
    pal.append([0, 0, i])
for i in range(0, 255, 4):
    pal.append([i, 0, (255-i)])
for i in range(0, 255, 4):
    pal.append([255, i, 0])
for i in range(0, 255, 4):
    pal.append([255, 255, i])
for i in range(255, 0, -4):
    pal.append([255, 255, i])
for i in range(255, 0, -4):
    pal.append([255, i, 0])
for i in range(255, 0, -4):
    pal.append([i, 0, (255-i)])
for i in range(255, 0, -4):          
    pal.append([0, 0, i])


pX = pY = 100           # Начальное положение прямоугольника на экране
sX = sY = 3             # Скорость его движения по осям координат.

# -------------------------------------------------------------------------------------------------------
#  Отрисовка закрашенного квадрата в нужных координатах, определенного размера.
# -------------------------------------------------------------------------------------------------------
def drawBox(x, y, size, color):
    pygame.draw.rect(screen, pal[color], (x, y, size, size))

# -------------------------------------------------------------------------------------------------------
while running:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False

    for y in range(pY, pY + SizePY):                        # Проходим по точкам принадлежищим прямоугольнику
        for x in range(pX, pX + SizePX):
            if (x in range(0, MX)) and (y in range(0, MY)): # Если мы не вышли за пределы экрана, то выполняем действия
                scr[y][x] += 1                              # Увеличиваем цвет точки на 1
                if scr[y][x] >= len(pal):                   # Если она стала цветом вне палитры, сбрасываем цвет на 0.
                    scr[y][x] = 0
                drawBox(x * scale, y * scale, scale, scr[y][x])     # Рисуем точку на экране.


    # Если наш прямоугольник выходит за границы нашего экрана,
    # то меняем скорость движения по оси (на которой он вышел за границу)
    # на противоположную.
    # Также направление меняется, если выпало случайное число, при котором будем менять направление.
    # Это позволяет хаотически двигаться прямоугольнику.
    if random.randint(0,10) == 4 or x not in range(0, MX):
        sX *= -1
    if random.randint(0,10) == 7 or y not in range(0, MY):
        sY *= -1

    # Изменяем экранные координаты прямоугольника. Просто прибавляем к ним скорость движения по осям.
    pX += sX
    pY += sY

    pygame.display.flip()

pygame.quit()

Ссылка на предыдущую статью: Разбираем алгоритмы компьютерной графики. Часть 4 – Анимация «Салют»

Комментарии (4)


  1. NekrodNIK
    27.03.2022 05:13
    +1

    ????


  1. mkvmaks
    27.03.2022 15:53

    А дальше будут разборы других алгоритмов???


    1. arwa Автор
      27.03.2022 16:09
      +2

      Будут, просто пока тема до конца не выбрана и копия экрана не готова


      1. aarmaageedoon
        27.03.2022 18:33

        Ждем еще! Ваша серия просто 10/10.