-- This demo renders a scene into a cubemap, then displays the rendered screen reflected on a sphere surface within the screen. -- -- Sample contributed by andi mcc with help from holo -- First a simple scene, a checkerboard floor and some floating cubes -- Want to see how the cubemap is done? Skip this whole section local scene = {} function scene.load() scene.floorSize = 6 scene.cubeCount = 60 scene.boundMin = vector(-10, -1, -10) scene.boundMax = vector(10, 9, 10) scene.speed = 1 scene.rotateSpeed = 1 scene.cubeSize = 0.2 scene.cubes = {} scene.sphereCenter = vector(0, 1.5, -0.5) scene.sphereRad = 0.125 for i = 1, scene.cubeCount do scene.generate(i, true) end end local function randomQuaternion() local u, v, w = math.random(), math.random(), math.random() return quaternion.pack( math.sqrt(1 - u) * math.sin(2 * v * math.pi), math.sqrt(1 - u) * math.cos(2 * v * math.pi), math.sqrt(u) * math.sin(2 * w * math.pi), math.sqrt(u) * math.cos(2 * w * math.pi) ) end function scene.generate(i, randomZ) -- Generate each cube with random position and color and a random rotational velocity local cube = {} local x = scene.boundMin.x + math.random() * (scene.boundMax.x - scene.boundMin.x) local y = scene.boundMin.y + math.random() * (scene.boundMax.y - scene.boundMin.y) local z if randomZ then z = scene.boundMin.z + math.random() * (scene.boundMax.z - scene.boundMin.z) else z = scene.boundMin.z end cube.at = vector(x, y, z) cube.rotateBasis = randomQuaternion() cube.rotateTarget = cube.rotateBasis:conjugate() cube.rotate = cube.rotateBasis cube.color = { math.random() * 0.8, math.random() * 0.8, math.random() * 0.8 } scene.cubes[i] = cube end function scene.update(dt) -- On each frame, move each cube and spin it a little for i, cube in ipairs(scene.cubes) do cube.at = cube.at + vector(0, 0, scene.speed * dt) if cube.at.z > scene.boundMax.z then -- If cube left the scene bounds respawn it scene.generate(i) else local rotateAmount = (cube.at.z - scene.boundMin.z) / (scene.boundMax.z - scene.boundMin.z) cube.rotate = cube.rotateBasis:slerp(cube.rotateTarget, rotateAmount) end end end function scene.draw(pass) -- First, draw a floor local floorRecenter = scene.floorSize / 2 + 0.5 for x = 1, scene.floorSize do for y = 1,scene.floorSize do if (x + y) % 2==0 then pass:setColor(0.25, 0.25, 0.25) else pass:setColor(0.5, 0.5, 0.5) end pass:plane(x - floorRecenter, 0, y - floorRecenter, 1, 1, math.pi / 2, 1, 0, 0) end end -- Draw cubes for _, cube in ipairs(scene.cubes) do pass:setColor(unpack(cube.color)) pass:cube(cube.at, scene.cubeSize, cube.rotate:unpack()) end end -- Now the cubemap stuff local cubemap = {} local unitX = vector(1, 0, 0) local unitY = vector(0, 1, 0) local unitZ = vector(0, 0, 1) function cubemap.load() -- Create cubemap texture cubemap.texture = lovr.graphics.newTexture(256, 256, 6, { type = "cube" }) -- Precalculate cubemap View-Projection matrices local center = scene.sphereCenter cubemap.facePerspective = lovr.math.newMat4():perspective(math.rad(90), 1, .1, 0) cubemap.faces = { lovr.math.newMat4():lookAt(center, center - unitX, vec3(0, 1, 0)), lovr.math.newMat4():lookAt(center, center + unitX, vec3(0, 1, 0)), lovr.math.newMat4():lookAt(center, center + unitY, vec3(0, 0, -1)), lovr.math.newMat4():lookAt(center, center - unitY, vec3(0, 0, 1)), lovr.math.newMat4():lookAt(center, center + unitZ, vec3(0, 1, 0)), lovr.math.newMat4():lookAt(center, center - unitZ, vec3(0, 1, 0)) } -- Create reflection shader cubemap.shader = lovr.graphics.newShader('unlit', [[ uniform textureCube cubemap; vec4 lovrmain() { vec3 V = normalize(CameraPositionWorld - PositionWorld); vec3 N = normalize(Normal); vec3 R = reflect(-V, N); vec4 sphereColor = Color * getPixel(cubemap, R * vec3(-1, 1, 1)); float ndi = dot(N, V) * 0.5 + 0.5; // Darken the sphere a little around the edges to give it apparent depth return vec4(sphereColor.rgb * ndi, 1.); } ]]) -- Set up a render pass that renders to the cubemap cubemap.pass = lovr.graphics.newPass(cubemap.texture) cubemap.pass:setClear(lovr.graphics.getBackgroundColor()) end function cubemap.draw() cubemap.pass:reset() for i = 1, 6 do cubemap.pass:setProjection(i, cubemap.facePerspective) cubemap.pass:setViewPose(i, cubemap.faces[i], true) end scene.draw(cubemap.pass) end -- Handle lovr function lovr.load() lovr.graphics.setBackgroundColor(0.9,0.9,0.9) scene.load() cubemap.load() end function lovr.update(dt) scene.update(dt) end function lovr.draw(pass) cubemap.draw() scene.draw(pass) -- Draw sphere textured with cube map pass:setColor(1, 0.6, 0.6) pass:setShader(cubemap.shader) pass:send('cubemap', cubemap.texture) pass:sphere(scene.sphereCenter, scene.sphereRad) return lovr.graphics.submit(cubemap.pass, pass) end