Created
July 6, 2012 13:44
-
-
Save bil-bas/3060237 to your computer and use it in GitHub Desktop.
Shader/Framebuffer tests
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/framebuffer.png | |
/framebuffer_section.png |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
uniform sampler2D texUnit0; | |
uniform float fade; | |
void main(void) { | |
vec4 color; | |
color = texture2D(texUnit0, gl_TexCoord[0].xy); | |
gl_FragColor = color * fade; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# 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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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)); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# 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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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