##pyOpenGLを導入する
今回はOpenGLでなんか作ることよりも,GLSLにさわってみることが目的なので,
python + pyOpenGLを使うこととした.(テクスチャのロードとかこっちの方が楽だしね!!)
まず,pyOpenGLとPyOpenGL-Demoを導入する.
% sudo pip install PyOpenGL PyOpenGL-Demo
次にサンプルを実行し,適切に導入されていることを確認する.
% cd /Library/Python/2.7/site-package/PyOpenGL-Demo/GLUT/
% python shader-test.py
##シェーダプログラミング用のひな形をつくる
コマンドラインから頂点シェーダプログラム,フラグメントシェーダプログラム,テクスチャを指定するひな形をつくることにする.基本的に上で使ったshader-test.pyをパクることにした.実際のコードは以下の通り.
shader-test.py
#! /usr/bin/env python
import numpy as np
import sys
import time
import Image
import OpenGL
OpenGL.ERROR_ON_COPY = True
from OpenGL.GL import *
from OpenGL.GLU import *
from OpenGL.GLUT import *
# PyOpenGL 3.0.1 introduces this convenience module...
from OpenGL.GL.shaders import *
vertices = None
indices = None
def InitGL( vertex_shade_code, fragment_shader_code, texture_image ):
glClearColor(0.0, 0.0, 0.0, 0.0)
texture_id = glGenTextures( 1 )
glPixelStorei( GL_UNPACK_ALIGNMENT, 1 )
glActiveTexture( GL_TEXTURE0 )
glBindTexture( GL_TEXTURE_2D, texture_id )
if texture_image.mode == 'RGB':
glTexImage2D( GL_TEXTURE_2D,
0,
4,
texture_image.size[0],
texture_image.size[1],
0,
GL_RGB,
GL_UNSIGNED_BYTE,
texture_image.tostring() )
else:
glTexImage2D( GL_TEXTURE_2D,
0,
4,
texture_image.size[0],
texture_image.size[1],
0,
GL_RGBA,
GL_UNSIGNED_BYTE,
texture_image.tostring() )
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR )
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR )
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE )
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE )
program = compileProgram(
compileShader( vertex_shade_code,GL_VERTEX_SHADER),
compileShader( fragment_shader_code,GL_FRAGMENT_SHADER),)
glUseProgram(program)
glUniform1i( glGetUniformLocation( program, "s_texture" ), 0 );
glUniform1f( glGetUniformLocation( program, "texture_width" ), float( texture_image.size[ 0 ] ) )
glUniform1f( glGetUniformLocation( program, "texture_height" ), float( texture_image.size[ 1 ] ) )
global vertices
global indices
position_vertices = [ -1.0, 1.0, 0.0,
-1.0, -1.0, 0.0,
1.0, -1.0, 0.0,
1.0, 1.0, 0.0, ]
texture_vertices = [ 0.0, 0.0,
0.0, texture_image.size[ 1 ],
texture_image.size[ 0 ], texture_image.size[ 1 ],
texture_image.size[ 0 ], 0.0 ]
indices = [ 0, 1, 2, 0, 2, 3 ]
position_loc = glGetAttribLocation( program, 'a_position' )
glVertexAttribPointer( position_loc,
3,
GL_FLOAT,
GL_FALSE,
3 * 4,
np.array( position_vertices, np.float32 ) )
tex_loc = glGetAttribLocation( program, 'a_texCoord' )
glVertexAttribPointer( tex_loc,
2,
GL_FLOAT,
GL_FALSE,
2 * 4,
np.array( texture_vertices, np.float32 ) )
glEnableVertexAttribArray( position_loc )
glEnableVertexAttribArray( tex_loc )
def ReSizeGLScene(Width, Height):
glViewport(0, 0, Width, Height)
# The main drawing function.
def DrawGLScene():
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
glEnable( GL_TEXTURE_2D )
glDrawElements( GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, np.array( indices, np.uint16 ) )
glDisable(GL_TEXTURE_2D)
glutSwapBuffers()
def keyPressed(*args):
# If escape is pressed, kill everything.
if args[0] == '\x1b':
sys.exit()
def usage():
print "usage:%s vertex_shader_file fragment_shader_file texture_file" % sys.argv[ 0 ]
def main():
try:
vertex_shader_file = sys.argv[ 1 ]
fragment_shader_file = sys.argv[ 2 ]
texture_file = sys.argv[ 3 ]
except IndexError:
usage()
sys.exit( -1 )
vertex_shade_code = '\n'.join( open( vertex_shader_file, 'r' ).readlines() )
fragment_shader_code = '\n'.join( open( fragment_shader_file, 'r' ).readlines() )
texture_image = Image.open( texture_file )
print texture_image.mode
assert texture_image.mode == 'RGBA' or texture_image.mode == 'RGB'
glutInit(sys.argv)
if texture_image.mode == 'RGBA':
glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH)
else:
glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH)
window_width,window_height = texture_image.size
glutInitWindowSize( window_width, window_height )
# the window starts at the upper left corner of the screen
glutInitWindowPosition(0, 0)
glutCreateWindow( sys.argv[ 0 ] )
glutDisplayFunc(DrawGLScene)
# Uncomment this line to get full screen.
#glutFullScreen()
# When we are doing nothing, redraw the scene.
glutIdleFunc(DrawGLScene)
# Register the function called when our window is resized.
glutReshapeFunc(ReSizeGLScene)
# Register the function called when the keyboard is pressed.
glutKeyboardFunc(keyPressed)
# Initialize our window.
InitGL( vertex_shade_code, fragment_shader_code, texture_image )
# Start Event Processing Engine
glutMainLoop()
if __name__ == "__main__":
print "Hit ESC key to quit."
main()
んで,下の頂点シェーダプログラムと
shader.vert
attribute vec4 a_position;
attribute vec2 a_texCoord;
varying vec2 v_texCoord;
uniform float texture_width;
uniform float texture_height;
void main() {
v_texCoord = a_texCoord;
gl_Position = a_position;
}
下のフラグメントシェーダプログラムを実行するには
shader.frag
varying vec2 v_texCoord;
uniform sampler2D s_texture;
uniform float texture_width;
uniform float texture_height;
void main() {
float t_x = v_texCoord.x / texture_width;
float t_y = v_texCoord.y / texture_height;
float d_x = 1.0 / texture_width;
float d_y = 1.0 / texture_height;
vec4 v1 = texture2D( s_texture, vec2( t_x, t_y ) + vec2( d_x, 0.0 ) );
vec4 v2 = texture2D( s_texture, vec2( t_x, t_y ) - vec2( d_x, 0.0 ) );
vec4 v3 = texture2D( s_texture, vec2( t_x, t_y ) + vec2( 0.0, d_y ) );
vec4 v4 = texture2D( s_texture, vec2( t_x, t_y ) - vec2( 0.0, d_y ) );
gl_FragColor = ( abs( v1 - v2 ) + abs( v3 - v4 ) ) / 2.0;
}
こんな感じ
% python shader-test.py shader.vert shader.frag texture.png