main.lua 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154
  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 applyBlur = true -- Set this to false to see the scene with no postprocessing.
  5. -- For the fragment shader: We are going to create a separable gaussian blur.
  6. -- A "separable" blur means we first blur horizontally, then blur vertically to get a 2D blur.
  7. local blurShader = [[
  8. // This one-dimensional blur filter samples five points and averages them by different amounts.
  9. // Weights and offsets taken from http://rastergrid.com/blog/2010/09/efficient-gaussian-blur-with-linear-sampling/
  10. // The weights for the center, one-point-out, and two-point-out samples
  11. #define WEIGHT0 0.2270270270
  12. #define WEIGHT1 0.3162162162
  13. #define WEIGHT2 0.0702702703
  14. // The distances-from-center for the samples
  15. #define OFFSET1 1.3846153846
  16. #define OFFSET2 3.2307692308
  17. Constants {
  18. // This constant will be set every draw to determine whether we are sampling horizontally or vertically.
  19. vec2 direction;
  20. };
  21. // The texture to sample from.
  22. uniform texture2DArray sourceTexture;
  23. // lovr's shader architecture will automatically supply a main(), which will call this lovrmain() function
  24. vec4 lovrmain() {
  25. vec2 uvOffset = direction / Resolution.xy; // Convert the offset from pixels to UVs
  26. vec4 color = vec4(0.0);
  27. color += getPixel(sourceTexture, UV, ViewIndex) * WEIGHT0;
  28. color += getPixel(sourceTexture, UV + uvOffset * OFFSET1, ViewIndex) * WEIGHT1;
  29. color += getPixel(sourceTexture, UV - uvOffset * OFFSET1, ViewIndex) * WEIGHT1;
  30. color += getPixel(sourceTexture, UV + uvOffset * OFFSET2, ViewIndex) * WEIGHT2;
  31. color += getPixel(sourceTexture, UV - uvOffset * OFFSET2, ViewIndex) * WEIGHT2;
  32. return color;
  33. }
  34. ]]
  35. -- The vertex and fragment shaders will be combined together into a shader program
  36. local screenShader
  37. -- Image of an eyechart
  38. local eyechart
  39. -- This table will contain two textures we will use as scratch space
  40. local tempTexture
  41. function lovr.load()
  42. -- Load the eyechart image
  43. -- Source: https://www.publicdomainpictures.net/view-image.php?image=244244&picture=eye-chart-test-vintage
  44. -- Creative Commons 0 / Public Domain license
  45. local texture = lovr.graphics.newTexture('eye-chart-test-vintage-cc0.jpg')
  46. local textureWidth, textureHeight = texture:getDimensions()
  47. eyechart = {
  48. scale = .75,
  49. aspect = textureHeight / textureWidth,
  50. texture = texture
  51. }
  52. -- Configure the objects needed for the blur
  53. if applyBlur then
  54. local width, height = lovr.headset.getDisplayDimensions()
  55. local layers = lovr.headset.getViewCount()
  56. -- Compile the shader
  57. screenShader = lovr.graphics.newShader('fill', blurShader)
  58. -- Create two temporary textures
  59. tempTexture = {
  60. lovr.graphics.newTexture(width, height, layers, { mipmaps = false }),
  61. lovr.graphics.newTexture(width, height, layers, { mipmaps = false })
  62. }
  63. -- Make a clamping sampler (clampler, get it?) to prevent blurred
  64. -- pixels from wrapping around the edges
  65. clampler = lovr.graphics.newSampler({ wrap = 'clamp' })
  66. end
  67. end
  68. -- The scene is drawn in this callback
  69. local function sceneDraw(pass)
  70. -- Draw text on the left and right
  71. for _, sign in ipairs { -1, 1 } do
  72. pass:push()
  73. pass:rotate(sign * math.pi / 2, 0, 1, 0)
  74. pass:text('MOVE CLOSER', 0, 0, -10, 5)
  75. pass:pop()
  76. end
  77. -- Draw the eye chart
  78. pass:setMaterial(eyechart.texture)
  79. pass:plane(0, 1.7, -1, eyechart.scale, eyechart.scale * eyechart.aspect)
  80. end
  81. -- This simple function is used to render a render pass that
  82. -- draws one texture onto another with the blur shader
  83. local function fullScreenDraw(source, destination, blurSize)
  84. local pass = lovr.graphics.newPass({ destination, depth = false, samples = 1 })
  85. pass:setSampler(clampler)
  86. pass:setShader(screenShader)
  87. pass:send('sourceTexture', source)
  88. pass:send('direction', blurSize)
  89. pass:fill()
  90. return pass
  91. end
  92. function lovr.draw(pass)
  93. if not applyBlur then
  94. -- No-postprocessing path: Call scene-draw callback without doing anything fancy
  95. sceneDraw(pass)
  96. else
  97. local passes = {}
  98. -- Start by drawing the scene to one of our temp textures.
  99. local scene = lovr.graphics.newPass(tempTexture[1])
  100. -- Make the scene pass use the same cameras as the headset
  101. for i = 1, pass:getViewCount() do
  102. scene:setViewPose(i, pass:getViewPose(i))
  103. scene:setProjection(i, pass:getProjection(i, mat4()))
  104. end
  105. sceneDraw(scene)
  106. table.insert(passes, scene)
  107. -- We now have the scene in a texture, which means we can apply a full-screen effect by
  108. -- rendering the texture with a shader. However, because our blur is separable,
  109. -- we will need to do this twice, once for horizontal blur and once for vertical.
  110. -- We would also like to do multiple blur passes at larger and larger scales, to get a blurrier blur.
  111. -- To achieve these many passes we will render from texture 1 into 2, and then 2 back into 1, and repeat.
  112. table.insert(passes, fullScreenDraw(tempTexture[1], tempTexture[2], { 1, 0 }))
  113. table.insert(passes, fullScreenDraw(tempTexture[2], tempTexture[1], { 0, 1 }))
  114. table.insert(passes, fullScreenDraw(tempTexture[1], tempTexture[2], { 2, 0 }))
  115. table.insert(passes, fullScreenDraw(tempTexture[2], tempTexture[1], { 0, 2 }))
  116. table.insert(passes, fullScreenDraw(tempTexture[1], tempTexture[2], { 4, 0 }))
  117. table.insert(passes, fullScreenDraw(tempTexture[2], tempTexture[1], { 0, 4 }))
  118. -- Finally, draw the blurred texture to the main display
  119. pass:fill(tempTexture[1])
  120. table.insert(passes, pass)
  121. return lovr.graphics.submit(passes)
  122. end
  123. end