Render Script для начинающих. 2. Буферы. Цветовой буфер (color buffer). [Defold без боли]

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

В прошлом посте мы остановились на теме цветового буфера в компьютерной графике, поэтому сегодня мы затронем эту тему и рассмотрим простые примеры с цветовым буфером в Defold!


Буферы — это как прозрачные стёкла на столе художника, которые лежат друг на друге. И каждое стекло служит для определенной цели.

  1. Цветовой буфер (Color Buffer) — первое стекло, где художник рисует краской.
  • На нём хранится цвет каждого кусочка рисунка.
  • Красные, зелёные, синие точки, как маленькие пиксели.
  • Это то, что мы в итоге видим на экране!
  1. Буфер глубины (Depth Buffer) — второе стекло с линейкой.
  • На нём написано “насколько далеко” находится каждая точка.
  • Если мы рисуем несколько предметов, этот буфер помнит: что ближе, что дальше?
  • Например: машина ближе чем гора, поэтому машина перекрывает гору.
  1. Буфер трафарета (Stencil Buffer) — третье стекло с маской.
  • Это как трафарет — через него рисуют только в определённых местах.
  • Например: “рисуем только в круглых отверстиях трафарета”.
  • Для спецэффектов (тени, зеркала, взрывы).

Буферы в Defold


Цветовые буферы (Color Buffers)

graphics.BUFFER_TYPE_COLOR0_BIT

Основной цветовой буфер. Хранит итоговый цвет каждого пикселя. RGBA значения (красный, зелёный, синий, альфа-прозрачность)

graphics.BUFFER_TYPE_COLOR1_BIT  -- Может быть nil
graphics.BUFFER_TYPE_COLOR2_BIT  -- Может быть nil
graphics.BUFFER_TYPE_COLOR3_BIT  -- Может быть nil

Есть ещё и буферы для продвинутых техник (Multiple Render Targets — MRT), когда нужно рисовать одновременно в несколько цветовых целей. Но пока что нам это не нужно.

Буфер глубины (Depth Buffer)

graphics.BUFFER_TYPE_DEPTH_BIT

Определяет, какой объект ближе к камере. Значения Z (расстояния от точки наблюдения) в диапазоне. Иными словами: определяет, что ближе, что дальше. Обязательно для 3D-графики и правильной отрисовки перекрывающихся объектов.

Буфер трафарета (Stencil Buffer)

Определяет, где рисовать, а где нет.

graphics.BUFFER_TYPE_STENCIL_BIT

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

Используется для спецэффектов:

  • Зеркала
  • Тени
  • Порталы
  • Маски
render.clear({
    [graphics.BUFFER_TYPE_COLOR0_BIT] = vmath.vector4(0, 0, 0, 1),
    [graphics.BUFFER_TYPE_DEPTH_BIT] = 1,
    [graphics.BUFFER_TYPE_STENCIL_BIT] = 0  -- Обнулить маску
})

В предыдущем уроке мы создали свой рендер и рендер скрипт. Проделайте тоже самое, создайте рендер скрипт и рендер, прежде чем выполнить примеры ниже:

Примечание:

Текущая привязка ввода к ЛКМ:

Пример 1. Изменение цветового буфера по клику:

Затем добавьте в main.collection игровой объект и компонент скрипт этому объекту:

В controller.script вставьте:

function init(self)
	msg.post(".", "acquire_input_focus")
end

function on_input(self, action_id, action)
	if action_id == hash("touch") and action.pressed then
		local new_color = vmath.vector4(1, 1, 1, 1)  --  Белый цвет
		msg.post("@render:", "change_color", { new_color = new_color })
		print("Отправлено сообщение для смены цвета!")
	end
end

В ваш новый render.script добавьте такой код:

function init(self)
    self.color = vmath.vector4(0, 0, 0, 1)  -- Начальный чёрный цвет
end

function update(self, dt)
    -- Обновляем цветовой буфер
    render.clear({
        [graphics.BUFFER_TYPE_COLOR0_BIT] = self.color
    })
end

function on_message(self, message_id, message)
    if message_id == hash("change_color") then
        self.color = message.new_color
        print("Цвет изменён на: " .. tostring(self.color))
    end
end

До клика:

После клика по экрану:

Пример 2. Синусоидальная радуга:

Вставьте такой код в ваш рендер скрипт:

function init(self)
    self.time = 0  -- Счётчик времени
end

function update(self, dt)
    self.time = self.time + dt  -- Время тикает каждый кадр

    -- Меняем цвет по синусу:
    -- Красный, зелёный, синий — волнами
    local r = (math.sin(self.time * 1.0) + 1) / 2  -- 0..1
    local g = (math.sin(self.time * 1.5) + 1) / 2
    local b = (math.sin(self.time * 2.0) + 1) / 2

    local clear_color = vmath.vector4(r, g, b, 1.0)  -- RGBA (1=непрозрачный)

    -- Очищаем экран этим цветом (только цветовой буфер!)
    render.clear({
        [graphics.BUFFER_TYPE_COLOR0_BIT] = clear_color
    })

end

Как итог:
sin_color

Пример 3. Случайный цвет по клику:

В controller.script добавь:

function init(self)
    msg.post(".", "acquire_input_focus")
end

function on_input(self, action_id, action)
    if action_id == hash("touch") and action.pressed then
        msg.post("@render:", "random_color")  -- Просто отправляем сообщение без параметра
        print("Отправлено сообщение для случайного цвета!")
    end
end

В render.script:

function init(self)
    self.color = vmath.vector4(0, 0, 0, 1)  -- Начальный чёрный цвет
    math.randomseed(os.time())  -- Инициализируем рандом для воспроизводимости
end

function update(self, dt)
    render.clear({
        [graphics.BUFFER_TYPE_COLOR0_BIT] = self.color
    })
end

function on_message(self, message_id, message)
    if message_id == hash("random_color") then
        local r = math.random()  -- 0..1
        local g = math.random()
        local b = math.random()
        self.color = vmath.vector4(r, g, b, 1)
        print("Цвет изменён на случайный: " .. tostring(self.color))
    end
end

random_color

Пример 4. Переход между двумя цветами:
function init(self)
    self.start_color = vmath.vector4(0, 0, 0, 1)  -- Начальный: чёрный
    self.end_color = vmath.vector4(1, 0.5, 0, 1)  -- Целевой: оранжевый
    self.duration = 2.0  -- Длительность перехода в секундах
    self.time = 0  -- Счётчик времени
end

function update(self, dt)
    self.time = self.time + dt
    local t = math.min(self.time / self.duration, 1.0)  -- Нормализованное время 0..1
    local current_color = vmath.lerp(t, self.start_color, self.end_color)  -- Линейная интерполяция

    render.clear({
        [graphics.BUFFER_TYPE_COLOR0_BIT] = current_color
    })

    -- Зацикливаем для повторения (опционально)
    if self.time >= self.duration then
        self.time = 0
    end
end

color_blink

Ещё раз, но другими словами

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

Фреймбуфер состоит из трёх основных типов буферов:

Тип буфера Назначение Содержимое
Цветовой буфер (Color Buffer) Хранит итоговый цвет каждого пикселя RGBA значения (красный, зелёный, синий, альфа-прозрачность)
Буфер глубины (Depth Buffer) Определяет, какой объект ближе к камере Значения Z (расстояния от точки наблюдения) в диапазоне [0..1]​
Буфер трафарета (Stencil Buffer) Маскирует области для специальных эффектов Целочисленные значения для условной отрисовки

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


Задания на дом:


  1. Почитать о линейной интерполяции. Где она применяется.
  2. Почитать о строении человеческого глаза. Как мы видим мир таким, какой он есть.

Всем спасибо за внимание!