require 'opengl' require 'glut' module GL def StackMatrix PushMatrix() yield PopMatrix() end def StackAttrib(mask) PushAttrib(mask) yield PopAttrib() end end class Spheres include GL LIGHT_COLORS = [[ 0.8, 0.9, 1.0, 1.0 ], [ 1.0, 0.5, 0.0, 1.0 ], [ 1.0, 0.9, 0.6, 1.0 ], [ 0.4, 0.6, 1.0, 1.0 ]] BLACK = [ 0.0, 0.0, 0.0, 1.0 ] def init_window GLUT.Init GLUT.InitDisplayMode(GLUT::DOUBLE | GLUT::RGB | GLUT::DEPTH) GLUT.InitWindowSize(500, 500) GLUT.InitWindowPosition(100,100) GLUT.CreateWindow end def set_funcs GLUT.DisplayFunc(method(:display).to_proc) GLUT.ReshapeFunc(method(:reshape).to_proc) GLUT.IdleFunc(method(:display).to_proc) end def init LIGHT_COLORS.each_with_index{|color, i| init_light(lights[i], color) } ClearColor(0.0, 0.0, 0.0, 0.0) ShadeModel(SMOOTH) Enable(LIGHTING) Enable(DEPTH_TEST) Enable(BLEND) BlendFunc(SRC_ALPHA, ONE_MINUS_SRC_ALPHA) end def init_light(light, color) light_position = [ 1.0, 1.0, 1.0, 0.0 ] Lightfv(light, POSITION, light_position) Lightfv(light, DIFFUSE, color) Lightfv(light, SPECULAR, color) Lightf(light, CONSTANT_ATTENUATION, 0.8) Lightf(light, LINEAR_ATTENUATION, 0.1) Lightf(light, QUADRATIC_ATTENUATION, 0.1) Enable(light) end def display Clear(COLOR_BUFFER_BIT | DEPTH_BUFFER_BIT) MatrixMode(MODELVIEW) GL.LoadIdentity set_camera draw_spheres draw_lights GL.Flush GLUT.SwapBuffers end def set_camera time = Time.now.to_f puts time GLU.LookAt( 3.0*(Math.sin(0.5*time)), 2.0, 3.0*(Math.cos(0.5*time)), 0.0, 0.0, 0.0, 0.0, 1.0, 0.0) end def draw_spheres StackAttrib(LIGHTING_BIT) do mat_diffuse = [ 1.0, 1.0, 1.0, 1.0] mat_specular = [ 1.0, 1.0, 1.0, 1.0 ] mat_shininess = [ 150.0 ] Material(FRONT, AMBIENT, BLACK) Material(FRONT, DIFFUSE, mat_diffuse) Material(FRONT, SPECULAR, mat_specular) Material(FRONT, SHININESS, mat_shininess) big_sphere_detail = 180 small_sphere_detail = 80 StackMatrix{ GLUT.SolidSphere(1.0, big_sphere_detail, big_sphere_detail) Translate(1.2, 1.2, 0.0) GLUT.SolidSphere(0.25, small_sphere_detail, small_sphere_detail) Translate(-2.4, 0.0, 0.0) GLUT.SolidSphere(0.25, small_sphere_detail, small_sphere_detail) Translate(0.0, -2.4, 0.0) GLUT.SolidSphere(0.25, small_sphere_detail, small_sphere_detail) Translate(2.4, 0.0, 0.0) GLUT.SolidSphere(0.25, small_sphere_detail, small_sphere_detail) } end end def draw_lights time = Time.now.to_f light_positions = [] light_positions[0] = [ -1.5*Math.sin(2*time), 1.2*Math.sin(time), 1.5*Math.cos(2*time), 1.0 ] light_positions[1] = [ -light_positions[0][0], -light_positions[0][1], -light_positions[0][2], light_positions[0][3] ] light_positions[2] = [ 1.5*Math.cos(time), -2.0*Math.sin(3*time), 1.5*Math.sin(2*time), 1.0 ] light_positions[3] = [ -light_positions[2][0], -light_positions[2][1], -light_positions[2][2], light_positions[2][3] ] (0..3).each{|i| update_light(i, lights[i], light_positions[i]) } end def lights @lights ||= (0..7).map{|i| GL.const_get("LIGHT#{i}")} end def update_light(index, light, position) StackMatrix{ Translate(position[0], position[1], position[2]) StackAttrib(LIGHTING_BIT){ Material(FRONT, SPECULAR, BLACK) Material(FRONT, SHININESS, BLACK) lc = LIGHT_COLORS[index] Material(FRONT, EMISSION, lc) GLUT.SolidSphere(0.1, 30, 30) } } Lightfv(light, POSITION, position) end def reshape(w, h) Viewport(0,0, w, h) MatrixMode(PROJECTION) GL.LoadIdentity GLU.Perspective(60.0, w.to_f/h, 0.5, 200.0) end def run init_window init set_funcs GLUT.MainLoop end end if __FILE__ == $0 Spheres.new.run end