Рисую треугольник, квадрат, первый шейдер и пару примеров из первой главы Nature of code

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

Интересные моменты:

  • точки в координатах должны идти по часовой стрелке A → B → C. Зачем нужно? Для того чтоб GPU понимал какая сторона ФРОНТ/БЕК и не рисовать те стороны, которые не видно, culling, не рендерит невидимые части.

треугольник

треугольник

Рисуем треугольник Mesh | Defold

# ASSETS
- создаем fragment program с именем mesh
- создаем vertex program с именем mesh
- создаем buffer с именем triangle.buffer
- создаем mesh.material
	- в meterial выставляем в vertex program наш mesh.vp
	- и в fragment program mesh.fp
	- tag добавляем model (ля рендер скрипта какой тип)
	- vertex constants: создаем две переменных нажимая на плюс
		- имя: mtx_proj type: Projection
		- имя: mtx_worldview type: Wordview
	- vertex space: Local

# OUTLINE
- создаем го с именем triangle
	- в этом го именяем scale x,y до 100,100 и позицию на x,y на 300,300
	- созаем меш, в свойствах меша:
		- в meterial ставим созданный mesh.material
		- vertices: ставим созданный triangle.buffer
		- position stream: position
		- normal stream: position 
// mesh.fp
varying mediump vec4 var_color;

void main()
{
	gl_FragColor = var_color;
}
// mesh.vp
uniform mediump mat4 mtx_worldview;
uniform mediump mat4 mtx_proj;

attribute mediump vec4 position;
attribute mediump vec4 color0;

varying mediump vec4 var_color;

void main()
{
	gl_Position = mtx_proj * mtx_worldview * vec4(position.xyz, 1.0);
	var_color = color0;
}
[
    {
	    -- name это имена attribute, точки координат(position) и цвета(color0)
		-- увидим в вертексном шейдере
        "name": "position",
        -- тип чисел, обычно float32
        "type": "float32",
        -- Сколько чисел описывают ОДНУ вершину.
        "count": 3,
        -- координаты
        -- можно делать простые фигуры через 0,1 и потом масштабировать через go scale или же сразу задавать размеры здесь, например, вместо 1 писать сразу 50
        "data": [
	        -- точки в координатах должны идти по часовой стрелке A → B → C. 
            -1, -1, 0,
            1, -1, 0,
            0,  1, 0
        ]
    },
    {
        "name": "color0",
        "type": "float32",
        "count": 4,
        -- r = красный  
		-- g = зелёный  
		-- b = синий
		-- a = альфа (прозрачность)
        "data": [
            0, 1, 0, 1,
            1, 0, 0, 1,
            0, 0, 1, 1
        ]
    }
]

-1,-1,0 это коориднаты x,y,z

квадрат

Меняем наш triangle.buffer, добавляем два треугольника.

[
    {
        "name": "position",
        "type": "float32",
        "count": 3,
        "data": [
	        -- один треугольник
            -1, -1, 0,
            1, -1, 0,
            1,  1, 0,

			-- второй
            -1,-1,0,
            1,1,0,
            -1,1,0
        ]
    },
    {
        "name": "color0",
        "type": "float32",
        "count": 4,
        "data": [
	        -- цвета для rgba для двух треугольников
            0, 1, 0, 1,
            1, 0, 0, 1,
            0, 0, 1, 1,

            0, 1, 0, 1,   
            0, 0, 1, 1,  
            1, 1, 0, 1    
        ]
    }
]

случайное блуждание

Размер экрана в game.project должен быть 960 на 640

# ASSETS
- добавляем картинку 1 на 1 пиксель с любым цветом
- создаем атлас, добалвяем картинку
- создаем game object
  - в него добавляем спрайт
	- в спрайте выбираем атлас, наше изображение
- создаем nature.script
  
# OUTLINE
- создаем го
	- добавляем nature.script к скрипту
	- создаем фабрику 
		- в фабрике выбираем Prototype наш созданный game object

в game.project увеличиваем лимиты для обьектов в коллекций COLLECTION: 65534
и у SPRITE: 65534
65534 - лимит для одной коллекции, для превышения новой создаем новую коллекцию

NATURE.SCRIPT

-- случайное число от a до b
local function rand(a,b)
	return math.random(a,b)
end

-- первый вариант 1,2,3,4 ход
local function step1(self)
	-- выбираем направление
	local choice = rand(1,4)
	if choice == 1 then
		-- ход +1 self.width это размер нашего спрайта в 1 пиксель
		self.startX = self.startX + self.width
	elseif choice == 2 then
		self.startX = self.startX - self.width
	elseif choice == 3 then
		self.startY = self.startY + self.width
	elseif choice == 4 then
		self.startY = self.startY - self.width
	end
end

-- улучшенный вариант хода
local function step(self)
	// по x 0,1,-1
	local x = rand(-1,1)
	// по y 0,1,-1
	local y = rand(-1,1)
	self.startX = self.startX + x
	self.startY = self.startY + y
end
local function show(self)
	local pos = vmath.vector3(self.startX,self.startY,1)
	factory.create("#factory", pos)
end

function init(self)
	-- для рандома + сброс первых значений
	math.randomseed(math.floor(socket.gettime()*1e6))
	math.random(); math.random(); math.random();

	-- размеры в game project игры
	local width = sys.get_config_int("display.width")
	local height = sys.get_config_int("display.height")

	-- для центра
	self.startX = width/2
	self.startY = height/2
	self.width = 1

	self.accum = 0 -- аккумулятор времени
	self.value = 0.0001 -- время в секундах для рисовки
end

function update(self, dt)
	self.accum = self.accum + dt

	while self.accum >= self.value do
		-- ходим
		step(self)
		-- рисуем
		show(self)
		self.accum = self.accum - self.value
	end
end

Стоблики

Размер экрана в game.project должен быть 960 на 640

# ASSETS
- добавляем 64 на 10
- создаем атлас, добалвяем картинку
- создаем game object
  - в него добавляем спрайт
	- в спрайте выбираем атлас, наше изображение
	- в спрайте включаем slice 9 (Size mode: Manual) и Slice 9 ставим 3,3,3,3
- создаем nature.script
  
# OUTLINE
- создаем го
	- добавляем nature.script к скрипту
	- создаем фабрику 
		- в фабрике выбираем Prototype наш созданный game object
-- случайное число от a до b
local function rand(a,b)
	return math.random(a,b)
end

local function step(item) 
	local id = item.id
	-- высота текущая
	local height = item.height
	-- число на которое прибавляем
	local value = 1

	-- прибавляем новую высоту
	local sprite_url = msg.url(nil, id, "sprite") 
	go.set(sprite_url, "size", vmath.vector3(64, height+value, 0))

	-- выравниваем относительно новой высоты pivot
	-- иначе уйдет под экран
	local p = go.get_position(id)
	local pos = vmath.vector3(p.x, height/2, p.z)

	item.height = item.height + value
end

function init(self)
	-- для рандома + сброс первых значений
	math.randomseed(math.floor(socket.gettime()*1e6))
	math.random(); math.random(); math.random();

	local table = {}

	-- стартовый стоблик
	local start = vmath.vector3(64/2, 1, 0)
	
	local dx = 64
	self.table = {}
	for i = 1, 15 do
		-- позиция для нового стоблика
		local pos = start + vmath.vector3((i-1) * dx, 0, 0)
		local id = factory.create("#factory", pos)
		-- обьект с id и текущей высотой
		local obj = {
			id = id, 
			height = 10
		}
		self.table[i] = obj
	end

	self.accum = 0 -- аккумулятор времени
	self.value = 0.001 -- в секундах ход
end

function update(self, dt)
	self.accum = self.accum + dt
	while self.accum >= self.value do
		-- выбираем случайный столб от 1 до 15
		local r = rand(1,15)
		-- прибавляем к нему +1 высоту
		step(self.table[r])
		self.accum = self.accum - self.value
	end
end

Шейдер, создаем блик на 0.1 милисекунд как урон для спрайта

Вот туториал на форуме, здесь исправленный вариант для версии 1.11
Shaders for beginners - The Defoldmine - Defold game engine forum
vertex program - определяет положение вершин
fragment program - красит каждый пиксель
материал - во что они объединяются подробнее - Материалы | Defold

# ASSETS
- берем любой png картинку и закидываем в проект
- создаем main.atlas, кидаем картинку в него
- идем в папку builltins/materials и ищем sprite.vp, sprite.fp и sprite.material и копируем их в папку main с нашей главной коллекцией
- созадем скрипт shader.script

# OUTLINE
- создаем го с именем shader
	- добавляем shader.script в него
	- добавляем спрайт, выбираем Image: атлас и defold animation наш спрайт
		- Material: вставляем наш sprite.material из /main

В sprite.material vertex и fragment program нужно выбрать из нашей папки main
в Fragment Constants добавить переменную blink_effect_trigger и выставить значения 1,1,1,1

Идем в sprite.fp

#version 140

in mediump vec2 var_texcoord0;

out vec4 out_fragColor;

uniform mediump sampler2D texture_sampler;
uniform fs_uniforms
{
    mediump vec4 tint;
    // отличие от статьи
    // теперь переменные fragment constant нужно определять в fs_uniforms
    uniform lowp vec4 blink_effect_trigger;
};

void main()
{
    // Pre-multiply alpha since all runtime textures already are
    mediump vec4 tint_pm = vec4(tint.xyz * tint.w, tint.w);
    // обьявляем переменную mediump vec4 color_of_pixel
    mediump vec4 color_of_pixel = texture(texture_sampler, var_texcoord0.xy) * tint;

	// blink_effect_trigger.r проверяем, если 1 = показывать, 0 - нет
	// color_of_pixel.a != 0.0 иначе вместо спрайта будет красить белым весь квадрат
    if((blink_effect_trigger.r == 1.0) && (color_of_pixel.a != 0.0)) {
	    // монжо поменять цвет на любой rgba формат
        color_of_pixel = vec4(1.,1.,1.,1.);
    }

	// отдаем для каждого пикселя
    out_fragColor = color_of_pixel;
}

ШЕЙДЕР СКРИПТ

function init(self)
	timer.delay(1, true, function() -- каждую секунду

		go.set("#sprite", "blink_effect_trigger", vmath.vector4(1)) -- красим в белый

		timer.delay(0.1, false, function()  -- и после 0.1 секунды
			go.set("#sprite", "blink_effect_trigger", vmath.vector4(0))  -- выключаем
		end)

	end)
end

Готово, спрайт мигает каждую секунду 0.1 секунду в белый цвет

1 лайк