-- red puffs are billboards oriented in GLSL -- blue puffs are billboards oriented in Lua function lovr.load() -- puff texture puffImageSize = 128 -- try 16 puffImage = lovr.data.newImage(puffImageSize, puffImageSize) for x = 0, puffImageSize - 1 do for y = 0, puffImageSize - 1 do local radius = puffImageSize / 2 - 1 local distanceFromCenter = vec2(radius, radius):distance(vec2(x, y)) local depth = math.sqrt(radius^2 - distanceFromCenter^2) puffImage:setPixel(x, y, 1, 1, 1, depth / radius) lovr.math.drain() end end puffTexture = lovr.graphics.newTexture(puffImage) puffMaterial = lovr.graphics.newMaterial({ texture = puffTexture }) -- puff positions redPuffs = {} for i = 1, 100 do table.insert(redPuffs, { position = lovr.math.newVec3( lovr.math.random() - .5, lovr.math.random() - .5 + 1.7, lovr.math.random() - .5 ) }) end bluePuffs = {} for i = 1, 100 do table.insert(bluePuffs, { position = lovr.math.newVec3( lovr.math.random() - .5, lovr.math.random() - .5 + 1.7, lovr.math.random() - .5 ) }) end -- red puff billboard shader redPuffVertex = [[ Constants { vec3 headPosition; }; mat4 target(vec3 from, vec3 to, vec3 up) { mat4 m; vec3 z = normalize(from - to); vec3 x = normalize(cross(up, z)); vec3 y = cross(z, x); m[0] = vec4(x, 0); m[1] = vec4(y, 0); m[2] = vec4(z, 0); m[3] = vec4(from, 1); return m; } vec4 lovrmain() { vec3 puffPosition = Transform[3].xyz; vec3 puffScale = vec3(length(Transform[0]), length(Transform[1]), length(Transform[2])); mat4 billboardModel = target(puffPosition, headPosition, vec3(0, 1, 0)); billboardModel *= mat4( puffScale.x, 0, 0, 0, 0, puffScale.y, 0, 0, 0, 0, puffScale.z, 0, 0, 0, 0, 1 ); return Projection * View * billboardModel * VertexPosition; } ]] defaultFragment = [[ vec4 lovrmain() { return DefaultColor; } ]] redPuffShader = lovr.graphics.newShader(redPuffVertex, defaultFragment) lovr.graphics.setBackgroundColor(0, .5, 1) end function lovr.draw(pass) pass:setSampler('nearest') -- try uncommenting -- red puffs pass:setColor(1, .9, .9) pass:setMaterial(puffMaterial) pass:setDepthWrite(false) pass:setShader(redPuffShader) pass:send('headPosition', { lovr.headset.getPosition('head') }) for _, puff in pairs(redPuffs) do pass:plane(puff.position, .1) end pass:setShader() -- blue puffs pass:setColor(.9, 1, 1) for _, puff in pairs(bluePuffs) do puff.orientation = quat(mat4():target(puff.position, vec3(lovr.headset.getPosition('head')))) pass:plane(puff.position, .1, .1, puff.orientation) end end