Skip to content

Instantly share code, notes, and snippets.

@runnerpack
Created March 8, 2015 18:35
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save runnerpack/6d51f5b40eff1bf1107a to your computer and use it in GitHub Desktop.
Save runnerpack/6d51f5b40eff1bf1107a to your computer and use it in GitHub Desktop.
Ashton problem
# Idea: use GLSL shaders in Gosu/Ashton to draw stereoscopic graphics
# Technologies (to be) supported:
# * Row interleaved
# * Over/under
# * Side-by-side
# * Anaglyph (Red/cyan, Green/magenta, Yellow/blue, and grayscale versions of those)
# * Rift-style HMDs(?)
# On FPR displays (like my Asus VG23AH) it works without special mode changes, expensive drivers, or fancy cables!
begin; require 'rubygems'; rescue LoadError; end
# $LOAD_PATH.unshift File.expand_path('../lib/', File.dirname(__FILE__))
require "ashton"
module Gosu3D
class Point2D
# Stores a 2D point
attr_accessor :x, :y, :c
def initialize(ix = 0, iy = 0, ic = 0xFFFFFFFF)
@x = ix
@y = iy
@c = ic
end
end # Point2D class
class Point3D
# Stores a 3D point and provides parallax-shifted and
# stereoscopic format-displaced values of the x and
# y coordinates, on-demand.
attr_reader :win, :lx, :rx, :ly, :ry, :x, :y, :z
attr_accessor :c
def initialize(win, ix = 0, iy = 0, iz = 0, ic = 0xFFFFFFFF)
@win = win
@x = ix
@y = iy
@z = iz
@c = ic
self.recalculate
end
def x= (nx)
@x = nx
recalculate
end
def y= (ny)
@y = ny
recalculate
end
def z= (nz)
@z = nz
recalculate
end
def win= (nwin)
@win = nwin
recalculate
end
def recalculate
if @win.tech == :sxs
@lx = (@x - (@z * 0.5)) * 0.5
@ly = @y
@rx = (@x + (@z * 0.5)) * 0.5 + @win.height * 0.5
@ry = @y
else
@lx = @x - (@z * 0.5)
@ly = @y * 0.5
@rx = @x + (@z * 0.5)
@ry = @y * 0.5 + @win.height * 0.5
end
end
def left; Point2D.new(@lx, @ly, @c); end
def right; Point2D.new(@rx, @ry, @c); end
end # Point3D class
W = 960
H = 540
FS = false
# W = 1920
# H = 1080
# FS = true
class Window3D < Gosu::Window
def near_file(path); File.expand_path "#{path}", File.dirname(__FILE__) end
attr_reader :tech
def initialize(w, h, fs = true, t = :hint)
super w, h, fs
self.caption = "Hardware-accelerated stereoscopy? Yes, please!"
@points = Array.new(10) {
color = Gosu::Color.rgba(255, 0, 0, 255)
color.hue = rand(360)
Gosu3D::Point3D.new(self, rand(W), rand(H), rand(30) - 15, color)
}
self.tech = t
end
def tech= (t)
# TODO: Input checking/filtering/fall-back
@tech = t
# Load the specified fragment shader
@shader = Ashton::Shader.new frag: near_file( "%s.glsl" % @tech.to_s )
end
def button_down(id)
if id == Gosu::KbEscape
close
elsif id == Gosu::KbTab
#
elsif id == Gosu::MsWheelUp
#
elsif id == Gosu::MsWheelDown
#
end
end
def draw
if button_down?(Gosu::KbTab)
# Convert the half-over/under image to the desired stereo output format
post_process (@shader) {
draw_3d
}
else
draw_3d
end
end
def draw_3d
(-1...@points.length - 1).each { |n|
line_3d(@points[n], @points[n + 1])
}
end
def line_3d(p1, p2, mode = :default)
l1 = p1.left
r1 = p1.right
l2 = p2.left
r2 = p2.right
z = [p1.z, p2.z].max
# Draw the left-eye line
draw_line(l1.x, l1.y, l1.c, l2.x, l2.y, l2.c, z, mode)
# Draw the right-eye line
draw_line(r1.x, r1.y, r1.c, r2.x, r2.y, r2.c, z, mode)
end
def triangle_3d(p1, p2, p3, mode = :default)
end
def quad_3d(p1, p2, p3, p4, mode = :default)
end
end # Window3D class
# class Image3D
# def initialize(win, src, tileable = false, stereo = true)
# @win = win
# @stereo = stereo
# if stereo
# @pair = Gosu::Image.load_tiles(win, src, -2, 1, tileable)
# @image = nil
# else
# @pair = nil
# @image = Gosu::Image.new(win, src, tileable)
# end
# end
# def draw(x, y, z, factor_x = 1, factor_y = 1, color = 0xffffffff, mode = :default)
# if @stereo
# l = @pair[1]
# r = @pair[0]
# else
# l = r = @image
# end
# l.draw(x - (z * 0.5), y * 0.5, z, factor_x, factor_y * 0.5, color, mode)
# r.draw(x + (z * 0.5), y * 0.5 + @win.height * 0.5, z, factor_x, factor_y * 0.5, color, mode)
# end
# def draw_as_quad(*args)
# puts "Warning: Drawing a stereo pair as a random quadrilateral may damage someone's eyeballs!"
# end
# def draw_rot(*args)
# puts "Warning: Drawing a stereo pair rotated may damage someone's eyeballs!"
# end
# end # Image3D class
Window3D.new(W, H, FS).show
end # Gosu3D module
// Convert a half-over/under stereo pair to a horizontally-interleaved stereo pair
uniform int in_WindowHeight;
uniform sampler2D in_Texture;
varying vec2 var_TexCoord;
// Algorithm:
// Save a copy of the destination pixel's coordinates with the Y value halved.
// Convert the destination pixel's Y coordinate to an integer, and test it.
// If it's even, add 0.5 to the saved Y coordinate to get the pixel from the bottom half of the texture.
void main()
{
vec2 texel = vec2(var_TexCoord.x, var_TexCoord.y / 2.0);
if (mod(floor(var_TexCoord.y * float(in_WindowHeight)), 2.0) < 0.01)
texel.y += 0.5;
gl_FragColor = texture2D(in_Texture, texel);
}
@runnerpack
Copy link
Author

(Sorry about the clutter...)
Desired output:
When tab is held down, the top and bottom halves of the window should be interlaced, pixel-by-pixel.
Actual output:
The first time tab is pressed, you can see that it seems to work for one frame, then everything goes black. Even when tab is released, the window stays black.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment