|
@@ -4,19 +4,9 @@
|
|
|
|
|
|
local useCanvas = true -- Set this to false to see the scene with no postprocessing.
|
|
|
|
|
|
--- 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 = [[
|
|
|
+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/
|
|
|
|
|
@@ -29,25 +19,23 @@ local screenShaderFragment = [[
|
|
|
#define OFFSET1 1.3846153846
|
|
|
#define OFFSET2 3.2307692308
|
|
|
|
|
|
- // The Canvas texture to sample from.
|
|
|
- uniform sampler2DMultiview canvas;
|
|
|
-
|
|
|
- // 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;
|
|
|
+ Constants {
|
|
|
+ // This constant will be set every draw to determine whether we are sampling horizontally or vertically.
|
|
|
+ vec2 direction;
|
|
|
+ };
|
|
|
|
|
|
- // This uniform will be set every draw to determine whether we are sampling horizontally or vertically.
|
|
|
- uniform 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 color() function
|
|
|
- vec4 color(vec4 graphicsColor, sampler2D image, vec2 uv) {
|
|
|
- vec2 pixelOff = direction / resolution;
|
|
|
+ // 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 += textureMultiview(canvas, uv) * WEIGHT0;
|
|
|
- color += textureMultiview(canvas, uv + pixelOff * OFFSET1) * WEIGHT1;
|
|
|
- color += textureMultiview(canvas, uv - pixelOff * OFFSET1) * WEIGHT1;
|
|
|
- color += textureMultiview(canvas, uv + pixelOff * OFFSET2) * WEIGHT2;
|
|
|
- color += textureMultiview(canvas, uv - pixelOff * OFFSET2) * WEIGHT2;
|
|
|
+ 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;
|
|
|
}
|
|
|
]]
|
|
@@ -67,10 +55,11 @@ function lovr.load()
|
|
|
-- 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,
|
|
|
- material = lovr.graphics.newMaterial( texture )
|
|
|
+ texture = texture
|
|
|
}
|
|
|
|
|
|
-- Configure the shader
|
|
@@ -78,49 +67,42 @@ function lovr.load()
|
|
|
local width, height = lovr.headset.getDisplayDimensions()
|
|
|
|
|
|
-- Compile the shader
|
|
|
- screenShader = lovr.graphics.newShader(screenShaderVertex, screenShaderFragment)
|
|
|
-
|
|
|
- -- Set the resolution uniform
|
|
|
- screenShader:send("resolution", {width, height})
|
|
|
+ screenShader = lovr.graphics.newShader('fill', blurShader)
|
|
|
|
|
|
-- Create two temporary canvases
|
|
|
tempCanvas = {
|
|
|
lovr.graphics.newCanvas(width, height),
|
|
|
lovr.graphics.newCanvas(width, height)
|
|
|
}
|
|
|
-
|
|
|
- tempCanvas[1]:getTexture():setWrap('clamp')
|
|
|
- tempCanvas[2]:getTexture():setWrap('clamp')
|
|
|
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)
|
|
|
-
|
|
|
+local function sceneDraw(pass)
|
|
|
-- 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()
|
|
|
+ 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
|
|
|
|
|
|
- lovr.graphics.plane(eyechart.material, 0, 1.7, -1, eyechart.scale, eyechart.scale * eyechart.aspect)
|
|
|
+ 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(source)
|
|
|
+local function fullScreenDraw(pass, source)
|
|
|
screenShader:send('canvas', source:getTexture())
|
|
|
- lovr.graphics.fill()
|
|
|
+ pass:fill()
|
|
|
end
|
|
|
|
|
|
-function lovr.draw()
|
|
|
+function lovr.draw(pass)
|
|
|
if not useCanvas then
|
|
|
|
|
|
-- No-postprocessing path: Call scene-draw callback without doing anything fancy
|
|
|
- sceneDraw()
|
|
|
+ sceneDraw(pass)
|
|
|
|
|
|
else
|
|
|
|
|
@@ -135,23 +117,23 @@ function lovr.draw()
|
|
|
-- 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})
|
|
|
+ pass:send("direction", {1, 0})
|
|
|
tempCanvas[2]:renderTo(fullScreenDraw, tempCanvas[1])
|
|
|
|
|
|
- screenShader:send("direction", {0, 1})
|
|
|
+ pass:send("direction", {0, 1})
|
|
|
tempCanvas[1]:renderTo(fullScreenDraw, tempCanvas[2])
|
|
|
|
|
|
- screenShader:send("direction", {2, 0})
|
|
|
+ pass:send("direction", {2, 0})
|
|
|
tempCanvas[2]:renderTo(fullScreenDraw, tempCanvas[1])
|
|
|
|
|
|
- screenShader:send("direction", {0, 2})
|
|
|
+ pass:send("direction", {0, 2})
|
|
|
tempCanvas[1]:renderTo(fullScreenDraw, tempCanvas[2])
|
|
|
|
|
|
- screenShader:send("direction", {4, 0})
|
|
|
+ pass:send("direction", {4, 0})
|
|
|
tempCanvas[2]:renderTo(fullScreenDraw, tempCanvas[1])
|
|
|
|
|
|
- screenShader:send("direction", {0, 4})
|
|
|
- screenShader:send("canvas", tempCanvas[2]:getTexture())
|
|
|
- lovr.graphics.fill() -- On the final pass, render directly to the screen.
|
|
|
+ pass:send("direction", {0, 4})
|
|
|
+ pass:send("canvas", tempCanvas[2]:getTexture())
|
|
|
+ pass:fill() -- On the final pass, render directly to the screen.
|
|
|
end
|
|
|
end
|