LoginSignup
37
31

More than 5 years have passed since last update.

pyOpenGLでシェーダープログラミング

Posted at

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

すると以下の様なウィンドウが表示される.
pyopengl.png

シェーダプログラミング用のひな形をつくる

コマンドラインから頂点シェーダプログラム,フラグメントシェーダプログラム,テクスチャを指定するひな形をつくることにする.基本的に上で使った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

結果はこんな感じ
pygl_result.jpg

37
31
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
37
31