Browse Source

Port Blur example;

bjorn 2 years ago
parent
commit
5c84fecc71
1 changed files with 152 additions and 139 deletions
  1. 152 139
      examples/Effects/Blur/main.lua

+ 152 - 139
examples/Effects/Blur/main.lua

@@ -1,139 +1,152 @@
--- This demo renders a scene to a canvas, then renders the canvas to screen filtered through a shader.
---
--- Sample contributed by andi mcc
-
-local useCanvas = true -- Set this to false to see the scene with no postprocessing.
-
--- For the fragment shader: We are going to create a separable gaussian blur.
--- A "separable" blur means we first blur horizontally, then blur vertically to get a 2D blur.
-local blurShader = [[
-  // This one-dimensional blur filter samples five points and averages them by different amounts.
-  // Weights and offsets taken from http://rastergrid.com/blog/2010/09/efficient-gaussian-blur-with-linear-sampling/
-
-  // The weights for the center, one-point-out, and two-point-out samples
-  #define WEIGHT0 0.2270270270
-  #define WEIGHT1 0.3162162162
-  #define WEIGHT2 0.0702702703
-
-  // The distances-from-center for the samples
-  #define OFFSET1 1.3846153846
-  #define OFFSET2 3.2307692308
-
-  Constants {
-    // This constant will be set every draw to determine whether we are sampling horizontally or vertically.
-    vec2 direction;
-  };
-
-  // The Canvas texture to sample from.
-  layout(set = 2, binding = 0) uniform texture2DArray canvas;
-
-  // lovr's shader architecture will automatically supply a main(), which will call this lovrmain() function
-  vec4 lovrmain() {
-    vec2 uvOffset = direction / Resolution.xy; // Convert the offset from pixels to UVs
-    vec4 color = vec4(0.0);
-    color += getPixel(canvas, UV, ViewIndex) * WEIGHT0;
-    color += getPixel(canvas, UV + uvOffset * OFFSET1, ViewIndex) * WEIGHT1;
-    color += getPixel(canvas, UV - uvOffset * OFFSET1, ViewIndex) * WEIGHT1;
-    color += getPixel(canvas, UV + uvOffset * OFFSET2, ViewIndex) * WEIGHT2;
-    color += getPixel(canvas, UV - uvOffset * OFFSET2, ViewIndex) * WEIGHT2;
-    return color;
-  }
-]]
-
--- The vertex and fragment shaders will be combined together into a shader program
-local screenShader
-
--- Image of an eyechart
-local eyechart
-
--- This table will contain two canvases we will use as scratch space
-local tempCanvas
-
-function lovr.load()
-  -- Load the eyechart image
-  -- Source: https://www.publicdomainpictures.net/view-image.php?image=244244&picture=eye-chart-test-vintage
-  -- Creative Commons 0 / Public Domain license
-  local texture = lovr.graphics.newTexture('eye-chart-test-vintage-cc0.jpg')
-  local textureWidth, textureHeight = texture:getDimensions()
-
-  eyechart = {
-    scale = .75,
-    aspect = textureHeight / textureWidth,
-    texture = texture
-  }
-
-  -- Configure the shader
-  if useCanvas then
-    local width, height = lovr.headset.getDisplayDimensions()
-
-    -- Compile the shader
-    screenShader = lovr.graphics.newShader('fill', blurShader)
-
-    -- Create two temporary canvases
-    tempCanvas = {
-      lovr.graphics.newCanvas(width, height),
-      lovr.graphics.newCanvas(width, height)
-    }
-  end
-end
-
--- The scene is drawn in this callback
-local function sceneDraw(pass)
-  -- Draw text on the left and right
-  for _, sign in ipairs { -1, 1 } do
-    pass:push()
-    pass:rotate(sign * math.pi / 2, 0, 1, 0)
-    pass:text('MOVE CLOSER', 0, 0, -10, 5)
-    pass:pop()
-  end
-
-  pass:setMaterial(eyechart.texture)
-  pass:plane(0, 1.7, -1, eyechart.scale, eyechart.scale * eyechart.aspect)
-  pass:setMaterial()
-end
-
--- This simple callback is used to draw one canvas onto another
-local function fullScreenDraw(pass, source)
-  screenShader:send('canvas', source:getTexture())
-  pass:fill()
-end
-
-function lovr.draw(pass)
-  if not useCanvas then
-
-    -- No-postprocessing path: Call scene-draw callback without doing anything fancy
-    sceneDraw(pass)
-
-  else
-
-    -- Start by drawing the scene to one of our temp canvases.
-    tempCanvas[1]:renderTo(sceneDraw)
-    tempCanvas[2]:renderTo(function() lovr.graphics.clear() end)
-
-    -- We now have the scene in a texture (a canvas), which means we can apply a full-screen effect
-    -- by rendering the texture with a shader material. However, because our blur is separable,
-    -- we will need to do this twice, once for horizontal blur and once for vertical.
-    -- We would also like to do multiple blur passes at larger and larger scales, to get a blurrier blur.
-    -- To achieve these many passes we will render from canvas A into B, and then B back into A, and repeat.
-    lovr.graphics.setShader(screenShader)
-
-    pass:send("direction", {1, 0})
-    tempCanvas[2]:renderTo(fullScreenDraw, tempCanvas[1])
-
-    pass:send("direction", {0, 1})
-    tempCanvas[1]:renderTo(fullScreenDraw, tempCanvas[2])
-
-    pass:send("direction", {2, 0})
-    tempCanvas[2]:renderTo(fullScreenDraw, tempCanvas[1])
-
-    pass:send("direction", {0, 2})
-    tempCanvas[1]:renderTo(fullScreenDraw, tempCanvas[2])
-
-    pass:send("direction", {4, 0})
-    tempCanvas[2]:renderTo(fullScreenDraw, tempCanvas[1])
-
-    pass:send("direction", {0, 4})
-    pass:send("canvas", tempCanvas[2]:getTexture())
-    pass:fill() -- On the final pass, render directly to the screen.
-  end
-end
+-- This demo renders a scene to a canvas, then renders the canvas to screen filtered through a shader.
+--
+-- Sample contributed by andi mcc
+
+local applyBlur = true -- Set this to false to see the scene with no postprocessing.
+
+-- For the fragment shader: We are going to create a separable gaussian blur.
+-- A "separable" blur means we first blur horizontally, then blur vertically to get a 2D blur.
+local blurShader = [[
+  // This one-dimensional blur filter samples five points and averages them by different amounts.
+  // Weights and offsets taken from http://rastergrid.com/blog/2010/09/efficient-gaussian-blur-with-linear-sampling/
+
+  // The weights for the center, one-point-out, and two-point-out samples
+  #define WEIGHT0 0.2270270270
+  #define WEIGHT1 0.3162162162
+  #define WEIGHT2 0.0702702703
+
+  // The distances-from-center for the samples
+  #define OFFSET1 1.3846153846
+  #define OFFSET2 3.2307692308
+
+  Constants {
+    // This constant will be set every draw to determine whether we are sampling horizontally or vertically.
+    vec2 direction;
+  };
+
+  // The texture to sample from.
+  layout(set = 2, binding = 0) uniform texture2DArray sourceTexture;
+
+  // lovr's shader architecture will automatically supply a main(), which will call this lovrmain() function
+  vec4 lovrmain() {
+    vec2 uvOffset = direction / Resolution.xy; // Convert the offset from pixels to UVs
+    vec4 color = vec4(0.0);
+    color += getPixel(sourceTexture, UV, ViewIndex) * WEIGHT0;
+    color += getPixel(sourceTexture, UV + uvOffset * OFFSET1, ViewIndex) * WEIGHT1;
+    color += getPixel(sourceTexture, UV - uvOffset * OFFSET1, ViewIndex) * WEIGHT1;
+    color += getPixel(sourceTexture, UV + uvOffset * OFFSET2, ViewIndex) * WEIGHT2;
+    color += getPixel(sourceTexture, UV - uvOffset * OFFSET2, ViewIndex) * WEIGHT2;
+    return color;
+  }
+]]
+
+-- The vertex and fragment shaders will be combined together into a shader program
+local screenShader
+
+-- Image of an eyechart
+local eyechart
+
+-- This table will contain two textures we will use as scratch space
+local tempTexture
+
+function lovr.load()
+  -- Load the eyechart image
+  -- Source: https://www.publicdomainpictures.net/view-image.php?image=244244&picture=eye-chart-test-vintage
+  -- Creative Commons 0 / Public Domain license
+  local texture = lovr.graphics.newTexture('eye-chart-test-vintage-cc0.jpg')
+  local textureWidth, textureHeight = texture:getDimensions()
+
+  eyechart = {
+    scale = .75,
+    aspect = textureHeight / textureWidth,
+    texture = texture
+  }
+
+  -- Configure the objects needed for the blur
+  if applyBlur then
+    local width, height = lovr.headset.getDisplayDimensions()
+    local layers = lovr.headset.getViewCount()
+
+    -- Compile the shader
+    screenShader = lovr.graphics.newShader('fill', blurShader)
+
+    -- Create two temporary textures
+    tempTexture = {
+      lovr.graphics.newTexture(width, height, layers, { mipmaps = false }),
+      lovr.graphics.newTexture(width, height, layers, { mipmaps = false })
+    }
+  end
+end
+
+-- The scene is drawn in this callback
+local function sceneDraw(pass)
+
+  -- Draw text on the left and right
+  for _, sign in ipairs { -1, 1 } do
+    pass:push()
+    pass:rotate(sign * math.pi / 2, 0, 1, 0)
+    pass:text('MOVE CLOSER', 0, 0, -10, 5)
+    pass:pop()
+  end
+
+  -- Draw the eye chart
+  pass:setMaterial(eyechart.texture)
+  pass:plane(0, 1.7, -1, eyechart.scale, eyechart.scale * eyechart.aspect)
+end
+
+-- This simple function is used to render a render pass that
+-- draws one texture onto another with the blur shader
+local function fullScreenDraw(source, destination, blurSize)
+  local pass = lovr.graphics.getPass('render', { destination, depth = false, samples = 1 })
+  pass:setShader(screenShader)
+  pass:send('sourceTexture', source)
+  pass:send('direction', blurSize)
+  pass:fill()
+  return pass
+end
+
+function lovr.draw(pass)
+  if not applyBlur then
+
+    -- No-postprocessing path: Call scene-draw callback without doing anything fancy
+    sceneDraw(pass)
+
+  else
+    local passes = {}
+
+    -- Start by drawing the scene to one of our temp textures.
+    local scene = lovr.graphics.getPass('render', tempTexture[1])
+
+    -- Make the scene pass use the same cameras as the headset
+    for i = 1, pass:getViewCount() do
+      scene:setViewPose(i, pass:getViewPose(i))
+      scene:setProjection(i, pass:getProjection(i, mat4()))
+    end
+
+    sceneDraw(scene)
+
+    table.insert(passes, scene)
+
+    -- We now have the scene in a texture, which means we can apply a full-screen effect by
+    -- rendering the texture with a shader. However, because our blur is separable,
+    -- we will need to do this twice, once for horizontal blur and once for vertical.
+    -- We would also like to do multiple blur passes at larger and larger scales, to get a blurrier blur.
+    -- To achieve these many passes we will render from texture 1 into 2, and then 2 back into 1, and repeat.
+
+    table.insert(passes, fullScreenDraw(tempTexture[1], tempTexture[2], { 1, 0 }))
+    table.insert(passes, fullScreenDraw(tempTexture[2], tempTexture[1], { 0, 1 }))
+
+    table.insert(passes, fullScreenDraw(tempTexture[1], tempTexture[2], { 2, 0 }))
+    table.insert(passes, fullScreenDraw(tempTexture[2], tempTexture[1], { 0, 2 }))
+
+    table.insert(passes, fullScreenDraw(tempTexture[1], tempTexture[2], { 4, 0 }))
+    table.insert(passes, fullScreenDraw(tempTexture[2], tempTexture[1], { 0, 4 }))
+
+    -- Finally, draw the blurred texture to the main display
+    pass:fill(tempTexture[1])
+
+    table.insert(passes, pass)
+
+    return lovr.graphics.submit(passes)
+  end
+end