main.lua 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167
  1. -- This demo renders a scene into a cubemap, then displays the rendered screen reflected on a sphere surface within the screen.
  2. --
  3. -- Sample contributed by andi mcc with help from holo
  4. -- First a simple scene, a checkerboard floor and some floating cubes
  5. -- Want to see how the cubemap is done? Skip this whole section
  6. local scene = {}
  7. function scene.load()
  8. scene.floorSize = 6
  9. scene.cubeCount = 60
  10. scene.boundMin = vector(-10, -1, -10)
  11. scene.boundMax = vector(10, 9, 10)
  12. scene.speed = 1
  13. scene.rotateSpeed = 1
  14. scene.cubeSize = 0.2
  15. scene.cubes = {}
  16. scene.sphereCenter = vector(0, 1.5, -0.5)
  17. scene.sphereRad = 0.125
  18. for i = 1, scene.cubeCount do
  19. scene.generate(i, true)
  20. end
  21. end
  22. local function randomQuaternion()
  23. local u, v, w = math.random(), math.random(), math.random()
  24. return quaternion.pack(
  25. math.sqrt(1 - u) * math.sin(2 * v * math.pi),
  26. math.sqrt(1 - u) * math.cos(2 * v * math.pi),
  27. math.sqrt(u) * math.sin(2 * w * math.pi),
  28. math.sqrt(u) * math.cos(2 * w * math.pi)
  29. )
  30. end
  31. function scene.generate(i, randomZ) -- Generate each cube with random position and color and a random rotational velocity
  32. local cube = {}
  33. local x = scene.boundMin.x + math.random() * (scene.boundMax.x - scene.boundMin.x)
  34. local y = scene.boundMin.y + math.random() * (scene.boundMax.y - scene.boundMin.y)
  35. local z
  36. if randomZ then
  37. z = scene.boundMin.z + math.random() * (scene.boundMax.z - scene.boundMin.z)
  38. else
  39. z = scene.boundMin.z
  40. end
  41. cube.at = vector(x, y, z)
  42. cube.rotateBasis = randomQuaternion()
  43. cube.rotateTarget = cube.rotateBasis:conjugate()
  44. cube.rotate = cube.rotateBasis
  45. cube.color = { math.random() * 0.8, math.random() * 0.8, math.random() * 0.8 }
  46. scene.cubes[i] = cube
  47. end
  48. function scene.update(dt) -- On each frame, move each cube and spin it a little
  49. for i, cube in ipairs(scene.cubes) do
  50. cube.at = cube.at + vector(0, 0, scene.speed * dt)
  51. if cube.at.z > scene.boundMax.z then -- If cube left the scene bounds respawn it
  52. scene.generate(i)
  53. else
  54. local rotateAmount = (cube.at.z - scene.boundMin.z) / (scene.boundMax.z - scene.boundMin.z)
  55. cube.rotate = cube.rotateBasis:slerp(cube.rotateTarget, rotateAmount)
  56. end
  57. end
  58. end
  59. function scene.draw(pass)
  60. -- First, draw a floor
  61. local floorRecenter = scene.floorSize / 2 + 0.5
  62. for x = 1, scene.floorSize do
  63. for y = 1,scene.floorSize do
  64. if (x + y) % 2==0 then
  65. pass:setColor(0.25, 0.25, 0.25)
  66. else
  67. pass:setColor(0.5, 0.5, 0.5)
  68. end
  69. pass:plane(x - floorRecenter, 0, y - floorRecenter, 1, 1, math.pi / 2, 1, 0, 0)
  70. end
  71. end
  72. -- Draw cubes
  73. for _, cube in ipairs(scene.cubes) do
  74. pass:setColor(unpack(cube.color))
  75. pass:cube(cube.at, scene.cubeSize, cube.rotate:unpack())
  76. end
  77. end
  78. -- Now the cubemap stuff
  79. local cubemap = {}
  80. local unitX = vector(1, 0, 0)
  81. local unitY = vector(0, 1, 0)
  82. local unitZ = vector(0, 0, 1)
  83. function cubemap.load()
  84. -- Create cubemap texture
  85. cubemap.texture = lovr.graphics.newTexture(256, 256, 6, { type = "cube" })
  86. -- Precalculate cubemap View-Projection matrices
  87. local center = scene.sphereCenter
  88. cubemap.facePerspective = lovr.math.newMat4():perspective(math.rad(90), 1, .1, 0)
  89. cubemap.faces = {
  90. lovr.math.newMat4():lookAt(center, center - unitX, vec3(0, 1, 0)),
  91. lovr.math.newMat4():lookAt(center, center + unitX, vec3(0, 1, 0)),
  92. lovr.math.newMat4():lookAt(center, center + unitY, vec3(0, 0, -1)),
  93. lovr.math.newMat4():lookAt(center, center - unitY, vec3(0, 0, 1)),
  94. lovr.math.newMat4():lookAt(center, center + unitZ, vec3(0, 1, 0)),
  95. lovr.math.newMat4():lookAt(center, center - unitZ, vec3(0, 1, 0))
  96. }
  97. -- Create reflection shader
  98. cubemap.shader = lovr.graphics.newShader('unlit', [[
  99. uniform textureCube cubemap;
  100. vec4 lovrmain() {
  101. vec3 V = normalize(CameraPositionWorld - PositionWorld);
  102. vec3 N = normalize(Normal);
  103. vec3 R = reflect(-V, N);
  104. vec4 sphereColor = Color * getPixel(cubemap, R * vec3(-1, 1, 1));
  105. float ndi = dot(N, V) * 0.5 + 0.5; // Darken the sphere a little around the edges to give it apparent depth
  106. return vec4(sphereColor.rgb * ndi, 1.);
  107. }
  108. ]])
  109. -- Set up a render pass that renders to the cubemap
  110. cubemap.pass = lovr.graphics.newPass(cubemap.texture)
  111. cubemap.pass:setClear(lovr.graphics.getBackgroundColor())
  112. end
  113. function cubemap.draw()
  114. cubemap.pass:reset()
  115. for i = 1, 6 do
  116. cubemap.pass:setProjection(i, cubemap.facePerspective)
  117. cubemap.pass:setViewPose(i, cubemap.faces[i], true)
  118. end
  119. scene.draw(cubemap.pass)
  120. end
  121. -- Handle lovr
  122. function lovr.load()
  123. lovr.graphics.setBackgroundColor(0.9,0.9,0.9)
  124. scene.load()
  125. cubemap.load()
  126. end
  127. function lovr.update(dt)
  128. scene.update(dt)
  129. end
  130. function lovr.draw(pass)
  131. cubemap.draw()
  132. scene.draw(pass)
  133. -- Draw sphere textured with cube map
  134. pass:setColor(1, 0.6, 0.6)
  135. pass:setShader(cubemap.shader)
  136. pass:send('cubemap', cubemap.texture)
  137. pass:sphere(scene.sphereCenter, scene.sphereRad)
  138. return lovr.graphics.submit(cubemap.pass, pass)
  139. end