psychopy.tools.gltools

OpenGL related helper functions.

Shaders

Tools for creating, compiling, using, and inspecting shader programs.

psychopy.tools.gltools.createProgram()[source]

Create an empty program object for shaders.

Returns:

OpenGL program object handle retrieved from a glCreateProgram call.

Return type:

int

Examples

Building a program with vertex and fragment shader attachments:

myProgram = createProgram()  # new shader object

# compile vertex and fragment shader sources
vertexShader = compileShader(vertShaderSource, GL.GL_VERTEX_SHADER)
fragmentShader = compileShader(fragShaderSource, GL.GL_FRAGMENT_SHADER)

# attach shaders to program
attachShader(myProgram, vertexShader)
attachShader(myProgram, fragmentShader)

# link the shader, makes `myProgram` attachments executable by their
# respective processors and available for use
linkProgram(myProgram)

# optional, validate the program
validateProgram(myProgram)

# optional, detach and discard shader objects
detachShader(myProgram, vertexShader)
detachShader(myProgram, fragmentShader)

deleteObject(vertexShader)
deleteObject(fragmentShader)

You can install the program for use in the current rendering state by calling:

useProgram(myShader) # OR glUseProgram(myShader)
# set uniforms/attributes and start drawing here ...
psychopy.tools.gltools.createProgramObjectARB()[source]

Create an empty program object for shaders.

This creates an Architecture Review Board (ARB) program variant which is compatible with older GLSL versions and OpenGL coding practices (eg. immediate mode) on some platforms. Use *ARB variants of shader helper functions (eg. compileShaderObjectARB instead of compileShader) when working with these ARB program objects. This was included for legacy support of existing PsychoPy shaders. However, it is recommended that you use createShader() and follow more recent OpenGL design patterns for new code (if possible of course).

Returns:

OpenGL program object handle retrieved from a glCreateProgramObjectARB call.

Return type:

int

Examples

Building a program with vertex and fragment shader attachments:

myProgram = createProgramObjectARB()  # new shader object

# compile vertex and fragment shader sources
vertexShader = compileShaderObjectARB(
    vertShaderSource, GL.GL_VERTEX_SHADER_ARB)
fragmentShader = compileShaderObjectARB(
    fragShaderSource, GL.GL_FRAGMENT_SHADER_ARB)

# attach shaders to program
attachObjectARB(myProgram, vertexShader)
attachObjectARB(myProgram, fragmentShader)

# link the shader, makes `myProgram` attachments executable by their
# respective processors and available for use
linkProgramObjectARB(myProgram)

# optional, validate the program
validateProgramARB(myProgram)

# optional, detach and discard shader objects
detachObjectARB(myProgram, vertexShader)
detachObjectARB(myProgram, fragmentShader)

deleteObjectARB(vertexShader)
deleteObjectARB(fragmentShader)

Use the program in the current OpenGL state:

useProgramObjectARB(myProgram)
psychopy.tools.gltools.compileShader(shaderSrc, shaderType)[source]

Compile shader GLSL code and return a shader object. Shader objects can then be attached to programs an made executable on their respective processors.

Parameters:
  • shaderSrc (str, list of str) – GLSL shader source code.

  • shaderType (GLenum) – Shader program type (eg. GL_VERTEX_SHADER, GL_FRAGMENT_SHADER, GL_GEOMETRY_SHADER, etc.)

Returns:

OpenGL shader object handle retrieved from a glCreateShader call.

Return type:

int

Examples

Compiling GLSL source code and attaching it to a program object:

# GLSL vertex shader source
vertexSource =             '''
    #version 330 core
    layout (location = 0) in vec3 vertexPos;

    void main()
    {
        gl_Position = vec4(vertexPos, 1.0);
    }
    '''
# compile it, specifying `GL_VERTEX_SHADER`
vertexShader = compileShader(vertexSource, GL.GL_VERTEX_SHADER)
attachShader(myProgram, vertexShader)  # attach it to `myProgram`
psychopy.tools.gltools.compileShaderObjectARB(shaderSrc, shaderType)[source]

Compile shader GLSL code and return a shader object. Shader objects can then be attached to programs an made executable on their respective processors.

Parameters:
  • shaderSrc (str, list of str) – GLSL shader source code text.

  • shaderType (GLenum) – Shader program type. Must be *_ARB enums such as GL_VERTEX_SHADER_ARB, GL_FRAGMENT_SHADER_ARB, GL_GEOMETRY_SHADER_ARB, etc.

Returns:

OpenGL shader object handle retrieved from a glCreateShaderObjectARB call.

Return type:

int

psychopy.tools.gltools.embedShaderSourceDefs(shaderSrc, defs)[source]

Embed preprocessor definitions into GLSL source code.

This function generates and inserts #define statements into existing GLSL source code, allowing one to use GLSL preprocessor statements to alter program source at compile time.

Passing {'MAX_LIGHTS': 8, 'NORMAL_MAP': False} to defs will create and insert the following #define statements into shaderSrc:

#define MAX_LIGHTS 8
#define NORMAL_MAP 0

As per the GLSL specification, the #version directive must be specified at the top of the file before any other statement (with the exception of comments). If a #version directive is present, generated #define statements will be inserted starting at the following line. If no #version directive is found in shaderSrc, the statements will be prepended to shaderSrc.

Using preprocessor directives, multiple shader program routines can reside in the same source text if enclosed by #ifdef and #endif statements as shown here:

#ifdef VERTEX
    // vertex shader code here ...
#endif

#ifdef FRAGMENT
    // pixel shader code here ...
#endif

Both the vertex and fragment shader can be built from the same GLSL code listing by setting either VERTEX or FRAGMENT as True:

vertexShader = gltools.compileShaderObjectARB(
    gltools.embedShaderSourceDefs(glslSource, {'VERTEX': True}),
    GL.GL_VERTEX_SHADER_ARB)
fragmentShader = gltools.compileShaderObjectARB(
    gltools.embedShaderSourceDefs(glslSource, {'FRAGMENT': True}),
    GL.GL_FRAGMENT_SHADER_ARB)

In addition, #ifdef blocks can be used to prune render code paths. Here, this GLSL snippet shows a shader having diffuse color sampled from a texture is conditional on DIFFUSE_TEXTURE being True, if not, the material color is used instead:

#ifdef DIFFUSE_TEXTURE
    uniform sampler2D diffuseTexture;
#endif
...
#ifdef DIFFUSE_TEXTURE
    // sample color from texture
    vec4 diffuseColor = texture2D(diffuseTexture, gl_TexCoord[0].st);
#else
    // code path for no textures, just output material color
    vec4 diffuseColor = gl_FrontMaterial.diffuse;
#endif

This avoids needing to provide two separate GLSL program sources to build shaders to handle cases where a diffuse texture is or isn’t used.

Parameters:
  • shaderSrc (str) – GLSL shader source code.

  • defs (dict) – Names and values to generate #define statements. Keys must all be valid GLSL preprocessor variable names of type str. Values can only be int, float, str, bytes, or bool types. Boolean values True and False are converted to integers 1 and 0, respectively.

Returns:

GLSL source code with #define statements inserted.

Return type:

str

Examples

Defining MAX_LIGHTS as 8 in a fragment shader program at runtime:

fragSrc = embedShaderSourceDefs(fragSrc, {'MAX_LIGHTS': 8})
fragShader = compileShaderObjectARB(fragSrc, GL_FRAGMENT_SHADER_ARB)
psychopy.tools.gltools.deleteObject(obj)[source]

Delete a shader or program object.

Parameters:

obj (int) – Shader or program object handle. Must have originated from a createProgram(), compileShader(), glCreateProgram or glCreateShader call.

psychopy.tools.gltools.deleteObjectARB(obj)[source]

Delete a program or shader object.

Parameters:

obj (int) – Program handle to attach shader to. Must have originated from a createProgramObjectARB(), compileShaderObjectARB, `glCreateProgramObjectARB() or glCreateShaderObjectARB call.

psychopy.tools.gltools.attachShader(program, shader)[source]

Attach a shader to a program.

Parameters:
  • program (int) – Program handle to attach shader to. Must have originated from a createProgram() or glCreateProgram call.

  • shader (int) – Handle of shader object to attach. Must have originated from a compileShader() or glCreateShader call.

psychopy.tools.gltools.attachObjectARB(program, shader)[source]

Attach a shader object to a program.

Parameters:
  • program (int) – Program handle to attach shader to. Must have originated from a createProgramObjectARB() or glCreateProgramObjectARB call.

  • shader (int) – Handle of shader object to attach. Must have originated from a compileShaderObjectARB() or glCreateShaderObjectARB call.

psychopy.tools.gltools.detachShader(program, shader)[source]

Detach a shader object from a program.

Parameters:
  • program (int) – Program handle to detach shader from. Must have originated from a createProgram() or glCreateProgram call.

  • shader (int) – Handle of shader object to detach. Must have been previously attached to program.

psychopy.tools.gltools.detachObjectARB(program, shader)[source]

Detach a shader object from a program.

Parameters:
  • program (int) – Program handle to detach shader from. Must have originated from a createProgramObjectARB() or glCreateProgramObjectARB call.

  • shader (int) – Handle of shader object to detach. Must have been previously attached to program.

psychopy.tools.gltools.linkProgram(program)[source]

Link a shader program. Any attached shader objects will be made executable to run on associated GPU processor units when the program is used.

Parameters:

program (int) – Program handle to link. Must have originated from a createProgram() or glCreateProgram call.

Raises:
  • ValueError – Specified program handle is invalid.

  • RuntimeError – Program failed to link. Log will be dumped to sterr.

psychopy.tools.gltools.linkProgramObjectARB(program)[source]

Link a shader program object. Any attached shader objects will be made executable to run on associated GPU processor units when the program is used.

Parameters:

program (int) – Program handle to link. Must have originated from a createProgramObjectARB() or glCreateProgramObjectARB call.

Raises:
  • ValueError – Specified program handle is invalid.

  • RuntimeError – Program failed to link. Log will be dumped to sterr.

psychopy.tools.gltools.validateProgram(program)[source]

Check if the program can execute given the current OpenGL state.

Parameters:

program (int) – Handle of program to validate. Must have originated from a createProgram() or glCreateProgram call.

psychopy.tools.gltools.validateProgramARB(program)[source]

Check if the program can execute given the current OpenGL state. If validation fails, information from the driver is dumped giving the reason.

Parameters:

program (int) – Handle of program object to validate. Must have originated from a createProgramObjectARB() or glCreateProgramObjectARB call.

psychopy.tools.gltools.useProgram(program)[source]

Use a program object’s executable shader attachments in the current OpenGL rendering state.

In order to install the program object in the current rendering state, a program must have been successfully linked by calling linkProgram() or glLinkProgram.

Parameters:

program (int) – Handle of program to use. Must have originated from a createProgram() or glCreateProgram call and was successfully linked. Passing 0 or None disables shader programs.

Examples

Install a program for use in the current rendering state:

useProgram(myShader)

Disable the current shader program by specifying 0:

useProgram(0)
psychopy.tools.gltools.useProgramObjectARB(program)[source]

Use a program object’s executable shader attachments in the current OpenGL rendering state.

In order to install the program object in the current rendering state, a program must have been successfully linked by calling linkProgramObjectARB() or glLinkProgramObjectARB.

Parameters:

program (int) – Handle of program object to use. Must have originated from a createProgramObjectARB() or glCreateProgramObjectARB call and was successfully linked. Passing 0 or None disables shader programs.

Examples

Install a program for use in the current rendering state:

useProgramObjectARB(myShader)

Disable the current shader program by specifying 0:

useProgramObjectARB(0)

Notes

Some drivers may support using glUseProgram for objects created by calling createProgramObjectARB() or glCreateProgramObjectARB.

psychopy.tools.gltools.getInfoLog(obj)[source]

Get the information log from a shader or program.

This retrieves a text log from the driver pertaining to the shader or program. For instance, a log can report shader compiler output or validation results. The verbosity and formatting of the logs are platform-dependent, where one driver may provide more information than another.

This function works with both standard and ARB program object variants.

Parameters:

obj (int) – Program or shader to retrieve a log from. If a shader, the handle must have originated from a compileShader(), glCreateShader, createProgramObjectARB() or glCreateProgramObjectARB call. If a program, the handle must have came from a createProgram(), createProgramObjectARB(), glCreateProgram or glCreateProgramObjectARB call.

Returns:

Information log data. Logs can be empty strings if the driver has no information available.

Return type:

str

psychopy.tools.gltools.getUniformLocations(program, builtins=False)[source]

Get uniform names and locations from a given shader program object.

This function works with both standard and ARB program object variants.

Parameters:
  • program (int) – Handle of program to retrieve uniforms. Must have originated from a createProgram(), createProgramObjectARB(), glCreateProgram or glCreateProgramObjectARB call.

  • builtins (bool, optional) – Include built-in GLSL uniforms (eg. gl_ModelViewProjectionMatrix). Default is False.

Returns:

Uniform names and locations.

Return type:

dict

psychopy.tools.gltools.getAttribLocations(program, builtins=False)[source]

Get attribute names and locations from the specified program object.

This function works with both standard and ARB program object variants.

Parameters:
  • program (int) – Handle of program to retrieve attributes. Must have originated from a createProgram(), createProgramObjectARB(), glCreateProgram or glCreateProgramObjectARB call.

  • builtins (bool, optional) – Include built-in GLSL attributes (eg. gl_Vertex). Default is False.

Returns:

Attribute names and locations.

Return type:

dict

Query

Tools for using OpenGL query objects.

psychopy.tools.gltools.createQueryObject(target=35007)[source]

Create a GL query object.

Parameters:

target (Glenum or int) – Target for the query.

Returns:

Query object.

Return type:

QueryObjectInfo

Examples

Get GPU time elapsed executing rendering/GL calls associated with some stimuli (this is not the difference in absolute time between consecutive beginQuery and endQuery calls!):

# create a new query object
qGPU = createQueryObject(GL_TIME_ELAPSED)

beginQuery(query)
myStim.draw()  # OpenGL calls here
endQuery(query)

# get time elapsed in seconds spent on the GPU
timeRendering = getQueryValue(qGPU) * 1e-9

You can also use queries to test if vertices are occluded, as their samples would be rejected during depth testing:

drawVAO(shape0, GL_TRIANGLES)  # draw the first object

# check if the object was completely occluded
qOcclusion = createQueryObject(GL_ANY_SAMPLES_PASSED)

# draw the next shape within query context
beginQuery(qOcclusion)
drawVAO(shape1, GL_TRIANGLES)  # draw the second object
endQuery(qOcclusion)

isOccluded = getQueryValue(qOcclusion) == 1

This can be leveraged to perform occlusion testing/culling, where you can render a cheap version of your mesh/shape, then the more expensive version if samples were passed.

psychopy.tools.gltools.QueryObjectInfo(name, target)[source]

Object for querying information. This includes GPU timing information.

psychopy.tools.gltools.beginQuery(query)[source]

Begin query.

Parameters:

query (QueryObjectInfo) – Query object descriptor returned by createQueryObject().

psychopy.tools.gltools.endQuery(query)[source]

End a query.

Parameters:

query (QueryObjectInfo) – Query object descriptor returned by createQueryObject(), previously passed to beginQuery().

psychopy.tools.gltools.getQuery(query)[source]

Get the value stored in a query object.

Parameters:

query (QueryObjectInfo) – Query object descriptor returned by createQueryObject(), previously passed to endQuery().

psychopy.tools.gltools.getAbsTimeGPU()[source]

Get the absolute GPU time in nanoseconds.

Returns:

Time elapsed in nanoseconds since the OpenGL context was fully realized.

Return type:

int

Examples

Get the current GPU time in seconds:

timeInSeconds = getAbsTimeGPU() * 1e-9

Get the GPU time elapsed:

t0 = getAbsTimeGPU()
# some drawing commands here ...
t1 = getAbsTimeGPU()
timeElapsed = (t1 - t0) * 1e-9  # take difference, convert to seconds

Framebuffer Objects (FBO)

Tools for creating Framebuffer Objects (FBOs).

psychopy.tools.gltools.createFBO(attachments=(), sizeHint=None)[source]

Create a Framebuffer Object.

Parameters:
  • attachments (list or tuple of tuple) – Optional attachments to initialize the Framebuffer with. Attachments are specified as a list of tuples. Each tuple must contain an attachment point (e.g. GL_COLOR_ATTACHMENT0, GL_DEPTH_ATTACHMENT, etc.) and a buffer descriptor type (RenderbufferInfo or TexImage2DInfo). If using a combined depth/stencil format such as GL_DEPTH24_STENCIL8, GL_DEPTH_ATTACHMENT and GL_STENCIL_ATTACHMENT must be passed the same buffer. Alternatively, one can use GL_DEPTH_STENCIL_ATTACHMENT instead. If using multisample buffers, all attachment images must use the same number of samples!. As an example, one may specify attachments as ‘attachments=((GL.GL_COLOR_ATTACHMENT0, frameTexture), (GL.GL_DEPTH_STENCIL_ATTACHMENT, depthRenderBuffer))’.

  • sizeHint (tuple, optional) – Size hint for the FBO. This is used to specify the dimensions of logical buffers attached to the FBO. The size hint is a tuple of two integers (width, height).

Returns:

Framebuffer descriptor.

Return type:

FramebufferInfo

Notes

  • All buffers must have the same number of samples.

  • The ‘userData’ field of the returned descriptor is a dictionary that can be used to store arbitrary data associated with the FBO.

  • Framebuffers need a single attachment to be complete.

Examples

Create an empty framebuffer with no attachments:

fbo = createFBO()  # invalid until attachments are added

Create a render target with multiple color texture attachments:

fbo = gt.createFBO(sizeHint=(512, 512))
gt.bindFBO(fbo)
gt.attachImage(  # color
    fbo, GL.GL_COLOR_ATTACHMENT0, gt.createTexImage2D(512, 512))
gt.attachImage(  # normal map
    fbo, GL.GL_COLOR_ATTACHMENT1, gt.createTexImage2D(512, 512))
gt.attachImage(  # depth/stencil
    fbo,
    GL.GL_DEPTH_STENCIL_ATTACHMENT,
    gt.createRenderbuffer(512, 512, GL.GL_DEPTH24_STENCIL8))
print(gt.isFramebufferComplete(fbo))  # True
gt.unbindFBO(None)

Examples of userData some custom function might access:

fbo.userData['flags'] = ['left_eye', 'clear_before_use']

Using a depth only texture (for shadow mapping?):

depthTex = createTexImage2D(800, 600,
                            internalFormat=GL.GL_DEPTH_COMPONENT24,
                            pixelFormat=GL.GL_DEPTH_COMPONENT)
fbo = createFBO([(GL.GL_DEPTH_ATTACHMENT, depthTex)])  # is valid

# discard FBO descriptor, just give me the ID
frameBuffer = createFBO().id
psychopy.tools.gltools.attach(fbo, attachPoint, imageBuffer)

Attach an image to a specified attachment point on the presently bound FBO.

Parameters:

fbo (FramebufferInfo) – Framebuffer descriptor to attach buffer to.

:param attachPoint int: Attachment point for ‘imageBuffer’ (e.g. GL.GL_COLOR_ATTACHMENT0). :param imageBuffer: Framebuffer-attachable buffer descriptor. :type imageBuffer: TexImage2D or RenderbufferInfo

Examples

Attach an image to attachment points on the framebuffer:

GL.glBindFramebuffer(GL.GL_FRAMEBUFFER, fbo)
attachImage(GL.GL_COLOR_ATTACHMENT0, colorTex)
attachImage(GL.GL_DEPTH_STENCIL_ATTACHMENT, depthRb)
GL.glBindFramebuffer(GL.GL_FRAMEBUFFER, lastBoundFbo)

# same as above, but using a context manager
with useFBO(fbo):
    attachImage(GL.GL_COLOR_ATTACHMENT0, colorTex)
    attachImage(GL.GL_DEPTH_STENCIL_ATTACHMENT, depthRb)
psychopy.tools.gltools.deleteFBO(fbo, deleteAttachments=True)[source]

Delete a framebuffer.

Parameters:
  • fbo (FramebufferInfo or int) – Framebuffer descriptor or name to delete.

  • deleteAttachments (bool, optional) – Delete attachments associated with the framebuffer. Default is True.

psychopy.tools.gltools.blitFBO(srcRect, dstRect=None, filter=9729, mask=16384)[source]

Copy a block of pixels between framebuffers via blitting. Read and draw framebuffers must be bound prior to calling this function. Beware, the scissor box and viewport are changed when this is called to dstRect.

Parameters:
  • srcRect (list of int) – List specifying the top-left and bottom-right coordinates of the region to copy from (<X0>, <Y0>, <X1>, <Y1>).

  • dstRect (list of int or None) – List specifying the top-left and bottom-right coordinates of the region to copy to (<X0>, <Y0>, <X1>, <Y1>). If None, srcRect is used for dstRect.

  • filter (int or str) – Interpolation method to use if the image is stretched, default is GL_LINEAR, but can also be GL_NEAREST.

  • mask (int or str) – Bitmask specifying which buffers to copy. Default is GL_COLOR_BUFFER_BIT.

Examples

Blitting pixels from on FBO to another:

# set buffers for reading and drawing
gt.setReadBuffer(fbo, 'GL_COLOR_ATTACHMENT0')
gt.setDrawBuffer(None, 'GL_BACK')  # default back buffer for window
gt.blitFBO((0 ,0, 512, 512), (0, 0, 512, 512))
psychopy.tools.gltools.useFBO(fbo)[source]

Context manager for Framebuffer Object bindings. This function yields the framebuffer name as an integer.

:param fbo int or Framebuffer: OpenGL Framebuffer Object name/ID or descriptor.

Yields:

int – OpenGL name of the framebuffer bound in the context.

Examples

Using a framebuffer context manager:

# FBO bound somewhere deep in our code
GL.glBindFramebuffer(GL.GL_FRAMEBUFFER, someOtherFBO)

...

# create a new FBO, but we have no idea what the currently bound FBO is
fbo = createFBO()

# use a context to bind attachments
with bindFBO(fbo):
    attach(GL.GL_COLOR_ATTACHMENT0, colorTex)
    attach(GL.GL_DEPTH_ATTACHMENT, depthRb)
    attach(GL.GL_STENCIL_ATTACHMENT, depthRb)
    isComplete = gltools.isComplete()

# someOtherFBO is still bound!

Renderbuffers

Tools for creating Renderbuffers.

psychopy.tools.gltools.createRenderbuffer(width, height, internalFormat=32856, samples=1)[source]

Create a new Renderbuffer Object with a specified internal format. A multisample storage buffer is created if samples > 1.

Renderbuffers contain image data and are optimized for use as render targets. See https://www.khronos.org/opengl/wiki/Renderbuffer_Object for more information.

Parameters:
  • width (int) – Buffer width in pixels.

  • height (int) – Buffer height in pixels.

  • internalFormat (int) – Format for renderbuffer data (e.g. GL_RGBA8, GL_DEPTH24_STENCIL8).

  • samples (int) – Number of samples for multi-sampling, should be >1 and power-of-two. If samples == 1, a single sample buffer is created.

Returns:

A descriptor of the created renderbuffer.

Return type:

Renderbuffer

Notes

The ‘userData’ field of the returned descriptor is a dictionary that can be used to store arbitrary data associated with the buffer.

psychopy.tools.gltools.deleteRenderbuffer(renderBuffer)[source]

Free the resources associated with a renderbuffer. This invalidates the renderbuffer’s ID.

Textures

Tools for creating textures.

psychopy.tools.gltools.createTexImage2D(width, height, target=3553, level=0, internalFormat=32856, pixelFormat=6408, dataType=5126, data=None, unpackAlignment=4, texParams=None)[source]

Create a 2D texture in video memory. This can only create a single 2D texture with targets GL_TEXTURE_2D or GL_TEXTURE_RECTANGLE.

Parameters:
  • width (int) – Texture width in pixels.

  • height (int) – Texture height in pixels.

  • target (int) – The target texture should only be either GL_TEXTURE_2D or GL_TEXTURE_RECTANGLE.

  • level (int) – LOD number of the texture, should be 0 if GL_TEXTURE_RECTANGLE is the target.

  • internalFormat (int) – Internal format for texture data (e.g. GL_RGBA8, GL_R11F_G11F_B10F).

  • pixelFormat (int) – Pixel data format (e.g. GL_RGBA, GL_DEPTH_STENCIL)

  • dataType (int) – Data type for pixel data (e.g. GL_FLOAT, GL_UNSIGNED_BYTE).

  • data (ctypes or None) – Ctypes pointer to image data. If None is specified, the texture will be created but pixel data will be uninitialized.

  • unpackAlignment (int) – Alignment requirements of each row in memory. Default is 4.

  • texParams (dict) – Optional texture parameters specified as dict. These values are passed to glTexParameteri. Each tuple must contain a parameter name and value. For example, texParameters={GL.GL_TEXTURE_MIN_FILTER: GL.GL_LINEAR, GL.GL_TEXTURE_MAG_FILTER: GL.GL_LINEAR}.

Returns:

A TexImage2DInfo descriptor.

Return type:

TexImage2DInfo

Notes

The ‘userData’ field of the returned descriptor is a dictionary that can be used to store arbitrary data associated with the texture.

Previous textures are unbound after calling ‘createTexImage2D’.

Examples

Creating a texture from an image file:

import pyglet.gl as GL  # using Pyglet for now

# empty texture
textureDesc = createTexImage2D(1024, 1024, internalFormat=GL.GL_RGBA8)

# load texture data from an image file using Pillow and NumPy
from PIL import Image
import numpy as np
im = Image.open(imageFile)  # 8bpp!
im = im.transpose(Image.FLIP_TOP_BOTTOM)  # OpenGL origin is at bottom
im = im.convert("RGBA")
pixelData = np.array(im).ctypes  # convert to ctypes!

width = pixelData.shape[1]
height = pixelData.shape[0]
textureDesc = gltools.createTexImage2D(
    width,
    height,
    internalFormat=GL.GL_RGBA,
    pixelFormat=GL.GL_RGBA,
    dataType=GL.GL_UNSIGNED_BYTE,
    data=pixelData,
    unpackAlignment=1,
    texParameters=[(GL.GL_TEXTURE_MAG_FILTER, GL.GL_LINEAR),
                   (GL.GL_TEXTURE_MIN_FILTER, GL.GL_LINEAR)])

GL.glBindTexture(GL.GL_TEXTURE_2D, textureDesc.id)
psychopy.tools.gltools.createTexImage2dFromFile(imgFile, transpose=True)[source]

Load an image from file directly into a texture.

This is a convenience function to quickly get an image file loaded into a 2D texture. The image is converted to RGBA format. Texture parameters are set for linear interpolation.

Parameters:
  • imgFile (str) – Path to the image file.

  • transpose (bool) – Flip the image so it appears upright when displayed in OpenGL image coordinates.

Returns:

Texture descriptor.

Return type:

TexImage2DInfo

psychopy.tools.gltools.createTexImage2DMultisample(width, height, target=37120, samples=1, internalFormat=32856, texParameters=())[source]

Create a 2D multisampled texture.

Parameters:
  • width (int) – Texture width in pixels.

  • height (int) – Texture height in pixels.

  • target (int) – The target texture (e.g. GL_TEXTURE_2D_MULTISAMPLE).

  • samples (int) – Number of samples for multi-sampling, should be >1 and power-of-two. Work with one sample, but will raise a warning.

  • internalFormat (int) – Internal format for texture data (e.g. GL_RGBA8, GL_R11F_G11F_B10F).

  • texParameters (list of tuple of int) – Optional texture parameters specified as a list of tuples. These values are passed to ‘glTexParameteri’. Each tuple must contain a parameter name and value. For example, texParameters=[(GL.GL_TEXTURE_MIN_FILTER, GL.GL_LINEAR), (GL.GL_TEXTURE_MAG_FILTER, GL.GL_LINEAR)]

Returns:

A TexImage2DMultisampleInfo descriptor.

Return type:

TexImage2DMultisampleInfo

psychopy.tools.gltools.deleteTexture(texture)[source]

Free the resources associated with a texture. This invalidates the texture’s ID.

psychopy.tools.gltools.bindTexture(texture, target=None, unit=None)[source]

Bind a texture to a target.

Parameters:
  • texture (GLuint, int, None, TexImage2DInfo or TexImage2DMultisampleInfo) – Texture to bind. If None, the texture will be unbound.

  • target (GLenum, int or str, optional) – Target to bind the texture to. Default is None. If None, and the texture is a TexImage2DInfo or TexImage2DMultisampleInfo object, the target will be inferred from the texture object. If specified, the value will override the target inferred from the texture. If None and the texture is an integer, the target will default to GL_TEXTURE_2D.

  • unit (GLenum, int or None, optional) – Texture unit to bind the texture to, this will also set the active texture unit. Default is None. If None, the texture will be bound to the currently active texture unit.

psychopy.tools.gltools.createCubeMap(width, height, target=34067, level=0, internalFormat=6408, pixelFormat=6408, dataType=5121, data=None, unpackAlignment=4, texParams=None)[source]

Create a cubemap.

Parameters:
  • name (int or GLuint) – OpenGL handle for the cube map. Is 0 if uninitialized.

  • target (int) – The target texture should only be GL_TEXTURE_CUBE_MAP.

  • width (int) – Texture width in pixels.

  • height (int) – Texture height in pixels.

  • level (int) – LOD number of the texture.

  • internalFormat (int) – Internal format for texture data (e.g. GL_RGBA8, GL_R11F_G11F_B10F).

  • pixelFormat (int) – Pixel data format (e.g. GL_RGBA, GL_DEPTH_STENCIL)

  • dataType (int) – Data type for pixel data (e.g. GL_FLOAT, GL_UNSIGNED_BYTE).

  • data (list or tuple) – List of six ctypes pointers to image data for each cubemap face. Image data is assigned to a face by index [+X, -X, +Y, -Y, +Z, -Z]. All images must have the same size as specified by width and height.

  • unpackAlignment (int) – Alignment requirements of each row in memory. Default is 4.

  • texParams (list of tuple of int) – Optional texture parameters specified as dict. These values are passed to glTexParameteri. Each tuple must contain a parameter name and value. For example, texParameters={ GL.GL_TEXTURE_MIN_FILTER: GL.GL_LINEAR, GL.GL_TEXTURE_MAG_FILTER: GL.GL_LINEAR}. These can be changed and will be updated the next time this instance is passed to bindTexture().

Vertex Buffer/Array Objects

Tools for creating and working with Vertex Buffer Objects (VBOs) and Vertex Array Objects (VAOs).

psychopy.tools.gltools.VertexArrayInfo(name=0, count=0, activeAttribs=None, indexBuffer=None, attribDivisors=None, isLegacy=False, userData=None)[source]

Vertex array object (VAO) descriptor.

This class only stores information about the VAO it refers to, it does not contain any actual array data associated with the VAO. Calling createVAO() returns instances of this class.

If isLegacy is True, attribute binding states are using deprecated (but still supported) pointer definition calls (eg. glVertexPointer). This is to ensure backwards compatibility. The keys stored in activeAttribs must be GLenum types such as GL_VERTEX_ARRAY.

Parameters:
  • name (int) – OpenGL handle for the VAO.

  • count (int) – Number of vertex elements. If indexBuffer is not None, count corresponds to the number of elements in the index buffer.

  • activeAttribs (dict) – Attributes and buffers defined as part of this VAO state. Keys are attribute pointer indices or capabilities (ie. GL_VERTEX_ARRAY). Modifying these values will not update the VAO state.

  • indexBuffer (VertexBufferInfo, optional) – Buffer object for indices.

  • attribDivisors (dict, optional) – Divisors for each attribute.

  • isLegacy (bool) – Array pointers were defined using the deprecated OpenGL API. If True, the VAO may work with older GLSL shaders versions and the fixed-function pipeline.

  • userData (dict or None, optional) – Optional user defined data associated with this VAO.

psychopy.tools.gltools.createVAO(attribBuffers, indexBuffer=None, attribDivisors=None, legacy=False)[source]

Create a Vertex Array object (VAO). VAOs store buffer binding states, reducing CPU overhead when drawing objects with vertex data stored in VBOs.

Define vertex attributes within a VAO state by passing a mapping for generic attribute indices and VBO buffers.

Parameters:
  • attribBuffers (dict) – Attributes and associated VBOs to add to the VAO state. Keys are vertex attribute pointer indices, values are VBO descriptors to define. Values can be tuples where the first value is the buffer descriptor, the second is the number of attribute components (int, either 2, 3 or 4), the third is the offset (int), and the last is whether to normalize the array (bool).

  • indexBuffer (VertexBufferInfo) – Optional index buffer.

  • attribDivisors (dict) – Attribute divisors to set. Keys are vertex attribute pointer indices, values are the number of instances that will pass between updates of an attribute. Setting attribute divisors is only permitted if legacy is False.

  • legacy (bool, optional) – Use legacy attribute pointer functions when setting the VAO state. This is for compatibility with older GL implementations. Key specified to attribBuffers must be GLenum types such as GL_VERTEX_ARRAY to indicate the capability to use.

Examples

Create a vertex array object and enable buffer states within it:

vao = createVAO({0: vertexPos, 1: texCoords, 2: vertexNormals})

Using an interleaved vertex buffer, all attributes are in the same buffer (vertexAttr). We need to specify offsets for each attribute by passing a buffer in a tuple with the second value specifying the offset:

# buffer with interleaved layout `00011222` per-attribute
vao = createVAO(
    {0: (vertexAttr, 3),            # size 3, offset 0
     1: (vertexAttr, 2, 3),         # size 2, offset 3
     2: (vertexAttr, 3, 5, True)})  # size 3, offset 5, normalize

You can mix interleaved and single-use buffers:

vao = createVAO(
    {0: (vertexAttr, 3, 0), 1: (vertexAttr, 3, 3), 2: vertexColors})

Specifying an optional index array, this is used for indexed drawing of primitives:

vao = createVAO({0: vertexPos}, indexBuffer=indices)

The returned VertexArrayInfo instance will have attribute isIndexed==True.

Drawing vertex arrays using a VAO, will use the indexBuffer if available:

# draw the array
drawVAO(vao, mode=GL.GL_TRIANGLES)

Use legacy attribute pointer bindings when building a VAO for compatibility with the fixed-function pipeline and older GLSL versions:

attribBuffers = {GL_VERTEX_ARRAY: vertexPos, GL_NORMAL_ARRAY: normals}
vao = createVAO(attribBuffers, legacy=True)

If you wish to used instanced drawing, you can specify attribute divisors this way:

vao = createVAO(
    {0: (vertexAttr, 3, 0), 1: (vertexAttr, 3, 3), 2: vertexColors},
    attribDivisors={2: 1})
psychopy.tools.gltools.drawVAO(vao, mode=4, start=0, count=None, instanceCount=None, flush=False)[source]

Draw a vertex array object. Uses glDrawArrays or glDrawElements if instanceCount is None, or else glDrawArraysInstanced or glDrawElementsInstanced is used.

Parameters:
  • vao (VertexArrayObject) – Vertex Array Object (VAO) to draw.

  • mode (int, optional) – Drawing mode to use (e.g. GL_TRIANGLES, GL_QUADS, GL_POINTS, etc.) for rasterization. Default is GL_TRIANGLES. Strings can be used for convenience (e.g. ‘GL_TRIANGLES’, ‘GL_QUADS’, ‘GL_POINTS’).

  • start (int, optional) – Starting index for array elements. Default is 0 which is the beginning of the array.

  • count (int, optional) – Number of indices to draw from start. Must not exceed vao.count - start.

  • instanceCount (int or None) – Number of instances to draw. If >0 and not None, instanced drawing will be used.

  • flush (bool, optional) – Flush queued drawing commands before returning.

Examples

Creating a VAO and drawing it:

# draw the VAO, renders the mesh
drawVAO(vaoDesc, GL.GL_TRIANGLES)
psychopy.tools.gltools.deleteVAO(vao)[source]

Delete a Vertex Array Object (VAO). This does not delete array buffers bound to the VAO.

Parameters:

vao (VertexArrayInfo) – VAO to delete. All fields in the descriptor except userData will be reset.

psychopy.tools.gltools.VertexBufferInfo(name=0, target=34962, usage=35044, dataType=5126, size=0, stride=0, shape=(0,), userData=None)[source]

Vertex buffer object (VBO) descriptor.

This class only stores information about the VBO it refers to, it does not contain any actual array data associated with the VBO. Calling createVBO() returns instances of this class.

It is recommended to use gltools functions bindVBO(), unbindVBO(), mapBuffer(), etc. when working with these objects.

Parameters:
  • name (GLuint or int) – OpenGL handle for the buffer.

  • target (GLenum or int, optional) – Target used when binding the buffer (e.g. GL_VERTEX_ARRAY or GL_ELEMENT_ARRAY_BUFFER). Default is GL_VERTEX_ARRAY)

  • usage (GLenum or int, optional) – Usage type for the array (i.e. GL_STATIC_DRAW).

  • dataType (Glenum, optional) – Data type of array. Default is GL_FLOAT.

  • size (int, optional) – Size of the buffer in bytes.

  • stride (int, optional) – Number of bytes between adjacent attributes. If 0, values are assumed to be tightly packed.

  • shape (tuple or list, optional) – Shape of the array used to create this VBO.

  • userData (dict, optional) – Optional user defined data associated with the VBO. If None, userData will be initialized as an empty dictionary.

psychopy.tools.gltools.createVBO(data, target=34962, dataType=None, usage=35044)[source]

Create an array buffer object (VBO).

Creates a VBO using input data, usually as a ndarray or list. Attributes common to one vertex should occupy a single row of the data array.

Parameters:
  • data (array_like) – A 2D array of values to write to the array buffer. The data type of the VBO is inferred by the type of the array. If the input is a Python list or tuple type, the data type of the array will be GL_DOUBLE.

  • target (int or str, optional) – Target used when binding the buffer (e.g. GL_VERTEX_ARRAY or GL_ELEMENT_ARRAY_BUFFER). Default is GL_VERTEX_ARRAY. Strings may also be used to specify the target, where the following are valid: ‘array’ (for GL_VERTEX_ARRAY) or ‘element_array’ (for GL_ELEMENT_ARRAY_BUFFER).

  • dataType (Glenum or None, optional) – Data type of array. Input data will be recast to an appropriate type if necessary. Default is None. If None, the data type will be inferred from the input data.

  • usage (GLenum or int, optional) – Usage hint for the array (i.e. GL_STATIC_DRAW). This will hint to the GL driver how the buffer will be used so it can optimize memory allocation and access. Default is GL_STATIC_DRAW.

Returns:

A descriptor with vertex buffer information.

Return type:

VertexBufferInfo

Examples

Creating a vertex buffer object with vertex data:

# vertices of a triangle
verts = [[ 1.0,  1.0, 0.0],   # v0
         [ 0.0, -1.0, 0.0],   # v1
         [-1.0,  1.0, 0.0]]   # v2

# load vertices to graphics device, return a descriptor
vboDesc = createVBO(verts)

Drawing triangles or quads using vertex buffer data:

nIndices, vSize = vboDesc.shape  # element size

bindVBO(vboDesc)
setVertexAttribPointer(
    GL_VERTEX_ARRAY, vSize, vboDesc.dataType, legacy=True)
enableVertexAttribArray(GL_VERTEX_ARRAY, legacy=True)

if vSize == 3:
    drawMode = GL_TRIANGLES
elif vSize == 4:
    drawMode = GL_QUADS

glDrawArrays(drawMode, 0, nIndices)
glFlush()

disableVertexAttribArray(GL_VERTEX_ARRAY, legacy=True)
unbindVBO()

Custom data can be associated with this vertex buffer by specifying userData:

myVBO = createVBO(data)
myVBO.userData['startIdx'] = 14  # first index to draw with

# use it later
nIndices, vSize = vboDesc.shape  # element size
startIdx = myVBO.userData['startIdx']
endIdx = nIndices - startIdx
glDrawArrays(GL_TRIANGLES, startIdx, endIdx)
glFlush()
psychopy.tools.gltools.bindVBO(vbo)[source]

Bind a VBO to the current GL state.

Parameters:

vbo (VertexBufferInfo) – VBO descriptor to bind.

Returns:

True is the binding state was changed. Returns False if the state was not changed due to the buffer already being bound.

Return type:

bool

psychopy.tools.gltools.unbindVBO(vbo)[source]

Unbind a vertex buffer object (VBO).

Parameters:

vbo (VertexBufferInfo) – VBO descriptor to unbind.

psychopy.tools.gltools.mapBuffer(vbo, start=0, length=None, read=True, write=True, noSync=False)[source]

Map a vertex buffer object to client memory. This allows you to modify its contents.

If planning to update VBO vertex data, make sure the VBO usage types are GL_DYNAMIC_* or GL_STREAM_* or else serious performance issues may arise.

Warning

Modifying buffer data must be done carefully, or else system stability may be affected. Do not use the returned view ndarray outside of successive mapBuffer() and unmapBuffer() calls. Do not use the mapped buffer for rendering until after unmapBuffer() is called.

Parameters:
  • vbo (VertexBufferInfo) – Vertex buffer to map to client memory.

  • start (int) – Initial index of the sub-range of the buffer to modify.

  • length (int or None) – Number of elements of the sub-array to map from offset. If None, all elements to from offset to the end of the array are mapped.

  • read (bool, optional) – Allow data to be read from the buffer (sets GL_MAP_READ_BIT). This is ignored if noSync is True.

  • write (bool, optional) – Allow data to be written to the buffer (sets GL_MAP_WRITE_BIT).

  • noSync (bool, optional) – If True, GL will not wait until the buffer is free (i.e. not being processed by the GPU) to map it (sets GL_MAP_UNSYNCHRONIZED_BIT). The contents of the previous storage buffer are discarded and the driver returns a new one. This prevents the CPU from stalling until the buffer is available.

Returns:

View of the data. The type of the returned array is one which best matches the data type of the buffer.

Return type:

ndarray

Examples

Map a buffer and edit it:

arr = mapBuffer(vbo)
arr[:, :] += 2.0  # add 2 to all values
unmapBuffer(vbo)  # call when done
# Don't ever modify `arr` after calling `unmapBuffer`. Delete it if
# necessary to prevent it form being used.
del arr

Modify a sub-range of data by specifying start and length, indices correspond to values, not byte offsets:

arr = mapBuffer(vbo, start=12, end=24)
arr[:, :] *= 10.0
unmapBuffer(vbo)
psychopy.tools.gltools.unmapBuffer(vbo)[source]

Unmap a previously mapped buffer. Must be called after mapBuffer() is called and before any drawing operations which use the buffer are called. Failing to call this before using the buffer could result in a system error.

Parameters:

vbo (VertexBufferInfo) – Vertex buffer descriptor.

Returns:

True if the buffer has been successfully modified. If False, the data was corrupted for some reason and needs to be resubmitted.

Return type:

bool

psychopy.tools.gltools.deleteVBO(vbo)[source]

Delete a vertex buffer object (VBO).

Parameters:

vbo (VertexBufferInfo) – Descriptor of VBO to delete.

psychopy.tools.gltools.setVertexAttribPointer(index, vbo, size=None, offset=0, normalize=False, legacy=False)[source]

Define an array of vertex attribute data with a VBO descriptor.

In modern OpenGL implementations, attributes are ‘generic’, where an attribute pointer index does not correspond to any special vertex property. Usually the usage for an attribute is defined in the shader program. It is recommended that shader programs define attributes using the layout parameters:

layout (location = 0) in vec3 position;
layout (location = 1) in vec2 texCoord;
layout (location = 2) in vec3 normal;

Setting attribute pointers can be done like this:

setVertexAttribPointer(0, posVbo)
setVertexAttribPointer(1, texVbo)
setVertexAttribPointer(2, normVbo)

For compatibility with older OpenGL specifications, some drivers will alias vertex pointers unless they are explicitly defined in the shader. This allows VAOs the be used with the fixed-function pipeline or older GLSL versions.

On nVidia graphics drivers (and maybe others), the following attribute pointer indices are aliased with reserved GLSL names:

  • gl_Vertex - 0

  • gl_Normal - 2

  • gl_Color - 3

  • gl_SecondaryColor - 4

  • gl_FogCoord - 5

  • gl_MultiTexCoord0 - 8

  • gl_MultiTexCoord1 - 9

  • gl_MultiTexCoord2 - 10

  • gl_MultiTexCoord3 - 11

  • gl_MultiTexCoord4 - 12

  • gl_MultiTexCoord5 - 13

  • gl_MultiTexCoord6 - 14

  • gl_MultiTexCoord7 - 15

Specifying legacy as True will allow for old-style pointer definitions. You must specify the capability as a GLenum associated with the pointer in this case:

setVertexAttribPointer(GL_VERTEX_ARRAY, posVbo, legacy=True)
setVertexAttribPointer(GL_TEXTURE_COORD_ARRAY, texVbo, legacy=True)
setVertexAttribPointer(GL_NORMAL_ARRAY, normVbo, legacy=True)
Parameters:
  • index (int) – Index of the attribute to modify. If legacy=True, this value should be a GLenum type corresponding to the capability to bind the buffer to, such as GL_VERTEX_ARRAY, GL_TEXTURE_COORD_ARRAY, GL_NORMAL_ARRAY, etc.

  • vbo (VertexBufferInfo) – VBO descriptor.

  • size (int, optional) – Number of components per vertex attribute, can be either 1, 2, 3, or 4. If None is specified, the component size will be inferred from the shape of the VBO. You must specify this value if the VBO is interleaved.

  • offset (int, optional) – Starting index of the attribute in the buffer.

  • normalize (bool, optional) – Normalize fixed-point format values when accessed.

  • legacy (bool, optional) – Use legacy vertex attributes (ie. GL_VERTEX_ARRAY, GL_TEXTURE_COORD_ARRAY, etc.) for backwards compatibility.

Examples

Define a generic attribute from a vertex buffer descriptor:

# set the vertex location attribute
setVertexAttribPointer(0, vboDesc)  # 0 is vertex in our shader
GL.glColor3f(1.0, 0.0, 0.0)  # red triangle

# draw the triangle
nIndices, vSize = vboDesc.shape  # element size
GL.glDrawArrays(GL.GL_TRIANGLES, 0, nIndices)

If our VBO has interleaved attributes, we can specify offset to account for that:

# define interleaved vertex attributes
#        |     Position    | Texture |   Normals   |
vQuad = [[ -1.0, -1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0],  # v0
         [ -1.0,  1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0],  # v1
         [  1.0,  1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0],  # v2
         [  1.0, -1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0]]  # v3

# create a VBO with interleaved attributes
vboInterleaved = createVBO(np.asarray(vQuad, dtype=np.float32))

# ... before rendering, set the attribute pointers
GL.glBindBuffer(vboInterleaved.target, vboInterleaved.name)
gltools.setVertexAttribPointer(
    0, vboInterleaved, size=3, offset=0)  # vertex pointer
gltools.setVertexAttribPointer(
    8, vboInterleaved, size=2, offset=3)  # texture pointer
gltools.setVertexAttribPointer(
    3, vboInterleaved, size=3, offset=5)  # normals pointer

# Note, we specified `bind=False` since we are managing the binding
# state. It is recommended that you do this when setting up interleaved
# buffers to avoid re-binding the same buffer.

# draw red, full screen quad
GL.glColor3f(1.0, 0.0, 0.0)
GL.glDrawArrays(GL.GL_QUADS, 0, vboInterleaved.shape[1])

# call these when done if `enable=True`
gltools.disableVertexAttribArray(0)
gltools.disableVertexAttribArray(8)
gltools.disableVertexAttribArray(1)

# unbind the buffer
GL.glBindBuffer(vboInterleaved.target, 0)
psychopy.tools.gltools.enableVertexAttribArray(index, legacy=False)[source]

Enable a vertex attribute array. Attributes will be used for use by subsequent draw operations. Be sure to call disableVertexAttribArray() on the same attribute to prevent currently enabled attributes from affecting later rendering.

Parameters:
  • index (int) – Index of the attribute to enable. If legacy=True, this value should be a GLenum type corresponding to the capability to bind the buffer to, such as GL_VERTEX_ARRAY, GL_TEXTURE_COORD_ARRAY, GL_NORMAL_ARRAY, etc.

  • legacy (bool, optional) – Use legacy vertex attributes (ie. GL_VERTEX_ARRAY, GL_TEXTURE_COORD_ARRAY, etc.) for backwards compatibility.

psychopy.tools.gltools.disableVertexAttribArray(index, legacy=False)[source]

Disable a vertex attribute array.

Parameters:
  • index (int) – Index of the attribute to enable. If legacy=True, this value should be a GLenum type corresponding to the capability to bind the buffer to, such as GL_VERTEX_ARRAY, GL_TEXTURE_COORD_ARRAY, GL_NORMAL_ARRAY, etc.

  • legacy (bool, optional) – Use legacy vertex attributes (ie. GL_VERTEX_ARRAY, GL_TEXTURE_COORD_ARRAY, etc.) for backwards compatibility.

Materials and Lighting

Tools for specifying the appearance of faces and shading. Note that these tools use the legacy OpenGL pipeline which may not be available on your platform. Use fragment/vertex shaders instead for newer applications.

psychopy.tools.gltools.createMaterial(params=(), textures=(), face=1032)[source]

Create a new material.

Parameters:
  • params (list of tuple, optional) – List of material modes and values. Each mode is assigned a value as (mode, color). Modes can be GL_AMBIENT, GL_DIFFUSE, GL_SPECULAR, GL_EMISSION, GL_SHININESS or GL_AMBIENT_AND_DIFFUSE. Colors must be a tuple of 4 floats which specify reflectance values for each RGBA component. The value of GL_SHININESS should be a single float. If no values are specified, an empty material will be created.

  • textures (list of tuple, optional) – List of texture units and TexImage2D descriptors. These will be written to the ‘textures’ field of the returned descriptor. For example, [(GL.GL_TEXTURE0, texDesc0), (GL.GL_TEXTURE1, texDesc1)]. The number of texture units per-material is GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS.

  • face (int, optional) – Faces to apply material to. Values can be GL_FRONT_AND_BACK, GL_FRONT and GL_BACK. The default is GL_FRONT_AND_BACK.

Returns:

A descriptor with material properties.

Return type:

Material

Examples

Creating a new material with given properties:

# The values for the material below can be found at
# http://devernay.free.fr/cours/opengl/materials.html

# create a gold material
gold = createMaterial([
    (GL.GL_AMBIENT, (0.24725, 0.19950, 0.07450, 1.0)),
    (GL.GL_DIFFUSE, (0.75164, 0.60648, 0.22648, 1.0)),
    (GL.GL_SPECULAR, (0.628281, 0.555802, 0.366065, 1.0)),
    (GL.GL_SHININESS, 0.4 * 128.0)])

Use the material when drawing:

useMaterial(gold)
drawVAO( ... )  # all meshes will be gold
useMaterial(None)  # turn off material when done

Create a red plastic material, but define reflectance and shine later:

red_plastic = createMaterial()

# you need to convert values to ctypes!
red_plastic.values[GL_AMBIENT] = (GLfloat * 4)(0.0, 0.0, 0.0, 1.0)
red_plastic.values[GL_DIFFUSE] = (GLfloat * 4)(0.5, 0.0, 0.0, 1.0)
red_plastic.values[GL_SPECULAR] = (GLfloat * 4)(0.7, 0.6, 0.6, 1.0)
red_plastic.values[GL_SHININESS] = 0.25 * 128.0

# set and draw
useMaterial(red_plastic)
drawVertexbuffers( ... )  # all meshes will be red plastic
useMaterial(None)
psychopy.tools.gltools.useMaterial(material, useTextures=True)[source]

Use a material for proceeding vertex draws.

Parameters:
  • material (Material or None) – Material descriptor to use. Default material properties are set if None is specified. This is equivalent to disabling materials.

  • useTextures (bool) – Enable textures. Textures specified in a material descriptor’s ‘texture’ attribute will be bound and their respective texture units will be enabled. Note, when disabling materials, the value of useTextures must match the previous call. If there are no textures attached to the material, useTexture will be silently ignored.

Return type:

None

Notes

  1. If a material mode has a value of None, a color with all components 0.0 will be assigned.

  2. Material colors and shininess values are accessible from shader programs after calling ‘useMaterial’. Values can be accessed via built-in ‘gl_FrontMaterial’ and ‘gl_BackMaterial’ structures (e.g. gl_FrontMaterial.diffuse).

Examples

Use a material when drawing:

useMaterial(metalMaterials.gold)
drawVAO( ... )  # all meshes drawn will be gold
useMaterial(None)  # turn off material when done
psychopy.tools.gltools.createLight(params=())[source]

Create a point light source.

psychopy.tools.gltools.useLights(lights, setupOnly=False)[source]

Use specified lights in successive rendering operations. All lights will be transformed using the present modelview matrix.

Parameters:
  • lights (List of Light or None) – Descriptor of a light source. If None, lighting is disabled.

  • setupOnly (bool, optional) – Do not enable lighting or lights. Specify True if lighting is being computed via fragment shaders.

psychopy.tools.gltools.setAmbientLight(color)[source]

Set the global ambient lighting for the scene when lighting is enabled. This is equivalent to GL.glLightModelfv(GL.GL_LIGHT_MODEL_AMBIENT, color) and does not contribute to the GL_MAX_LIGHTS limit.

Parameters:

color (tuple) – Ambient lighting RGBA intensity for the whole scene.

Notes

If unset, the default value is (0.2, 0.2, 0.2, 1.0) when GL_LIGHTING is enabled.

Meshes

Tools for loading or procedurally generating meshes (3D models).

psychopy.tools.gltools.ObjMeshInfo(vertexPos=None, texCoords=None, normals=None, faces=None, extents=None, mtlFile=None)[source]

Descriptor for mesh data loaded from a Wavefront OBJ file.

psychopy.tools.gltools.loadObjFile(objFile)[source]

Load a Wavefront OBJ file (*.obj).

Loads vertex, normals, and texture coordinates from the provided *.obj file into arrays. These arrays can be processed then loaded into vertex buffer objects (VBOs) for rendering. The *.obj file must at least specify vertex position data to be loaded successfully. Normals and texture coordinates are optional.

Faces can be either triangles or quads, but not both. Faces are grouped by their materials. Index arrays are generated for each material present in the file.

Data from the returned ObjMeshInfo object can be used to create vertex buffer objects and arrays for rendering. See Examples below for details on how to do this.

Parameters:

objFile (str) – Path to the *.OBJ file to load.

Returns:

Mesh data.

Return type:

ObjMeshInfo

See also

loadMtlFile

Load a *.mtl file.

Notes

  1. This importer should work fine for most sanely generated files. Export your model with Blender for best results, even if you used some other package to create it.

  2. The mesh cannot contain both triangles and quads.

Examples

Loading a *.obj mode from file:

objModel = loadObjFile('/path/to/file.obj')
# load the material (*.mtl) file, textures are also loaded
mtllib = loadMtl('/path/to/' + objModel.mtlFile)

Creating separate vertex buffer objects (VBOs) for each vertex attribute:

vertexPosVBO = createVBO(objModel.vertexPos)
texCoordVBO = createVBO(objModel.texCoords)
normalsVBO = createVBO(objModel.normals)

Create vertex array objects (VAOs) to draw the mesh. We create VAOs for each face material:

objVAOs = {}  # dictionary for VAOs
# for each material create a VAO
# keys are material names, values are index buffers
for material, faces in objModel.faces.items():
    # convert index buffer to VAO
    indexBuffer =                 gltools.createVBO(
            faces.flatten(),  # flatten face index for element array
            target=GL.GL_ELEMENT_ARRAY_BUFFER,
            dataType=GL.GL_UNSIGNED_INT)

    # see `setVertexAttribPointer` for more information about attribute
    # pointer indices
    objVAOs[material] = gltools.createVAO(
        {0: vertexPosVBO,  # 0 = gl_Vertex
         8: texCoordVBO,   # 8 = gl_MultiTexCoord0
         2: normalsVBO},   # 2 = gl_Normal
         indexBuffer=indexBuffer)

    # if using legacy attribute pointers, do this instead ...
    # objVAOs[key] = createVAO({GL_VERTEX_ARRAY: vertexPosVBO,
    #                           GL_TEXTURE_COORD_ARRAY: texCoordVBO,
    #                           GL_NORMAL_ARRAY: normalsVBO},
    #                           indexBuffer=indexBuffer,
    #                           legacy=True)  # this needs to be `True`

To render the VAOs using objVAOs created above, do the following:

for material, vao in objVAOs.items():
    useMaterial(mtllib[material])
    drawVAO(vao)

useMaterial(None)  # disable materials when done

Optionally, you can create a single-storage, interleaved VBO by using numpy.hstack. On some GL implementations, using single-storage buffers offers better performance:

interleavedData = numpy.hstack(
    (objModel.vertexPos, objModel.texCoords, objModel.normals))
vertexData = createVBO(interleavedData)

Creating VAOs with interleaved, single-storage buffers require specifying additional information, such as size and offset:

objVAOs = {}
for key, val in objModel.faces.items():
    indexBuffer =                 gltools.createVBO(
            faces.flatten(),
            target=GL.GL_ELEMENT_ARRAY_BUFFER,
            dataType=GL.GL_UNSIGNED_INT)

    objVAOs[key] = createVAO({0: (vertexData, 3, 0),  # size=3, offset=0
                              8: (vertexData, 2, 3),  # size=2, offset=3
                              2: (vertexData, 3, 5),  # size=3, offset=5
                              indexBuffer=val)

Drawing VAOs with interleaved buffers is exactly the same as shown before with separate buffers.

psychopy.tools.gltools.loadMtlFile(mtllib, texParams=None)[source]

Load a material library file (*.mtl).

Parameters:
  • mtllib (str) – Path to the material library file.

  • texParams (list or tuple) – Optional texture parameters for loaded textures. Texture parameters are specified as a list of tuples. Each item specifies the option and parameter. For instance, [(GL.GL_TEXTURE_MAG_FILTER, GL.GL_LINEAR), …]. By default, linear filtering is used for both the minifying and magnification filter functions. This is adequate for most uses.

Returns:

Dictionary of materials. Where each key is the material name found in the file, and values are Material namedtuple objects.

Return type:

dict

See also

loadObjFile

Load an *.OBJ file.

Examples

Load material associated with an *.OBJ file:

objModel = loadObjFile('/path/to/file.obj')
# load the material (*.mtl) file, textures are also loaded
mtllib = loadMtl('/path/to/' + objModel.mtlFile)

Use a material when rendering vertex arrays:

useMaterial(mtllib[material])
drawVAO(vao)
useMaterial(None)  # disable materials when done
psychopy.tools.gltools.createUVSphere(radius=0.5, sectors=16, stacks=16, flipFaces=False)[source]

Create a UV sphere.

Procedurally generate a UV sphere by specifying its radius, and number of stacks and sectors. The poles of the resulting sphere will be aligned with the Z-axis.

Surface normals and texture coordinates are automatically generated. The returned normals are computed to produce smooth shading.

Parameters:
  • radius (float, optional) – Radius of the sphere in scene units (usually meters). Default is 0.5.

  • sectors (int, optional) – Number of longitudinal and latitudinal sub-divisions. Default is 16 for both.

  • stacks (int, optional) – Number of longitudinal and latitudinal sub-divisions. Default is 16 for both.

  • flipFaces (bool, optional) – If True, normals and face windings will be set to point inward towards the center of the sphere. Texture coordinates will remain the same. Default is False.

Returns:

Vertex attribute arrays (position, texture coordinates, and normals) and triangle indices.

Return type:

tuple

Examples

Create a UV sphere and VAO to render it:

vertices, textureCoords, normals, faces =             gltools.createUVSphere(sectors=32, stacks=32)

vertexVBO = gltools.createVBO(vertices)
texCoordVBO = gltools.createVBO(textureCoords)
normalsVBO = gltools.createVBO(normals)
indexBuffer = gltools.createVBO(
    faces.flatten(),
    target=GL.GL_ELEMENT_ARRAY_BUFFER,
    dataType=GL.GL_UNSIGNED_INT)

vao = gltools.createVAO({0: vertexVBO, 8: texCoordVBO, 2: normalsVBO},
    indexBuffer=indexBuffer)

# in the rendering loop
gltools.drawVAO(vao, GL.GL_TRIANGLES)

The color of the sphere can be changed by calling glColor*:

glColor4f(1.0, 0.0, 0.0, 1.0)  # red
gltools.drawVAO(vao, GL.GL_TRIANGLES)

Raw coordinates can be transformed prior to uploading to VBOs. Here we can rotate vertex positions and normals so the equator rests on Z-axis:

r = mt.rotationMatrix(90.0, (1.0, 0, 0.0))  # 90 degrees about +X axis
vertices = mt.applyMatrix(r, vertices)
normals = mt.applyMatrix(r, normals)
psychopy.tools.gltools.createPlane(size=(1.0, 1.0))[source]

Create a plane.

Procedurally generate a plane (or quad) mesh by specifying its size. Texture coordinates are computed automatically, with origin at the bottom left of the plane. The generated plane is perpendicular to the +Z axis, origin of the plane is at its center.

Parameters:

size (tuple or float) – Dimensions of the plane. If a single value is specified, the plane will be square. Provide a tuple of floats to specify the width and length of the plane (eg. size=(0.2, 1.3)).

Returns:

Vertex attribute arrays (position, texture coordinates, and normals) and triangle indices.

Return type:

tuple

Examples

Create a plane mesh and draw it:

vertices, textureCoords, normals, faces = gltools.createPlane()

vertexVBO = gltools.createVBO(vertices)
texCoordVBO = gltools.createVBO(textureCoords)
normalsVBO = gltools.createVBO(normals)
indexBuffer = gltools.createVBO(
    faces.flatten(),
    target=GL.GL_ELEMENT_ARRAY_BUFFER,
    dataType=GL.GL_UNSIGNED_INT)

vao = gltools.createVAO({0: vertexVBO, 8: texCoordVBO, 2: normalsVBO},
    indexBuffer=indexBuffer)

# in the rendering loop
gltools.drawVAO(vao, GL.GL_TRIANGLES)
psychopy.tools.gltools.createMeshGridFromArrays(xvals, yvals, zvals=None, tessMode='diag', computeNormals=True)[source]

Create a mesh grid using coordinates from arrays.

Generates a mesh using data in provided in 2D arrays of vertex coordinates. Triangle faces are automatically computed by this function by joining adjacent vertices at neighbouring indices in the array. Texture coordinates are generated covering the whole mesh, with origin at the bottom left.

Parameters:
  • xvals (array_like) – NxM arrays of X and Y coordinates. Both arrays must have the same shape. the resulting mesh will have a single vertex for each X and Y pair. Faces will be generated to connect adjacent coordinates in the array.

  • yvals (array_like) – NxM arrays of X and Y coordinates. Both arrays must have the same shape. the resulting mesh will have a single vertex for each X and Y pair. Faces will be generated to connect adjacent coordinates in the array.

  • zvals (array_like, optional) – NxM array of Z coordinates for each X and Y. Must have the same shape as X and Y. If not specified, the Z coordinates will be filled with zeros.

  • tessMode (str, optional) – Tessellation mode. Specifies how faces are generated. Options are ‘center’, ‘radial’, and ‘diag’. Default is ‘diag’. Modes ‘radial’ and ‘center’ work best with odd numbered array dimensions.

  • computeNormals (bool, optional) – Compute normals for the generated mesh. If False, all normals are set to face in the +Z direction. Presently, computing normals is a slow operation and may not be needed for some meshes.

Returns:

Vertex attribute arrays (position, texture coordinates, and normals) and triangle indices.

Return type:

tuple

Examples

Create a 3D sine grating mesh using 2D arrays:

x = np.linspace(0, 1.0, 32)
y = np.linspace(1.0, 0.0, 32)
xx, yy = np.meshgrid(x, y)
zz = np.tile(np.sin(np.linspace(0.0, 32., 32)) * 0.02, (32, 1))

vertices, textureCoords, normals, faces =             gltools.createMeshGridFromArrays(xx, yy, zz)
psychopy.tools.gltools.createMeshGrid(size=(1.0, 1.0), subdiv=0, tessMode='diag')[source]

Create a grid mesh.

Procedurally generate a grid mesh by specifying its size and number of sub-divisions. Texture coordinates are computed automatically. The origin is at the center of the mesh. The generated grid is perpendicular to the +Z axis, origin of the grid is at its center.

Parameters:
  • size (tuple or float) – Dimensions of the mesh. If a single value is specified, the plane will be square. Provide a tuple of floats to specify the width and length of the plane (eg. size=(0.2, 1.3)).

  • subdiv (int, optional) – Number of subdivisions. Zero subdivisions are applied by default, and the resulting mesh will only have vertices at the corners.

  • tessMode (str, optional) – Tessellation mode. Specifies how faces are subdivided. Options are ‘center’, ‘radial’, and ‘diag’. Default is ‘diag’. Modes ‘radial’ and ‘center’ work best with an odd number of subdivisions.

Returns:

Vertex attribute arrays (position, texture coordinates, and normals) and triangle indices.

Return type:

tuple

Examples

Create a grid mesh and draw it:

vertices, textureCoords, normals, faces = gltools.createPlane()

vertexVBO = gltools.createVBO(vertices)
texCoordVBO = gltools.createVBO(textureCoords)
normalsVBO = gltools.createVBO(normals)
indexBuffer = gltools.createVBO(
    faces.flatten(),
    target=GL.GL_ELEMENT_ARRAY_BUFFER,
    dataType=GL.GL_UNSIGNED_INT)

vao = gltools.createVAO({0: vertexVBO, 8: texCoordVBO, 2: normalsVBO},
    indexBuffer=indexBuffer)

# in the rendering loop
gltools.drawVAO(vao, GL.GL_TRIANGLES)

Randomly displace vertices off the plane of the grid by setting the Z value per vertex:

vertices, textureCoords, normals, faces =             gltools.createMeshGrid(subdiv=11)

numVerts = vertices.shape[0]
vertices[:, 2] = np.random.uniform(-0.02, 0.02, (numVerts,)))  # Z

# you must recompute surface normals to get correct shading!
normals = gltools.calculateVertexNormals(vertices, faces)

# create a VAO as shown in the previous example here to draw it ...
psychopy.tools.gltools.createBox(size=(1.0, 1.0, 1.0), flipFaces=False)[source]

Create a box mesh.

Create a box mesh by specifying its size in three dimensions (x, y, z), or a single value (float) to create a cube. The resulting box will be centered about the origin. Texture coordinates and normals are automatically generated for each face.

Setting flipFaces=True will make faces and normals point inwards, this allows boxes to be viewed and lit correctly from the inside.

Parameters:
  • size (tuple or float) – Dimensions of the mesh. If a single value is specified, the box will be a cube. Provide a tuple of floats to specify the width, length, and height of the box (eg. size=(0.2, 1.3, 2.1)).

  • flipFaces (bool, optional) – If True, normals and face windings will be set to point inward towards the center of the box. Texture coordinates will remain the same. Default is False.

Returns:

Vertex attribute arrays (position, texture coordinates, and normals) and triangle indices.

Return type:

tuple

Examples

Create a box mesh and draw it:

vertices, textureCoords, normals, faces = gltools.createBox()

vertexVBO = gltools.createVBO(vertices)
texCoordVBO = gltools.createVBO(textureCoords)
normalsVBO = gltools.createVBO(normals)
indexBuffer = gltools.createVBO(
    faces.flatten(),
    target=GL.GL_ELEMENT_ARRAY_BUFFER,
    dataType=GL.GL_UNSIGNED_INT)

vao = gltools.createVAO({0: vertexVBO, 8: texCoordVBO, 2: normalsVBO},
    indexBuffer=indexBuffer)

# in the rendering loop
gltools.drawVAO(vao, GL.GL_TRIANGLES)
psychopy.tools.gltools.transformMeshPosOri(vertices, normals, pos=(0.0, 0.0, 0.0), ori=(0.0, 0.0, 0.0, 1.0))[source]

Transform a mesh.

Transform mesh vertices and normals to a new position and orientation using a position coordinate and rotation quaternion. Values vertices and normals must be the same shape. This is intended to be used when editing raw vertex data prior to rendering. Do not use this to change the configuration of an object while rendering.

Parameters:
  • vertices (array_like) – Nx3 array of vertices.

  • normals (array_like) – Nx3 array of normals.

  • pos (array_like, optional) – Position vector to transform mesh vertices. If Nx3, vertices will be transformed by corresponding rows of pos.

  • ori (array_like, optional) – Orientation quaternion in form [x, y, z, w]. If Nx4, vertices and normals will be transformed by corresponding rows of ori.

Returns:

Transformed vertices and normals.

Return type:

tuple

Examples

Create and re-orient a plane to face upwards:

vertices, textureCoords, normals, faces = createPlane()

# rotation quaternion
qr = quatFromAxisAngle((1., 0., 0.), -90.0)  # -90 degrees about +X axis

# transform the normals and points
vertices, normals = transformMeshPosOri(vertices, normals, ori=qr)

Any create* primitive generating function can be used inplace of createPlane.

psychopy.tools.gltools.calculateVertexNormals(vertices, faces, shading='smooth')[source]

Calculate vertex normals given vertices and triangle faces.

Finds all faces sharing a vertex index and sets its normal to either the face normal if shading=’flat’ or the average normals of adjacent faces if shading=’smooth’. Note, this function does not convert between flat and smooth shading. Flat shading only works correctly if each vertex belongs to exactly one face.

The direction of the normals are determined by the winding order of triangles, assumed counter clock-wise (OpenGL default). Most 3D model editing software exports using this convention. If not, winding orders can be reversed by calling:

faces = numpy.fliplr(faces)

In some case when using ‘smooth’, creases may appear if vertices are at the same location, but do not share the same index. This may be desired in some cases, however one may use the smoothCreases() function computing normals to smooth out creases.

Parameters:
  • vertices (array_like) – Nx3 vertex positions.

  • faces (array_like) – Nx3 vertex indices.

  • shading (str, optional) – Shading mode. Options are ‘smooth’ and ‘flat’. Flat only works with meshes where no vertex index is shared across faces, if not, the returned normals will be invalid.

Returns:

Vertex normals array with the shame shape as vertices. Computed normals are normalized.

Return type:

ndarray

Examples

Recomputing vertex normals for a UV sphere:

# create a sphere and discard normals
vertices, textureCoords, _, faces = gltools.createUVSphere()
normals = gltools.calculateVertexNormals(vertices, faces)

Miscellaneous

Miscellaneous tools for working with OpenGL.

psychopy.tools.gltools.getIntegerv(parName)[source]

Get a single integer parameter value, return it as a Python integer.

Parameters:

pName (int) – OpenGL property enum to query (e.g. GL_MAJOR_VERSION).

Return type:

int

psychopy.tools.gltools.getFloatv(parName)[source]

Get a single float parameter value, return it as a Python float.

Parameters:

pName (float) – OpenGL property enum to query.

Return type:

float

psychopy.tools.gltools.getString(parName)[source]

Get a single string parameter value, return it as a Python UTF-8 string.

Parameters:

pName (int) – OpenGL property enum to query (e.g. GL_VENDOR).

Return type:

str

psychopy.tools.gltools.getOpenGLInfo()[source]

Get general information about the OpenGL implementation on this machine. This should provide a consistent means of doing so regardless of the OpenGL interface we are using.

Returns are dictionary with the following fields:

vendor, renderer, version, majorVersion, minorVersion, doubleBuffer,
maxTextureSize, stereo, maxSamples, extensions

Supported extensions are returned as a list in the ‘extensions’ field. You can check if a platform supports an extension by checking the membership of the extension name in that list.

Return type:

OpenGLInfo

psychopy.tools.gltools.getModelViewMatrix()[source]

Get the present model matrix from the OpenGL matrix stack.

Returns:

4x4 model/view matrix.

Return type:

ndarray

psychopy.tools.gltools.getProjectionMatrix()[source]

Get the present projection matrix from the OpenGL matrix stack.

Returns:

4x4 projection matrix.

Return type:

ndarray

Examples

Working with Framebuffer Objects (FBOs):

Creating an empty framebuffer with no attachments:

fbo = createFBO()  # invalid until attachments are added

Create a render target with multiple color texture attachments:

colorTex = createTexImage2D(1024,1024)  # empty texture
depthRb = createRenderbuffer(800,600,internalFormat=GL.GL_DEPTH24_STENCIL8)

GL.glBindFramebuffer(GL.GL_FRAMEBUFFER, fbo.id)
attach(GL.GL_COLOR_ATTACHMENT0, colorTex)
attach(GL.GL_DEPTH_ATTACHMENT, depthRb)
attach(GL.GL_STENCIL_ATTACHMENT, depthRb)
# or attach(GL.GL_DEPTH_STENCIL_ATTACHMENT, depthRb)
GL.glBindFramebuffer(GL.GL_FRAMEBUFFER, 0)

Attach FBO images using a context. This automatically returns to the previous FBO binding state when complete. This is useful if you don’t know the current binding state:

with useFBO(fbo):
    attach(GL.GL_COLOR_ATTACHMENT0, colorTex)
    attach(GL.GL_DEPTH_ATTACHMENT, depthRb)
    attach(GL.GL_STENCIL_ATTACHMENT, depthRb)

How to set userData some custom function might access:

fbo.userData['flags'] = ['left_eye', 'clear_before_use']

Binding an FBO for drawing/reading:

GL.glBindFramebuffer(GL.GL_FRAMEBUFFER, fb.id)

Depth-only framebuffers are valid, sometimes need for generating shadows:

depthTex = createTexImage2D(800, 600,
                            internalFormat=GL.GL_DEPTH_COMPONENT24,
                            pixelFormat=GL.GL_DEPTH_COMPONENT)
fbo = createFBO([(GL.GL_DEPTH_ATTACHMENT, depthTex)])

Deleting a framebuffer when done with it. This invalidates the framebuffer’s ID and makes it available for use:

deleteFBO(fbo)

Back to top