Skip to content

Instantly share code, notes, and snippets.

@bil-bas
Created July 6, 2012 13:44
Show Gist options
  • Save bil-bas/3060237 to your computer and use it in GitHub Desktop.
Save bil-bas/3060237 to your computer and use it in GitHub Desktop.
Shader/Framebuffer tests
/framebuffer.png
/framebuffer_section.png
uniform sampler2D tex;
uniform float contrast;
void main(void)
{
vec4 color = texture2D(tex, gl_TexCoord[0].xy);
color = (color-0.5)*contrast + 0.5;
gl_FragColor.rgb = color.rgb;
gl_FragColor.a = 1.0;
}
uniform sampler2D texUnit0;
uniform float fade;
void main(void) {
vec4 color;
color = texture2D(texUnit0, gl_TexCoord[0].xy);
gl_FragColor = color * fade;
}
class Framebuffer
attr_reader :width, :height
def initialize(width, height)
@width, @height = width.to_i, height.to_i
@fbo, @fbo_texture = init_framebuffer
#@fbo_depth = init_framebuffer_depth # Do we even need this for Gosu?
status = glCheckFramebufferStatusEXT GL_FRAMEBUFFER_EXT
raise unless status == GL_FRAMEBUFFER_COMPLETE_EXT
glBindFramebufferEXT GL_FRAMEBUFFER_EXT, 0
glBindRenderbufferEXT GL_RENDERBUFFER_EXT, 0
end
# Clears the buffer to transparent.
def clear(options = {})
options = {
color: [0.0, 0.0, 0.0, 0.0],
}.merge! options
glClearColor *options[:color]
glClear GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT
end
# Enable the framebuffer to use (e.g. to draw or convert it).
def use
raise unless block_given?
glBindFramebufferEXT GL_FRAMEBUFFER_EXT, @fbo
result = yield self
glBindFramebufferEXT GL_FRAMEBUFFER_EXT, 0
result
end
# BUG: Draws inverted.
def draw x, y
glEnable GL_TEXTURE_2D
glBindTexture GL_TEXTURE_2D, @fbo_texture
glBegin GL_QUADS do
glTexCoord2d 0, 1
glVertex2d x, y + @height # BL
glTexCoord2d 0, 0
glVertex2d x, y # TL
glTexCoord2d 1, 0
glVertex2d x + @width, y # TR
glTexCoord2d 1, 1
glVertex2d x + @width, y + @height # BR
end
glBindTexture GL_TEXTURE_2D, 0
end
# Convert the current contents of the buffer into a Gosu::Image
#
#
# @bug Image will be inverted (Maybe use a second buffer to turn it?).
#
# @option options [Boolean] :caching - TexPlay behaviour.
# @option options [Boolean] :tileable - Standard Gosu behaviour.
# @option options [Array<Integer>] :rect Area to save [x, y, w, h]
def to_image(options = {})
options = {
rect: [0, 0, @width, @height],
}.merge! options
rect = options[:rect]
# Draw onto the clean flip buffer, in order to flip before saving.
@fbo_flip, @fbo_flip_texture = init_framebuffer unless defined? @fbo_flip
glBindFramebufferEXT GL_FRAMEBUFFER_EXT, @fbo_flip
glClearColor 0, 0, 0, 0
glClear GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT
draw 0, 0
# Read the data in the flip-buffer.
glBindTexture GL_TEXTURE_2D, @fbo_flip_texture
blob = glReadPixels *rect, GL_RGBA, GL_UNSIGNED_BYTE
# Clean up.
glBindTexture GL_TEXTURE_2D, 0
glBindFramebufferEXT GL_FRAMEBUFFER_EXT, 0
# Create a new Image from the flipped pixel data.
Gosu::Image.new $window, TexPlay::ImageStub.new(blob, rect[2], rect[3]), options
end
protected
def init_framebuffer_depth
fbo_depth = glGenRenderbuffersEXT(1)[0]
glBindRenderbufferEXT GL_RENDERBUFFER_EXT, fbo_depth
glRenderbufferStorageEXT GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT24,
@width, @height
glFramebufferRenderbufferEXT GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT,
GL_RENDERBUFFER_EXT, fbo_depth
fbo_depth
end
# Create an fbo and its texture
def init_framebuffer
fbo = glGenFramebuffersEXT(1)[0]
glBindFramebufferEXT GL_FRAMEBUFFER_EXT, fbo
texture = init_framebuffer_texture
glFramebufferTexture2DEXT GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, texture, 0
[fbo, texture]
end
protected
# Called by init_framebuffer.
def init_framebuffer_texture
texture = glGenTextures(1)[0]
glBindTexture GL_TEXTURE_2D, texture
glTexParameterf GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE
glTexParameterf GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE
glTexParameteri GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST
glTexParameteri GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST
glTexImage2D GL_TEXTURE_2D, 0, GL_RGBA8, @width, @height,
0, GL_RGBA, GL_UNSIGNED_BYTE, nil
glBindTexture GL_TEXTURE_2D, 0 # Unbind the texture
texture
end
protected
def delete
glDeleteFramebuffersEXT @fbo
glDeleteTextures @fbo_texture
#glDeleteRenderbuffersEXT 1, @fbo_depth
glDeleteFramebuffersEXT @fbo_flip if defined? @fbo_flip
glDeleteTextures @fbo_flip_texture if defined? @fbo_flip_texture
glBindFramebufferEXT GL_FRAMEBUFFER_EXT, 0
end
end
uniform sampler2D texUnit0;
uniform int t;
float rand(vec2 co) {
return fract(sin(dot(co.xy, vec2(12.9898, 78.233))) * 43758.5453);
}
void main(void) {
vec4 color;
color = texture2D(texUnit0, gl_TexCoord[0].xy);
vec3 mezzo = vec3(0.0);
if(rand(gl_TexCoord[0].xy + float(t)/640.0) <= color.r) { mezzo.r = 1.0; }
if(rand(gl_TexCoord[0].xy + float(t)/640.0) <= color.g) { mezzo.g = 1.0; }
if(rand(gl_TexCoord[0].xy + float(t)/640.0) <= color.b) { mezzo.b = 1.0; }
gl_FragColor.rgb = mezzo;
}
uniform sampler2D texUnit0;
uniform float intensity;
uniform int t;
float rand(vec2 co) {
return fract(sin(dot(co.xy, vec2(12.9898, 78.233))) * 43758.5453);
}
void main(void) {
vec4 color;
color = texture2D(texUnit0, gl_TexCoord[0].xy);
vec4 influence = min(color, 1.0-color);
float noise = 1.0 - 2.0*rand(gl_TexCoord[0].xy + float(t)/640.0);
gl_FragColor = color + intensity*influence*noise;
}
# The tutorial game over a landscape rendered with OpenGL.
# Basically shows how arbitrary OpenGL calls can be put into
# the block given to Window#gl, and that Gosu Images can be
# used as textures using the gl_tex_info call.
require 'rubygems'
require 'gosu'
require 'gl'
require 'glu'
include Gl
include Glu
module ZOrder
Stars, Player, UI = *0..3
end
# The only really new class here.
# Draws a scrolling, repeating texture with a randomized height map.
class GLBackground
# Height map size
POINTS_X = 7
POINTS_Y = 7
# Scrolling speed
SCROLLS_PER_STEP = 50
def initialize(window)
@image = Gosu::Image.new(window, "media/Earth.png", true)
@scrolls = 0
@height_map = Array.new(POINTS_Y) { Array.new(POINTS_X) { rand } }
end
def scroll
@scrolls += 1
if @scrolls == SCROLLS_PER_STEP then
@scrolls = 0
@height_map.shift
@height_map.push Array.new(POINTS_X) { rand }
end
end
def exec_gl
# Get the name of the OpenGL texture the Image resides on, and the
# u/v coordinates of the rect it occupies.
# gl_tex_info can return nil if the image was too large to fit onto
# a single OpenGL texture and was internally split up.
info = @image.gl_tex_info
return unless info
# Pretty straightforward OpenGL code.
glDepthFunc(GL_GEQUAL)
glEnable(GL_DEPTH_TEST)
glEnable(GL_BLEND)
glMatrixMode(GL_PROJECTION)
glLoadIdentity
glFrustum(-0.10, 0.10, -0.075, 0.075, 1, 100)
glMatrixMode(GL_MODELVIEW)
glLoadIdentity
glTranslate(0, 0, -4)
glEnable(GL_TEXTURE_2D)
glBindTexture(GL_TEXTURE_2D, info.tex_name)
offs_y = 1.0 * @scrolls / SCROLLS_PER_STEP
0.upto(POINTS_Y - 2) do |y|
0.upto(POINTS_X - 2) do |x|
glBegin(GL_TRIANGLE_STRIP)
z = @height_map[y][x]
glColor4d(1, 1, 1, z)
glTexCoord2d(info.left, info.top)
glVertex3d(-0.5 + (x - 0.0) / (POINTS_X-1), -0.5 + (y - offs_y - 0.0) / (POINTS_Y-2), z)
z = @height_map[y+1][x]
glColor4d(1, 1, 1, z)
glTexCoord2d(info.left, info.bottom)
glVertex3d(-0.5 + (x - 0.0) / (POINTS_X-1), -0.5 + (y - offs_y + 1.0) / (POINTS_Y-2), z)
z = @height_map[y][x + 1]
glColor4d(1, 1, 1, z)
glTexCoord2d(info.right, info.top)
glVertex3d(-0.5 + (x + 1.0) / (POINTS_X-1), -0.5 + (y - offs_y - 0.0) / (POINTS_Y-2), z)
z = @height_map[y+1][x + 1]
glColor4d(1, 1, 1, z)
glTexCoord2d(info.right, info.bottom)
glVertex3d(-0.5 + (x + 1.0) / (POINTS_X-1), -0.5 + (y - offs_y + 1.0) / (POINTS_Y-2), z)
glEnd
end
end
end
end
# Roughly adapted from the tutorial game. Always faces north.
class Player
Speed = 7
attr_reader :score
def initialize(window, x, y)
@image = Gosu::Image.new(window, "media/Starfighter.bmp", false)
@beep = Gosu::Sample.new(window, "media/Beep.wav")
@x, @y = x, y
@score = 0
end
def move_left
@x = [@x - Speed, 0].max
end
def move_right
@x = [@x + Speed, 800].min
end
def accelerate
@y = [@y - Speed, 50].max
end
def brake
@y = [@y + Speed, 600].min
end
def draw
@image.draw(@x - @image.width / 2, @y - @image.height / 2, ZOrder::Player)
end
def collect_stars(stars)
stars.reject! do |star|
if Gosu::distance(@x, @y, star.x, star.y) < 35 then
@score += 10
@beep.play
true
else
false
end
end
end
end
# Also taken from the tutorial, but drawn with draw_rot and an increasing angle
# for extra rotation coolness!
class Star
attr_reader :x, :y
def initialize(animation)
@animation = animation
@color = Gosu::Color.new(0xff000000)
@color.red = rand(255 - 40) + 40
@color.green = rand(255 - 40) + 40
@color.blue = rand(255 - 40) + 40
@x = rand * 800
@y = 0
end
def draw
img = @animation[Gosu::milliseconds / 100 % @animation.size];
img.draw_rot(@x, @y, ZOrder::Stars, @y, 0.5, 0.5, 1, 1, @color, :add)
end
def update
# Move towards bottom of screen
@y += 3
# Return false when out of screen (gets deleted then)
@y < 650
end
end
class GameWindow < Gosu::Window
def initialize
super(800, 600, false)
self.caption = "Gosu & OpenGL Integration Demo"
@gl_background = GLBackground.new(self)
@player = Player.new(self, 400, 500)
@star_anim = Gosu::Image::load_tiles(self, "media/Star.png", 25, 25, false)
@stars = Array.new
@font = Gosu::Font.new(self, Gosu::default_font_name, 20)
end
def update
@player.move_left if button_down? Gosu::KbLeft or button_down? Gosu::GpLeft
@player.move_right if button_down? Gosu::KbRight or button_down? Gosu::GpRight
@player.accelerate if button_down? Gosu::KbUp or button_down? Gosu::GpUp
@player.brake if button_down? Gosu::KbDown or button_down? Gosu::GpDown
@player.collect_stars(@stars)
@stars.reject! { |star| !star.update }
@gl_background.scroll
@stars.push(Star.new(@star_anim)) if rand(20) == 0
end
def draw
# gl will execute the given block in a clean OpenGL environment, then reset
# everything so Gosu's rendering can take place again.
gl do
glClearColor(0.0, 0.2, 0.5, 1.0)
glClearDepth(0)
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
@gl_background.exec_gl
end
@player.draw
@stars.each { |star| star.draw }
@font.draw("Score: #{@player.score}", 10, 10, ZOrder::UI, 1.0, 1.0, 0xffffff00)
end
def button_down(id)
if id == Gosu::KbEscape
close
end
end
end
window = GameWindow.new
window.show
#define KERNEL_SIZE 9
uniform int pixelSize;
uniform sampler2D tex;
uniform int width;
uniform int height;
vec2 texCoords[KERNEL_SIZE];
void main(void)
{
vec4 avgColor;
vec2 texCoordsStep = 1.0/(vec2(float(width),float(height))/float(pixelSize));
vec2 pixelBin = floor(gl_TexCoord[0].st/texCoordsStep);
vec2 inPixelStep = texCoordsStep/3.0;
vec2 inPixelHalfStep = inPixelStep/2.0;
texCoords[0] = vec2(inPixelHalfStep.x, inPixelStep.y*2.0 + inPixelHalfStep.y) + pixelBin * texCoordsStep;
texCoords[1] = vec2(inPixelStep.x + inPixelHalfStep.x, inPixelStep.y*2.0 + inPixelHalfStep.y) + pixelBin * texCoordsStep;
texCoords[2] = vec2(inPixelStep.x*2.0 + inPixelHalfStep.x, inPixelStep.y*2.0 + inPixelHalfStep.y) + pixelBin * texCoordsStep;
texCoords[3] = vec2(inPixelHalfStep.x, inPixelStep.y + inPixelHalfStep.y) + pixelBin * texCoordsStep;
texCoords[4] = vec2(inPixelStep.x + inPixelHalfStep.x, inPixelStep.y + inPixelHalfStep.y) + pixelBin * texCoordsStep;
texCoords[5] = vec2(inPixelStep.x*2.0 + inPixelHalfStep.x, inPixelStep.y + inPixelHalfStep.y) + pixelBin * texCoordsStep;
texCoords[6] = vec2(inPixelHalfStep.x, inPixelHalfStep.y) + pixelBin * texCoordsStep;
texCoords[7] = vec2(inPixelStep.x + inPixelHalfStep.x, inPixelHalfStep.y) + pixelBin * texCoordsStep;
texCoords[8] = vec2(inPixelStep.x*2.0 + inPixelHalfStep.x, inPixelHalfStep.y) + pixelBin * texCoordsStep;
avgColor = texture2D(tex, texCoords[0]) +
texture2D(tex, texCoords[1]) +
texture2D(tex, texCoords[2]) +
texture2D(tex, texCoords[3]) +
texture2D(tex, texCoords[4]) +
texture2D(tex, texCoords[5]) +
texture2D(tex, texCoords[6]) +
texture2D(tex, texCoords[7]) +
texture2D(tex, texCoords[8]);
avgColor /= float(KERNEL_SIZE);
gl_FragColor = avgColor;
}
uniform sampler2D Texture;
uniform float BlurFactor;
uniform float BrightFactor;
uniform float origin_x;
uniform float origin_y;
uniform int passes;
void main(void)
{
vec2 Origin = vec2(origin_x, 1.0 - origin_y);
vec2 TexCoord = vec2(gl_TexCoord[0].xy);
vec4 SumColor = vec4(0.0, 0.0, 0.0, 0.0);
TexCoord += vec2(1.0 / 1024.0, 1.0 / 768.0) * 0.5 - Origin;
for (int i = 0; i < passes; i++)
{
float scale = 1.0 - BlurFactor * (float(i) / float(passes - 1));
SumColor += texture2D(Texture, TexCoord * scale + Origin);
}
gl_FragColor = SumColor / float(passes) * BrightFactor;
//gl_FragColor = texture2D(Texture, gl_TexCoord[0].xy + vec2(0.1));
}
uniform sampler2D tex;
void main(void)
{
vec4 Sepia1 = vec4( 0.2, 0.05, 0.0, 1.0 );
vec4 Sepia2 = vec4( 1.0, 0.9, 0.5, 1.0 );
vec4 Color = texture2D(tex, vec2(gl_TexCoord[0]));
float SepiaMix = dot(vec3(0.3, 0.59, 0.11), vec3(Color));
Color = mix(Color, vec4(SepiaMix), vec4(0.5));
vec4 Sepia = mix(Sepia1, Sepia2, SepiaMix);
gl_FragColor = mix(Color, Sepia, 1.0);
}
begin
require 'rubygems'
rescue
end
require 'gl'
class Shader
include Gl
attr_reader :window, :shader_filename
attr_reader :program_id, :vertex_shader_id, :fragment_shader_id
@@canvas_texture_id = nil
def initialize(window, shader_filename)
@window = window
@shader_filename = shader_filename
@program_id = nil
@vertex_shader_id = nil
@fragment_shader_id = nil
create_canvas unless @@canvas_texture_id
compile
end
def apply
@window.gl do
# copy frame buffer to canvas texture
glBindTexture(GL_TEXTURE_2D, @@canvas_texture_id)
glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 0, 0, @window.width, @window.height, 0)
# apply shader
glUseProgram(@program_id)
# clear screen
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
glColor4f(1.0, 1.0, 1.0, 1.0)
glMatrixMode(GL_PROJECTION)
glPushMatrix
glLoadIdentity
glViewport(0, 0, @window.width, @window.height)
glOrtho(0, @window.width, @window.height, 0, -1, 1)
# draw processed canvas texture over the screen
glBindTexture(GL_TEXTURE_2D, @@canvas_texture_id)
glBegin(GL_QUADS)
glTexCoord2f(0.0, 1.0); glVertex2f(0.0, 0.0)
glTexCoord2f(1.0, 1.0); glVertex2f(@window.width, 0.0)
glTexCoord2f(1.0, 0.0); glVertex2f(@window.width, @window.height)
glTexCoord2f(0.0, 0.0); glVertex2f(0.0, @window.height)
glEnd
# done, disable shader
glUseProgram(0)
# and out
glPopMatrix
end
end
def uniform(name, value)
glUseProgram(@program_id)
if value.is_a?(Float)
glUniform1f(glGetUniformLocation(@program_id, name), value)
elsif value.is_a?(Integer)
glUniform1i(glGetUniformLocation(@program_id, name), value)
else
raise ArgumentError, "Uniform data type not supported"
end
glUseProgram(0)
end
alias []= uniform
private
def create_canvas
@@canvas_texture_id = glGenTextures(1).first
glBindTexture(GL_TEXTURE_2D, @@canvas_texture_id)
glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, 1)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB8, @window.width, @window.height, 0, GL_RGB, GL_UNSIGNED_BYTE, "\0" * @window.width * @window.height * 3)
return @@canvas_texture_id
end
def compile
# create program
@program_id = glCreateProgram
# create vertex shader
@vertex_shader_id = glCreateShader(GL_VERTEX_SHADER)
glShaderSource(@vertex_shader_id, "void main(void)\r\n{\r\ngl_Position = ftransform();\r\ngl_TexCoord[0] = gl_MultiTexCoord0;\r\n}\r\n")
glCompileShader(@vertex_shader_id)
glAttachShader(@program_id, @vertex_shader_id)
# create fragment shader
@fragment_shader_id = glCreateShader(GL_FRAGMENT_SHADER)
glShaderSource(@fragment_shader_id, File.read(@shader_filename))
glCompileShader(@fragment_shader_id)
glAttachShader(@program_id, @fragment_shader_id)
# compile program
glLinkProgram(@program_id)
# check for compile errors
unless glGetProgramiv(@program_id, GL_LINK_STATUS) == GL_TRUE
raise glGetProgramInfoLog(@program_id).chomp
end
return @program_id
end
end
class Shader
INVALID_LOCATION = -1
BOOL_TRUE, BOOL_FALSE = 1, 0
attr_reader :image
class << self
# Canvas used to copy out the screen before post-processing it back onto the screen.
def canvas_texture
@canvas_texture ||= begin
texture = glGenTextures(1).first
glBindTexture GL_TEXTURE_2D, texture
glTexParameteri GL_TEXTURE_2D, GL_GENERATE_MIPMAP, 1
glTexParameteri GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE
glTexParameteri GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE
glTexImage2D GL_TEXTURE_2D, 0, GL_RGB8, $window.width, $window.height, 0,
GL_RGB, GL_UNSIGNED_BYTE, nil
texture
end
end
end
def initialize(options = {})
@uniform_locations = {}
@attribute_locations = {}
@image = nil
@vertex_source = options[:vertex] || DEFAULT_VERTEX_SOURCE
@fragment_source = options[:fragment] || options[:frag] || DEFAULT_FRAGMENT_SOURCE
@vertex = compile GL_VERTEX_SHADER, @vertex_source
@fragment = compile GL_FRAGMENT_SHADER, @fragment_source
link
self.color = [1, 1, 1, 1]
end
# Make this the current shader program.
def use
previous_program = glGetIntegerv GL_CURRENT_PROGRAM
glUseProgram @program
if block_given?
result = yield self
glUseProgram previous_program
end
result
end
# Disable the shader program (not needed in block version of #use).
def disable
glUseProgram 0 # Disable the shader!
end
# Is this the current shader program?
def current?
glGetIntegerv(GL_CURRENT_PROGRAM) == @program
end
def image=(image)
use do
if image
info = image.gl_tex_info
# Bind the single texture to 'in_Texture'
glActiveTexture GL_TEXTURE0
glBindTexture GL_TEXTURE_2D, info.tex_name
glUniform1i uniform("in_Texture"), 0
raise unless glGetIntegerv(GL_ACTIVE_TEXTURE) == GL_TEXTURE0
# Ensure that the shader knows to use the texture.
glUniform1i uniform("in_TextureEnabled"), BOOL_TRUE
glUniform2f uniform("in_SpriteOffset"), info.left, info.top
glUniform2f uniform("in_SpriteSize"), info.right - info.left, info.bottom - info.top
else
glUniform1i uniform("in_TextureEnabled"), BOOL_FALSE
end
end
@image = image
end
def color=(color)
#use do
glVertexAttrib4f attribute("in_Color"), *color
#end
@color = color
end
# Set the value of a uniform.
def []=(name, value)
use do
case value
when Float
glUniform1f uniform(name), value
when Integer
glUniform1i uniform(name), value
when Array
size = value.size
raise ArgumentError, "Empty array not supported for uniform data" if size.zero?
raise ArgumentError, "Only support uniforms up to 4 elements" if size > 4
case value[0]
when Float
GL.send "glUniform#{size}f", uniform(name), value
when Integer
GL.send "glUniform#{size}i", uniform(name), value
else
raise ArgumentError, "Uniform data type not supported for element of type: #{value[0].class}"
end
else
raise ArgumentError, "Uniform data type not supported for type: #{value.class}"
end
end
end
# Full screen post-processing using the shader.
def post_process
$window.gl do
width, height = $window.width, $window.height
canvas = Shader.canvas_texture
# copy frame buffer to canvas texture
glBindTexture GL_TEXTURE_2D, canvas
glCopyTexImage2D GL_TEXTURE_2D, 0, GL_RGBA8, 0, 0, width, height, 0
use do
# clear screen
glClear GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT
glColor4f 1.0, 1.0, 1.0, 1.0
glMatrixMode GL_PROJECTION
glLoadIdentity
glViewport 0, 0, width, height
glOrtho 0, width, height, 0, -1, 1
# Assign the canvas, which was a copy of the screen.
glActiveTexture GL_TEXTURE0
glBindTexture GL_TEXTURE_2D, canvas
glUniform1i uniform("in_Texture"), 0
raise unless glGetIntegerv(GL_ACTIVE_TEXTURE) == GL_TEXTURE0
glUniform1i uniform("in_TextureEnabled"), BOOL_TRUE
glUniform2f uniform("in_SpriteOffset"), 0, 0
glUniform2f uniform("in_SpriteSize"), width, height
# draw processed canvas texture over the screen
glBindTexture GL_TEXTURE_2D, canvas
glBegin GL_QUADS do
glTexCoord2f(0.0, 1.0); glVertex2f(0.0, 0.0)
glTexCoord2f(1.0, 1.0); glVertex2f(width, 0.0)
glTexCoord2f(1.0, 0.0); glVertex2f(width, height)
glTexCoord2f(0.0, 0.0); glVertex2f(0.0, height)
end
end
end
end
protected
def uniform(name)
location = @uniform_locations[name]
if location
location
else
location = glGetUniformLocation @program, name.to_s
raise "No #{name} uniform specified in program" if location == INVALID_LOCATION
@uniform_locations[name] = location
end
end
protected
def attribute(name)
location = @attribute_locations[name]
if location
location
else
location = glGetAttribLocation @program, name.to_s
raise "No #{name} attribute specified in program" if location == INVALID_LOCATION
@attribute_locations[name] = location
end
end
protected
def compile(type, source)
shader = glCreateShader type
glShaderSource shader, source
glCompileShader shader
unless glGetShaderiv shader, GL_COMPILE_STATUS
raise glGetShaderInfoLog(shader)
end
shader
end
protected
def link
@program = glCreateProgram
glAttachShader @program, @vertex
glAttachShader @program, @fragment
glLinkProgram @program
unless glGetProgramiv @program, GL_LINK_STATUS
raise glGetProgramInfoLog(@program)
end
nil
end
DEFAULT_VERTEX_SOURCE =<<END
#version 110
uniform mat4 in_ModelView;
uniform mat4 in_ProjectionView;
attribute vec4 in_Vertex;
attribute vec2 in_TexCoord;
attribute vec4 in_Color;
uniform vec2 in_SpriteOffset;
uniform vec2 in_SpriteSize;
varying vec4 var_Color;
varying vec2 var_TexCoord;
void main()
{
//gl_Position = in_Vertex * gl_ProjectionMatrix;// * in_ModelView * in_ProjectionView;
//gl_Position = vec4(in_Vertex, 0.0, 1.0)* (in_ModelView * in_Projection);
//gl_Position = in_Vertex;
gl_Position = ftransform();
var_Color = in_Color;
var_TexCoord = in_SpriteOffset + (in_TexCoord * in_SpriteSize);
}
END
DEFAULT_FRAGMENT_SOURCE =<<END
#version 110
uniform sampler2D in_Texture;
uniform bool in_TextureEnabled;
varying vec4 var_Color;
varying vec2 var_TexCoord;
void main()
{ if(in_TextureEnabled)
{
gl_FragColor = texture2D(in_Texture, var_TexCoord) * var_Color;
}
else
{
gl_FragColor = var_Color;
}
}
END
end
# Use of GLSL shaders in Gosu.
require 'rubygems'
require 'gosu'
require 'opengl' # This should ideally be the 'opengl' gem, not 'ruby-opengl'
require 'texplay'
require_relative "shader"
require_relative "framebuffer"
module ZOrder
Stars, Player, UI = *0..3
end
# The only really new class here.
# Draws a scrolling, repeating texture with a randomized height map.
class GLBackground
# Height map size
POINTS_X = 7
POINTS_Y = 7
# Scrolling speed
SCROLLS_PER_STEP = 50
def initialize(window)
@image = Gosu::Image.new(window, "media/Earth.png", true)
@framebuffer = Framebuffer.new $window.width, $window.height
@shader = Shader.new # Just use default shaders for now.
@shader.image = @image
end
def draw_with_shaders
width, height = $window.width, $window.height
# Doing it with shaders.
@shader.use do |s|
s.color = [1, 1, 1, 1] # color can be set inside or outside of glBegin.
s.image = @image # image can only be set outside of glBegin.
glBegin GL_QUADS do
# Drawing a quad in a single colour (TOP RIGHT).
glVertex2d width / 2, height / 2 # BL
glVertex2d width / 2, 0 # TL
glVertex2d width, 0 # TR
glVertex2d width, height / 2 # BR
# Quad with coloured corners (BOTTOM RIGHT).
s.color = [0, 1, 1, 1]
glVertex2d width / 2, height # BL
s.color = [1, 1, 0, 1]
glVertex2d width / 2, height / 2 # TL
s.color = [0, 0, 0, 0]
glVertex2d width, height / 2 # TR
s.color = [1, 0, 0, 1]
glVertex2d width, height# BR
end
end
end
def draw_without_shaders
width, height = $window.width, $window.height
# Drawing a textured quad without a shader (BOTTOM LEFT).
info = @image.gl_tex_info
glEnable(GL_TEXTURE_2D)
glBindTexture(GL_TEXTURE_2D, info.tex_name)
glBegin GL_QUADS do
glColor4d(0, 1, 1, 1)
glTexCoord2d(info.left, info.bottom)
glVertex2d 0, height # BL
glColor4d(1, 1, 0, 1)
glTexCoord2d(info.left, info.top)
glVertex2d 0, height / 2 # TL
glColor4d(1.0, 1.0, 1.0, 0.5)
glTexCoord2d(info.right, info.top)
glVertex2d width / 2, height / 2 # TR
glColor4d(1, 0, 0, 1)
glTexCoord2d(info.right, info.bottom)
glVertex2d width / 2, height # BR
glColor4d(1, 1, 1, 1) # Reset colour so it doesn't polute!
end
end
def draw
@framebuffer.use do |fb|
fb.clear
draw_with_shaders
draw_without_shaders
@shader.image.draw 0, 0, 0 # Just draw somethihng normally to hopefully get it into the fbo!
end
# Convert the OpenGL framebuffer into a Gosu image, save it then display it.
t = Time.now
image = @framebuffer.to_image
image.save "framebuffer.png" unless File.exists? "framebuffer.png"
p Time.now - t
image.draw 0, 0, 0
image = @framebuffer.to_image rect: [400, 400, 200, 200]
image.save "framebuffer_section.png" unless File.exists? "framebuffer_section.png"
#@framebuffer.draw 0, 0 # Is upside-down!
end
end
class GameWindow < Gosu::Window
def initialize
super 800, 600, false
self.caption = "Gosu & OpenGL Integration Demo (SHADERS)"
$window = self
@gl_background = GLBackground.new self
@font = Gosu::Font.new(self, Gosu::default_font_name, 20)
@star = Gosu::Image.new(self, "media/LargeStar.png", true)
@image = Gosu::Image.new(self, "media/Earth.png", true)
end
def draw
glClearColor 0.0, 0.2, 0.5, 1.0
glClearDepth 0
glClear GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT
@gl_background.draw
@image.draw 0, 100, Float::INFINITY
@font.draw Gosu::fps.to_s, 0, 50, Float::INFINITY
@font.draw "Gosu::Image#draw", 0, 0, Float::INFINITY
@font.draw_rel "flat shader", width, 0, Float::INFINITY, 1, 0
@font.draw_rel "coloured shader", width, height, Float::INFINITY, 1, 1
@font.draw_rel "standard quad", 0, height, Float::INFINITY, 0, 1
end
def button_down(id)
if id == Gosu::KbEscape
close
end
end
end
window = GameWindow.new
window.show
uniform sampler2D tex;
uniform float x, y, min, max, ratio, refraction;
void main(void)
{
vec2 source_coords = gl_TexCoord[0].xy;
vec3 color = texture2D(tex, source_coords).rgb;
vec2 rel = source_coords - vec2(x,1.0-y);
rel.x *= ratio;
float dist = sqrt(rel.x*rel.x + rel.y*rel.y);
float inner = (dist - min)/(max - min);
if(dist >= min && dist <= max)
{
float depth = 0.5 + 0.5*cos((inner + 0.5) * 2.0 * 3.14159);
source_coords -= depth * rel/dist * refraction;
color = texture2D(tex, source_coords).rgb;
}
gl_FragColor.rgb = color;
}
uniform sampler2D texUnit0;
uniform float column_width;
void main(void)
{
vec3 color = texture2D(texUnit0, gl_TexCoord[0].xy).rgb;
float column_index = gl_TexCoord[0].x / column_width;
int ci = int(column_index);
while(ci >= 3) { ci -= 3; } // % doesn't seem to work
gl_FragColor.rgb = color * 0.125;
if(ci==0) { gl_FragColor.r = color.r; }
if(ci==1) { gl_FragColor.g = color.g; }
if(ci==2) { gl_FragColor.b = color.b; }
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment