main.lua 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157
  1. -- This demo renders a scene to a canvas, then renders the canvas to screen filtered through a shader.
  2. --
  3. -- Sample contributed by andi mcc
  4. local useCanvas = true -- Set this to false to see the scene with no postprocessing.
  5. -- A shader program consists of a vertex shader (which describes how to transform polygons)
  6. -- and a fragment shader (which describes how to color pixels).
  7. -- For a full-screen shader, the vertex shader should just pass the polygon through unaltered.
  8. -- This is the same as the "default" full-screen shader used by lovr.graphics.plane:
  9. local screenShaderVertex = [[
  10. vec4 position(mat4 projection, mat4 transform, vec4 vertex) {
  11. return vertex;
  12. }
  13. ]]
  14. -- For the fragment shader: We are going to create a separable gaussian blur.
  15. -- A "separable" blur means we first blur horizontally, then blur vertically to get a 2D blur.
  16. local screenShaderFragment = [[
  17. // This one-dimensional blur filter samples five points and averages them by different amounts.
  18. // Weights and offsets taken from http://rastergrid.com/blog/2010/09/efficient-gaussian-blur-with-linear-sampling/
  19. // The weights for the center, one-point-out, and two-point-out samples
  20. #define WEIGHT0 0.2270270270
  21. #define WEIGHT1 0.3162162162
  22. #define WEIGHT2 0.0702702703
  23. // The distances-from-center for the samples
  24. #define OFFSET1 1.3846153846
  25. #define OFFSET2 3.2307692308
  26. // The Canvas texture to sample from.
  27. uniform sampler2DMultiview canvas;
  28. // UVs are sampled from a texture over the range 0..1.
  29. // This uniform is set outside the shader so we know what UV distance "one pixel" is.
  30. uniform vec2 resolution;
  31. // This uniform will be set every draw to determine whether we are sampling horizontally or vertically.
  32. uniform vec2 direction;
  33. // lovr's shader architecture will automatically supply a main(), which will call this color() function
  34. vec4 color(vec4 graphicsColor, sampler2D image, vec2 uv) {
  35. vec2 pixelOff = direction / resolution;
  36. vec4 color = vec4(0.0);
  37. color += textureMultiview(canvas, uv) * WEIGHT0;
  38. color += textureMultiview(canvas, uv + pixelOff * OFFSET1) * WEIGHT1;
  39. color += textureMultiview(canvas, uv - pixelOff * OFFSET1) * WEIGHT1;
  40. color += textureMultiview(canvas, uv + pixelOff * OFFSET2) * WEIGHT2;
  41. color += textureMultiview(canvas, uv - pixelOff * OFFSET2) * WEIGHT2;
  42. return color;
  43. }
  44. ]]
  45. -- The vertex and fragment shaders will be combined together into a shader program
  46. local screenShader
  47. -- Image of an eyechart
  48. local eyechart
  49. -- This table will contain two canvases we will use as scratch space
  50. local tempCanvas
  51. function lovr.load()
  52. -- Load the eyechart image
  53. -- Source: https://www.publicdomainpictures.net/view-image.php?image=244244&picture=eye-chart-test-vintage
  54. -- Creative Commons 0 / Public Domain license
  55. local texture = lovr.graphics.newTexture('eye-chart-test-vintage-cc0.jpg')
  56. local textureWidth, textureHeight = texture:getDimensions()
  57. eyechart = {
  58. scale = .75,
  59. aspect = textureHeight / textureWidth,
  60. material = lovr.graphics.newMaterial( texture )
  61. }
  62. -- Configure the shader
  63. if useCanvas then
  64. local width, height = lovr.headset.getDisplayDimensions()
  65. -- Compile the shader
  66. screenShader = lovr.graphics.newShader(screenShaderVertex, screenShaderFragment)
  67. -- Set the resolution uniform
  68. screenShader:send("resolution", {width, height})
  69. -- Create two temporary canvases
  70. tempCanvas = {
  71. lovr.graphics.newCanvas(width, height),
  72. lovr.graphics.newCanvas(width, height)
  73. }
  74. tempCanvas[1]:getTexture():setWrap('clamp')
  75. tempCanvas[2]:getTexture():setWrap('clamp')
  76. end
  77. end
  78. -- The scene is drawn in this callback
  79. local function sceneDraw()
  80. lovr.graphics.clear() -- Because we are drawing to a canvas, we must manually clear
  81. lovr.graphics.setShader(nil)
  82. -- Draw text on the left and right
  83. for _, sign in ipairs {-1, 1} do
  84. lovr.graphics.push()
  85. lovr.graphics.rotate(sign * math.pi/2, 0, 1, 0)
  86. lovr.graphics.print("MOVE CLOSER", 0, 0, -10, 5)
  87. lovr.graphics.pop()
  88. end
  89. lovr.graphics.plane(eyechart.material, 0, 1.7, -1, eyechart.scale, eyechart.scale * eyechart.aspect)
  90. end
  91. -- This simple callback is used to draw one canvas onto another
  92. local function fullScreenDraw(source)
  93. screenShader:send('canvas', source:getTexture())
  94. lovr.graphics.fill()
  95. end
  96. function lovr.draw()
  97. if not useCanvas then
  98. -- No-postprocessing path: Call scene-draw callback without doing anything fancy
  99. sceneDraw()
  100. else
  101. -- Start by drawing the scene to one of our temp canvases.
  102. tempCanvas[1]:renderTo(sceneDraw)
  103. tempCanvas[2]:renderTo(function() lovr.graphics.clear() end)
  104. -- We now have the scene in a texture (a canvas), which means we can apply a full-screen effect
  105. -- by rendering the texture with a shader material. However, because our blur is separable,
  106. -- we will need to do this twice, once for horizontal blur and once for vertical.
  107. -- We would also like to do multiple blur passes at larger and larger scales, to get a blurrier blur.
  108. -- To achieve these many passes we will render from canvas A into B, and then B back into A, and repeat.
  109. lovr.graphics.setShader(screenShader)
  110. screenShader:send("direction", {1, 0})
  111. tempCanvas[2]:renderTo(fullScreenDraw, tempCanvas[1])
  112. screenShader:send("direction", {0, 1})
  113. tempCanvas[1]:renderTo(fullScreenDraw, tempCanvas[2])
  114. screenShader:send("direction", {2, 0})
  115. tempCanvas[2]:renderTo(fullScreenDraw, tempCanvas[1])
  116. screenShader:send("direction", {0, 2})
  117. tempCanvas[1]:renderTo(fullScreenDraw, tempCanvas[2])
  118. screenShader:send("direction", {4, 0})
  119. tempCanvas[2]:renderTo(fullScreenDraw, tempCanvas[1])
  120. screenShader:send("direction", {0, 4})
  121. screenShader:send("canvas", tempCanvas[2]:getTexture())
  122. lovr.graphics.fill() -- On the final pass, render directly to the screen.
  123. end
  124. end