Хабр Курсы для всех
РЕКЛАМА
Практикум, Хекслет, SkyPro, авторские курсы — собрали всех и попросили скидки. Осталось выбрать!
И вообще там был православный пиксель арт)

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


framebuffer[ i + j * width ] = ( framebuffer[ i + j * width ] * noise + palette_fire( noise ) ) * lightIntensity;

--$Name:Рейтрейсер$
--$Info:порт рейтрейсера с pico-8$
--$Author:Kerbal$
--$Version:0.1$
require "sprite"
require "timer"
require "click"
local sqrt = math.sqrt
local flr = math.floor
local min = math.min
local max = math.max
local rnd = math.random
local abs = math.abs
local cam_shift_x = 0.1
local cam_shift_y = 0.1
local r_g_b = {
[0] = {0,0,0},
{29, 43, 83},
{126, 37, 83},
{0, 135, 81},
{171, 82, 54},
{95, 87, 79},
{194, 195, 199},
{255, 241, 232},
{255, 0, 77},
{255, 163, 0},
{255, 236, 39},
{0, 228, 54},
{41, 173, 255},
{131, 118, 156},
{255, 119, 168},
{255, 204, 170},
{199, 240, 216}, --NOKIA SCREEN
{67, 82, 61}, --NOKIA INK
{0,0,0},
{0,0,0},
{0,0,0},
}
--[[
local colorGradients = {
sky = {16, 16, 16, 16},
green = {17, 16, 16, 17},
red = {17, 16, 17},
}
]]
local colorGradients = {
red = {1, 2, 8, 9, 10, 7},
green = {0, 1, 5, 3, 11},
sky = {15, 12, 12, 1},
nokia1 = {16, 16, 16, 16},
nokia2 = {17, 16, 16, 17},
nokia3 = {17, 16, 17},
}
--math
--local function length(x, y, z) return sqrt(x*x + y*y + z*z) end
local function length(x, y, z)
local y = y or 0
local z = z or 0
return (x*x + y*y + z*z)^0.5
end
local function norm(x, y, z)
local y = y or 0
local z = z or 0
local l = length(x,y,z)
return x/l, y/l, z/l
end
local function dot(xa, ya, za, xb, yb, zb)
local za = za or 0
local zb = zb or 0
return xa*xb + ya*yb + za*zb
end
local function dot2(xa, ya, xb, yb)
return xa*xb + ya*yb
end
--globals
local ex, ey, ez = 0, 1, -1.5 --camera position
local fov = 45 --camera FOV
local tmin, tmax = .1, 200 --minimum and maximum distance from camera
local maxSteps = 100 --maximum number of steps to take
local lx, ly, lz = norm(.2, .5, -.6) --light direction
--distance field functions
----------Shapes
local function sdCircle(x, y, r)
return length(x, y) - r
end
local function sdBox(x, y, w, h)
local n, m = abs(x)-w, abs(y)-h
return length(max(max(n,0),max(m,0))) + min(max(n,m),0.0);
end
----------Shapes
----------Primitives
local function plane(x, y, z) return y end
local function cube(x, y, z, side)
return max(abs(x), abs(y), abs(z)) - side / 2
end
local function box(x, y, z, a, b, c)
local l, n, k = abs(x) - a, abs(y) - b, abs(z) - c
return length(max(l, n, k, 0.0)) + min(max(l,max(n, k)),0.0)
end
--[[
local function rbox(x, y, z, a, b, c, r)
local l, n, k = abs(x) - a - r, abs(y) - b - r, abs(z) - c - r
return length(max(l, n, k, 0.0)) + min(max(l,max(n, k)),0.0)
end
]]
local function sphere(x, y, z, radius)
local manhattanDistance = x + y + z
--this if is to avoid errors due to pico's limited number range
if manhattanDistance > radius * 2 then return manhattanDistance end
return length(x, y, z) - radius
end
local function cone(x, y, z, h, r)
local h, r = norm(h, r)
local q = length(x, z)
return dot2(h, r, q, y)
end
local function torus(x, y, z, rring, r)
return (((x^2 + y^2)^0.5 - rring)^2 + z^2)^0.5 - r
end
local function torus2(x, y, z, rring, r)
return (((y^2 + z^2)^0.5 - rring)^2 + x^2)^0.5 - r
end
local function capped_cylinder(x,y,z, h, r)
local a, b = abs(length(y,z)) - h, abs(x) - r
return min(max(a, b), 0.0) + length(max(a, b, 0.0))
end
local function sdTriPrism(x, y, z, w, h)
local a,b,c = abs(x), abs(y), abs(z)
return max(c - h, max(a * 0.866025 + y * 0.5, -y) - w * 0.5)
end
----------Union, Subtraction, Intersection
local function op_union (a, b)
return min(a, b)
end
local function op_substraction (a, b)
return max(-a, b)
end
local function op_intersection (a, b)
return max(a, b)
end
local function cutbox(x, y, z, h)
return max(-sphere(x, y, z, h*.7), box(x, y, z, h))
end
local function cutsphere(x, y, z, h)
return max(sphere(x, y, z, h*.75), box(x, y, z, h))
end
--union combines two distance functions
local function union(a, am, b, bm) if a < b then return a, am else return b, bm end end
--scene defines the total distance function
local function scene(x, y, z)
local d, m = tmax, 0 --max distance is skyk
d, m = union(d, m, plane(x, y, z), "green")
-- d, m = union(d, m, cube(x-2, y-2, z-1, 1), "green")
d, m = union(d, m, box(x-2, y-2, z-1, .1, .5, .5), "green")
d, m = union(d, m, sdBox(x, z - 3, .2, .2), "green")
d, m = union(d, m, sdBox(x, z, .2, .2), "green")
d, m = union(d, m, sdBox(x - 3, z - 3, .2, .2), "green")
d, m = union(d, m, sdBox(x - 3, z, .2, .2), "green")
d, m = union(d, m, sdCircle(x - 3, y, .1), "red")
d, m = union(d, m, sdCircle(y, z, .1), "red")
-- d, m = union(d, m, cone(x-4, y-2, z, 0.01, 0.01), "green")
d, m = union(d, m, torus( z-1, x-2, y-1, 1.5, .1), "green")
-- d, m = union(d, m, torus2(x-3, y-1, z-2, 2, .1), "green")
d, m = union(d, m, sphere(x, y-1, z, .5), "red")
d, m = union(d, m, sdTriPrism(y-2, x-2, z, .3, .2), "red")
d, m = union(d, m, sphere(x, y-.5, z, .3), "red")
d, m = union(d, m, capped_cylinder(x-2, y-1, z, .2, .2), "red" )
-- d, m = union(d, m, cutbox(x-2, y-1, z-1, 1), "red" )
d, m = union(d, m, op_substraction(sphere(x-2, y-1, z-1, 0.7), box(x-2, y-1, z-1, .5, .5, .5)), "red" )
-- d, m = union(d, m, cutsphere(x-2, y-1, z-.75, 1), "cnob" )
d, m = union(d, m, sphere(x, y-1.5, z, .3), "red")
d, m = union(d, m, sphere(x-1, y-1, z-1, .3), "green")
return d,m
end
--calculates the normal at xyz
local function sceneNormal(x, y, z)
local eps = 0.1
local xa, xb = scene(x+eps, y, z), scene(x-eps ,y ,z)
local ya, yb = scene(x, y+eps, z), scene(x, y-eps ,z)
local za ,zb = scene(x, y, z+eps), scene(x, y, z-eps)
return norm(xa-xb, ya-yb, za-zb)
end
--rendering
--returns smooth color based on position and gradient
local function dither(x, y, gradient, value)
local whole = flr(value * #gradient)
local fraction = value * #gradient - whole
local low = gradient[min(whole + 1, #gradient)]
local high = gradient[min(min(whole + 1, #gradient) + 1, #gradient)]
if fraction < 1/7 then return low end
if fraction < 2/7 then if (x+1)%3==0 and y%3==0 then return high else return low end end
if fraction < 3/7 then if x%2==0 and y%2==0 then return high else return low end end
if fraction < 4/7 then if (x%2==0 and y%2==0) or (x%2~=0 and y%2~=0) then return high else return low end end
if fraction < 5/7 then if x%2==0 and (y+1)%2==0 then return low else return high end end
if fraction < 6/7 then if (x+1)%3==0 and y%3==0 then return low else return high end end
return high
end
local function getShadowPoint(t, x, y, z) return x + lx * t, y + ly * t, z + lz * t end
--computes the shoft shadow value
local function shadow(x,y,z)
--t starts at 0.2 so the shadow ray doesn't intersect
--the object it's trying to shadow
local res, t, distance, sx, sy, sz = 1, 0.2, 0, 0, 0, 0
for i = 1, 6 do
sx, sy, sz = getShadowPoint(t, x, y, z)
distance, _ = scene(sx, sy, sz) --we don't care about the color
res = min(res, 2 * distance / t) --increase 2 to get sharper shadows
t = t + min(max(distance, .02), .2)
if distance < .05 or t > 10.0 then break end
end
return min(max(res, 0), 1)
end
--calculates the final lighting and color
local function render(x, y, t, tx, ty, tz, rx, ry, rz, color)
local nx, ny, nz = sceneNormal(tx, ty, tz)
local light = 0
light = light + min(max(dot(nx, ny, nz, lx, ly, lz), 0), 1) --sun light
light = light * shadow(tx,ty,tz) --shadow color
light = min(max(light, 0), 1) --clamp final light value
return dither(x, y, colorGradients[color], light)
end
--calculates the sky color
local function sky(x, y, rx, ry, rz)
local altitude = (min(max(ry, 0), 1) ^ 1.5)
return dither(x, y, colorGradients.sky, altitude)
end
local function nokia1(x, y, rx, ry, rz)
local altitude = (min(max(ry, 0), 1) ^ 1.5)
return dither(x, y, colorGradients.nokia1, altitude)
end
--tracing
--this is the heart of a ray tracer
--the for loop pushes the test point forward until
--it finds a surface that is close enough to render
--the forward direction is based off the xy of the screen and fov
local function getRayDirection(x, y) return norm(x / 64 - 1, (128 - y) / 64 - 1, 90 / fov) end
local function getTestPoint(t, rx, ry, rz) return ex + rx * t, ey + ry * t, ez + rz * t end
function trace(x,y)
local rx, ry, rz = getRayDirection(x, y)
local tx, ty, tz = 0, 0, 0
local t, distance, color = 0, 0, 0
for i = 1, maxSteps do
tx, ty, tz = getTestPoint(t, rx, ry, rz)
distance, color = scene(tx, ty, tz)
--the test point is close enough, render
if distance < .05 then return render(x, y, t, tx, ty, tz, rx, ry, rz, color) end
--the test point is too far, give up, draw the sky
if distance >= tmax then break end
--move forward by some fraction
t = t + distance * .7
end
return sky(x, y, rx, ry, rz)
-- return nokia1(x, y, rx, ry, rz)
end
--just here to get pico to work the way it should be defaut tbh
--function _init() cls() end
--function _update() end
--pick random points to trace, but only if they
--havent been traced before
--cache the expensive trace function, and set pixel
--local traced = {}
function _draw()
for y = 1, 512 do
for x = 1, 512 do
-- local x, y = flr(rnd(128)), flr(rnd(128))
local i = x + y * 255
-- if not traced[i] then
-- pxl:val(x, y, i, j, t)
-- print ("x ".."y "..trace(x,y))
local k = trace(x,y)
--[[ print (r_g_b[k])
print (trace(x,y))
print (r_g_b[k][2])
print (r_g_b[k][3] or "nope")
]]
pxl:val(x,y, r_g_b[k][1], r_g_b[k][2], r_g_b[k][3])
-- pset(x, y, trace(x,y))
--traced[i] = true
-- end
end
end
end
sprite.direct(true)
declare 'pxl' (false)
function game:timer()
--[[
local x, y, i, j
t = t + f
v = v + r
if t < 1 then f = 1 end
if t > 254 then f = -1 end
if v < 1 then r = 1 end
if v > 254 then r = -1 end
-- i = (x*y^0.5+t)%512
for x = 0, 255 do
for y = 0, 255 do
i = (x^2-y^2+t^1.3)%255
j = (x^2-y^2+v^1.3)%255
-- pxl:val(x, y, i, j, t)
pxl:val(x, y, j, t, v)
end
end
]]
-- print (colorGradients["red"][3])
-- table.insert(colorGradients["red"],colorGradients["red"][1])
-- table.remove(colorGradients["red"],1)
_draw()
pxl:draw_spr(sprite.scr())
if ex > 1 or ex < -1 then cam_shift_x = cam_shift_x * -1 end
if ey > 2 or ey < 0.3 then cam_shift_y = cam_shift_y * -1 end
ex = ex + cam_shift_x
ey = ey + cam_shift_y
-- fps = (1000/(instead.ticks() - old_ticks))
-- print (fps)
-- old_ticks = instead.ticks()
end
function start(load)
pxl = pixels.new(512, 512)
timer:set(10)
end
Рисуем мультяшный взрыв за 180 строчек голого C++