Browse Source

Add Canvas example demonstrating a separable gaussian blur over the whole screen

mcc 7 years ago
parent
commit
bd67071fb1
2 changed files with 148 additions and 0 deletions
  1. BIN
      examples/Canvas/eye-chart-test-vintage-cc0.jpg
  2. 148 0
      examples/Canvas/main.lua

BIN
examples/Canvas/eye-chart-test-vintage-cc0.jpg


+ 148 - 0
examples/Canvas/main.lua

@@ -0,0 +1,148 @@
+-- This demo renders a scene to a canvas, then renders the canvas to screen filtered through a shader.
+-- Set this to false to see the scene with no postprocessing.
+local useCanvas = true
+
+-- A shader program consists of a vertex shader (which describes how to transform polygons)
+-- and a fragment shader (which describes how to color pixels).
+-- For a full-screen shader, the vertex shader should just pass the polygon through unaltered.
+-- This is the same as the "default" full-screen shader used by lovr.graphics.plane:
+local screenShaderVertex = [[
+  vec4 position(mat4 projection, mat4 transform, vec4 vertex) {
+    return vertex;
+  }
+]]
+
+-- 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 screenShaderFragment = [[
+  // 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
+
+  // UVs are sampled from a texture over the range 0..1.
+  // This uniform is set outside the shader so we know what UV distance "one pixel" is.  
+  uniform vec2 resolution;
+  
+  // This uniform will be set every draw to determine whether we are sampling horizontally or vertically.
+  uniform vec2 direction;
+  
+  // lovr's shader architecture will automatically supply a main(), which will call this color() function 
+  vec4 color(vec4 graphicsColor, sampler2D image, vec2 uv) {
+    vec2 pixelOff = direction / resolution;
+    vec4 color = vec4(0.0);
+    color += texture2D(image, uv) * WEIGHT0;
+    color += texture2D(image, uv + pixelOff * OFFSET1) * WEIGHT1;
+    color += texture2D(image, uv - pixelOff * OFFSET1) * WEIGHT1;
+    color += texture2D(image, uv + pixelOff * OFFSET2) * WEIGHT2;
+    color += texture2D(image, uv - pixelOff * OFFSET2) * 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 = {
+    aspect = textureHeight / textureWidth,
+    material = lovr.graphics.newMaterial( texture )
+  }
+
+  -- Configure the shader
+  if useCanvas then
+    local width, height = lovr.headset.getDisplayDimensions()
+    
+    -- Compile the shader
+    screenShader = lovr.graphics.newShader(screenShaderVertex, screenShaderFragment)
+
+    -- Set the resolution uniform
+    screenShader:send("resolution", {width, height})
+
+    -- 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()
+  lovr.graphics.clear() -- Because we are drawing to a canvas, we must manually clear
+  lovr.graphics.setShader(nil)
+
+  -- Draw text on the left and right
+  for _, sign in ipairs {-1, 1} do
+    lovr.graphics.push()
+    lovr.graphics.rotate(sign * math.pi/2, 0, 1, 0)
+    lovr.graphics.print("MOVE CLOSER", 0, 0, -10, 5)
+    lovr.graphics.pop()
+  end
+
+  -- Because the eyechart image is non-square, we must stretch its plane
+  lovr.graphics.scale(1, eyechart.aspect, 1)
+  lovr.graphics.translate(0, 0.5, -0.75)
+  lovr.graphics.plane(eyechart.material, 0, 0, 0)
+end
+
+-- This simple callback is used to draw one canvas onto another
+local function fullScreenDraw(source)
+  lovr.graphics.plane(source)
+end
+
+function lovr.draw()
+  if not useCanvas then
+
+    -- No-postprocessing path: Call scene-draw callback without doing anything fancy
+    sceneDraw()
+
+  else
+
+    -- Start by drawing the scene to one of our temp canvases.
+    tempCanvas[1]:renderTo(sceneDraw)
+
+    -- 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)
+
+    screenShader:send("direction", {1, 0})
+    tempCanvas[2]:renderTo(fullScreenDraw, tempCanvas[1])
+
+    screenShader:send("direction", {0, 1})
+    tempCanvas[1]:renderTo(fullScreenDraw, tempCanvas[2])
+
+    screenShader:send("direction", {2, 0})
+    tempCanvas[2]:renderTo(fullScreenDraw, tempCanvas[1])
+
+    screenShader:send("direction", {0, 2})
+    tempCanvas[1]:renderTo(fullScreenDraw, tempCanvas[2])
+
+    screenShader:send("direction", {4, 0})
+    tempCanvas[2]:renderTo(fullScreenDraw, tempCanvas[1])
+
+    screenShader:send("direction", {0, 4})
+    lovr.graphics.plane(tempCanvas[2]) -- On the final pass, render directly to the screen.
+  end
+end