I have written a code to render a triangle using a shader program. I want to rotate the triangle. I'm using PyGLM to set a transformation matrix. Here I'm presenting the whole code. If I run this code a triangle is appearing in the window as expected, but there is no rotation. I think I've failed to pass the transformation matrix to the buffer.
from OpenGL.GL import *
from OpenGL.GLU import *
from OpenGL.GLUT import *
from OpenGL.GL import shaders
import numpy as np
import glm
VERTEX_SHADER = """
#version 330
in vec4 position;
in vec3 color;
out vec3 newColor;
void main()
{
gl_Position = position;
newColor = color;
}
"""
FRAGMENT_SHADER = """
#version 330
in vec3 newColor;
out vec4 outColor;
void main()
{
outColor = vec4(newColor,1.0f);
}
"""
shaderProgram = None
def initliaze():
global VERTEXT_SHADER
global FRAGMEN_SHADER
global shaderProgram
vertexshader = shaders.compileShader(VERTEX_SHADER, GL_VERTEX_SHADER)
fragmentshader = shaders.compileShader(FRAGMENT_SHADER, GL_FRAGMENT_SHADER)
shaderProgram = shaders.compileProgram(vertexshader, fragmentshader)
triangles = [-0.5, -0.5, 0.0, 1.0,0.0,0.0,
0.5, -0.5, 0.0, 0.0,1.0,0.0,
0.0, 0.5, 0.0, 0,0,0.0,1.0]
triangles = np.array(triangles, dtype=np.float32)
VBO = glGenBuffers(1)
glBindBuffer(GL_ARRAY_BUFFER, VBO)
glBufferData(GL_ARRAY_BUFFER, triangles.nbytes, triangles, GL_DYNAMIC_DRAW)
position = glGetAttribLocation(shaderProgram, 'position')
glVertexAttribPointer(position, 3, GL_FLOAT, GL_FALSE, 24, ctypes.c_void_p(0))
glEnableVertexAttribArray(position)
color = glGetAttribLocation(shaderProgram, 'color')
glVertexAttribPointer(color, 3, GL_FLOAT, GL_FALSE, 24, ctypes.c_void_p(12))
glEnableVertexAttribArray(color)
def render():
global shaderProgram
global angle
#shader
glUseProgram(shaderProgram)
glClearColor(0, 0, 0, 1)
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
#transform matrix
transform = glm.mat4(1)
transform = glm.translate(transform, glm.vec3(0.5,-0.5,0.0))
transform = glm.rotate(transform, glutGet(GLUT_ELAPSED_TIME),glm.vec3(0,0,1))
transformLoc = glGetUniformLocation(shaderProgram,"transform")
glUniformMatrix4fv(transformLoc, 1, GL_FALSE, glm.value_ptr(transform))
#render program
glDrawArrays(GL_TRIANGLES, 0, 3)
glUseProgram(0)
glutSwapBuffers()
def main():
glutInit([])
glutInitWindowSize(640, 480)
glutCreateWindow("pyopengl with glut 2")
initliaze()
glutDisplayFunc(render)
glutMainLoop()
if __name__ == '__main__':
main()
In VERTEX_SHADER you didn't mentioned transform variable. So your triangle position remain fixed after you run the program. Change your VERTEX_SHADER as following.
VERTEX_SHADER = """
#version 330
in vec4 position;
in vec3 color;
out vec3 newColor;
uniform mat4 transform;
void main()
{
gl_Position = transform*position;
newColor = color;
}
"""
In your code you are accessing the location of location of a uniform variable transform by following line.
transformLoc = glGetUniformLocation(shaderProgram,"transform")
You should add glutPostRedisplay() function after the glutSwapBuffers() function to visualize the continuous change.
Looks like you will want to create your own library from GLM. What you're doing in the code above no longer works. As another user stated, this is a good template to build functionality from. I'd suggest downloading GLM, taking it apart, and reverse engineering what you need into Python.
Related
I have separated the program to three different files, but I don't understand why I get error on glVertexAttribPointer on line 70. I'm using Python 3.10.8
main.py
import glfw
import Shaders
from OpenGL.GL import *
from OpenGL.GLUT import *
from Math_3d import Vector2f
class Window:
def __init__(self, width: int, height: int, title: str):
if not glfw.init():
raise Exception("glfw can not be initialized")
glfw.window_hint(glfw.CONTEXT_VERSION_MAJOR, 3)
glfw.window_hint(glfw.CONTEXT_VERSION_MINOR, 3)
glfw.window_hint(glfw.OPENGL_PROFILE, glfw.OPENGL_CORE_PROFILE)
self._win = glfw.create_window(width, height, title, None, None)
if not self._win:
glfw.terminate()
raise Exception("glfw window can not be created")
glfw.set_window_pos(self._win, 400, 200)
glfw.make_context_current(self._win)
def createshaders(self):
# Request program and shader slots from the GPU
program = glCreateProgram()
vertex = glCreateShader(GL_VERTEX_SHADER)
fragment = glCreateShader(GL_FRAGMENT_SHADER)
# Set shader sources
glShaderSource(vertex, Shaders.vertex_code)
glShaderSource(fragment, Shaders.fragment_code)
# Compile shaders
glCompileShader(vertex)
glCompileShader(fragment)
if not glGetShaderiv(vertex, GL_COMPILE_STATUS):
report_shader = glGetShaderInfoLog(vertex)
print(report_shader)
raise RuntimeError("Vertex shader compilation error")
if not glGetShaderiv(fragment, GL_COMPILE_STATUS):
report_frag = glGetShaderInfoLog(fragment)
print(report_frag)
raise RuntimeError("Fragment shader compilation error")
# Link objects to program
glAttachShader(program, vertex)
glAttachShader(program, fragment)
glLinkProgram(program)
if not glGetProgramiv(program, GL_LINK_STATUS):
print(glGetProgramInfoLog(program))
raise RuntimeError('Linking error')
# Get rid of shaders
glDetachShader(program, vertex)
glDetachShader(program, fragment)
# Make default program to run
glUseProgram(program)
# Vertex Buffer Object
# Create point vertex data
v2f_1 = Vector2f(0.0, 0.0)
# Request a buffer slot from GPU
buffer = glGenBuffers(1)
# Make this buffer the default one
glBindBuffer(GL_ARRAY_BUFFER, buffer)
strides = v2f_1.data.strides[0]
loc = glGetAttribLocation(program, 'position')
glEnableVertexAttribArray(loc)
glVertexAttribPointer(loc, 2, GL_FLOAT, False, strides, None)
# glBufferData(GL_ARRAY_BUFFER, v2f_1, v2f_1, GL_DYNAMIC_DRAW)
def renderscene(self):
while not glfw.window_should_close(self._win):
glfw.poll_events()
glClear(GL_COLOR_BUFFER_BIT)
glDrawArrays(GL_POINTS, 0, 1)
glfw.swap_buffers(self._win)
glfw.terminate()
if __name__ == '__main__':
win = Window(1024, 768, "GLFW Window")
win.createshaders() # Create and initialize shaders and initialize Vertex Buffer Object
win.renderscene() # Swap buffer and render scene
Shaders.py
vertex_code = """
attribute vec2 position;
void main()
{
gl_Position = vec4(position, 0.0, 1.0);
}
"""
fragment_code = """
void main()
{
gl_FragColor = vec4(0.0, 0.0, 0.0, 1.0);
}
"""
Math_3d.py
import numpy as np
class Vector2f:
def __init__(self, x, y):
self.data = np.array([x, y], dtype=np.float32)
if __name__ == '__main__':
vec2 = Vector2f(0.0, 0.0)
print(vec2.data)
print(type(vec2.data.strides[0]))
print(vec2.data.strides[0])
I have tried to debug the line 70, but did not get any good result while using PyCharm.
Any recommendations on this? Closest answers would be according to 61491497 and 56957118 what I am aiming for.
You're using a Core profile OpenGL Context (glfw.OPENGL_CORE_PROFILE). Therefore you have to create a Vertex Array Obejct:
class Window:
# [...]
def createshaders(self):
# [...]
v2f_1 = Vector2f(0.0, 0.0)
# Request a buffer slot from GPU
buffer = glGenBuffers(1)
glBindBuffer(GL_ARRAY_BUFFER, buffer)
strides = v2f_1.data.strides[0]
glBufferData(GL_ARRAY_BUFFER, v2f_1.data, GL_DYNAMIC_DRAW)
vao = glGenVertexArrays(1)
glBindVertexArray(vao)
loc = glGetAttribLocation(program, 'position')
glEnableVertexAttribArray(loc)
glVertexAttribPointer(loc, 2, GL_FLOAT, False, strides, None)
Additionally, you need to change either the background color or the fragment color, because you won't be able to see a black point on a black background. e.g. red:
gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
I'm considering switching to ModernGL over PyOpenGL, and I'm struggling to implement anything right now.
First of all, I would like to try the classic "triangle that changes shape using a time uniform and sine function", but I'm stuck on how to write to the uniform.
Here is what the documentation says about this:
A uniform is a global GLSL variable declared with the “uniform” storage qualifier. These act as
parameters that the user of a shader program can pass to that program.
In ModernGL, Uniforms can be accessed using Program.__getitem__() or Program.__iter__().
# Set a vec4 uniform
uniform['color'] = 1.0, 1.0, 1.0, 1.0
# Optionally we can store references to a member and set the value directly
uniform = program['color']
uniform.value = 1.0, 0.0, 0.0, 0.0
uniform = program['cameraMatrix']
uniform.write(camera_matrix)
This is my code:
import moderngl as mgl
import glfw
import numpy as np
import time
from math import sin
glfw.init()
glfw.window_hint(glfw.CONTEXT_VERSION_MAJOR, 3)
glfw.window_hint(glfw.CONTEXT_VERSION_MINOR, 3)
glfw.window_hint(glfw.OPENGL_PROFILE, glfw.OPENGL_CORE_PROFILE)
window = glfw.create_window(800, 600, "__DELETEME__", None, None)
glfw.make_context_current(window)
context = mgl.create_context()
vertex_source = """
#version 330 core
in vec2 aPos;
uniform float time;
void main() {
gl_Position = vec4(aPos.x, aPos.y + sin(time), 0.0, 1.0);
}
"""
fragment_source = """
#version 330 core
out vec4 color;
void main(){
color = vec4(0.0, 0.0, 1.0, 1.0);
}
"""
program = context.program(vertex_shader=vertex_source, fragment_shader=fragment_source)
data = np.array([
0.5, 0,
-0.5, 0,
0, 0.5], dtype = "float32")
vbo = context.buffer(data.tobytes())
vao = context.vertex_array(program, vbo, "aPos")
uniform = program["time"]
uniform.value = 1.0
while not glfw.window_should_close(window):
now = time.time()
vao.render()
elapsed = time.time() - now
glfw.poll_events()
glfw.swap_buffers(window)
glfw.terminate()
Now it draws nothing. What am I doing wrong? Thanks!
The elapsed time is the difference between the start time and the current time. Get the start time before the application loop and compute the elapsed time in the loop in every frame:
start_time = time.time()
while not glfw.window_should_close(window):
elapsed = time.time() - start_time
# [...]
The value of the uniform "time" has to be updated continuously in the loop:
while not glfw.window_should_close(window):
# [...]
uniform.value = elapsed
You have to clear the display in every frame, in the application loop (See ModernGL Context):
while not glfw.window_should_close(window):
# [...]
context.clear(0.0, 0.0, 0.0)
vao.render()
Complete example:
import moderngl as mgl
import glfw
import numpy as np
import time
from math import sin
glfw.init()
glfw.window_hint(glfw.CONTEXT_VERSION_MAJOR, 3)
glfw.window_hint(glfw.CONTEXT_VERSION_MINOR, 3)
glfw.window_hint(glfw.OPENGL_PROFILE, glfw.OPENGL_CORE_PROFILE)
window = glfw.create_window(800, 600, "__DELETEME__", None, None)
glfw.make_context_current(window)
context = mgl.create_context()
vertex_source = """
#version 330 core
in vec2 aPos;
uniform float time;
void main() {
gl_Position = vec4(aPos.x, aPos.y + sin(time), 0.0, 1.0);
}
"""
fragment_source = """
#version 330 core
out vec4 color;
void main(){
color = vec4(0.0, 0.0, 1.0, 1.0);
}
"""
program = context.program(vertex_shader=vertex_source, fragment_shader=fragment_source)
data = np.array([
0.5, 0,
-0.5, 0,
0, 0.5], dtype = "float32")
vbo = context.buffer(data.tobytes())
vao = context.vertex_array(program, vbo, "aPos")
uniform = program["time"]
uniform.value = 1.0
start_time = time.time()
while not glfw.window_should_close(window):
elapsed = time.time() - start_time
uniform.value = elapsed
context.clear(0.0, 0.0, 0.0)
vao.render()
glfw.poll_events()
glfw.swap_buffers(window)
glfw.terminate()
I've seen many minimal PyOpenGL examples, but none of the ones I've found make use of VAOs or glDrawArrays / glDrawElements. Instead they all use glVertex, glut shapes, and occasionally the old glCallList function.
I'm now trying to write a minimal working example that uses vertex arrays and buffers for vertex data. If I understand PyOpenGL correctly, the following code should draw a single white triangle on the screen, but instead I get a blank screen.
What might I be doing incorrectly? I have also had a bit of difficulty making sense of the PyOpenGL documentation. Many of the functions are defined one way, but are used in many places and work a totally different way, and some don't even seem to work the way they're defined (e.g. glGenBuffers which states it accepts two arguments, but seems to only accept one and return the generated buffers).
p.s. I'm not using numpy nor pygame.
from OpenGL.GLUT import *
from OpenGL.GLU import *
from OpenGL.GL import *
import ctypes
import sys
name = 'PyOpenGL Example'
vao = None
program = None
def main():
glutInit(sys.argv)
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH)
glutInitContextVersion(4,0)
glutInitWindowSize(600,400)
glutCreateWindow(name)
print(glGetString(GL_VERSION))
glClearColor(0,0,0,1)
glutDisplayFunc(display)
# glutMouseFunc(callback)
# glutMotionFunc(callback)
# glutPassiveMotionFunc(callback)
# glutKeyboardFunc(callback)
# glutSpecialFunc(callback)
vshader = glCreateShader(GL_VERTEX_SHADER)
fshader = glCreateShader(GL_FRAGMENT_SHADER)
glShaderSource(vshader,["""
#version 400
uniform mat4 u_model;
uniform mat4 u_view;
in vec4 a_pos;
in vec4 a_color;
in vec4 a_normal;
out vec4 v_color;
out vec4 v_normal;
void main() {
gl_Position = a_pos; // * u_model * u_view;
v_color = a_color;
v_normal = normalize(a_normal);
}
"""])
glCompileShader(vshader)
msg = glGetShaderInfoLog(vshader)
if msg:
print(f"Failed to compile Vertex Shader: {msg}")
exit(0)
glShaderSource(fshader,["""
#version 400
uniform mat4 u_model;
uniform mat4 u_view;
in vec4 v_color;
in vec4 v_normal;
layout(location=0) out vec4 f_color;
void main() {
f_color = v_color;
}
"""])
glCompileShader(fshader)
msg = glGetShaderInfoLog(fshader)
if msg:
print(f"Failed to compile Fragment Shader: {msg}")
exit(0)
global program
program = glCreateProgram()
glAttachShader(program,vshader)
glAttachShader(program,fshader)
glLinkProgram(program)
msg = glGetProgramInfoLog(program)
if msg:
print(f"Failed to link Program: {msg}")
exit(0)
glUseProgram(program)
uniforms = {
'model': glGetUniformLocation(program,'u_model'),
'view': glGetUniformLocation(program,'u_view'),
}
print(uniforms)
attrs = {
'pos': glGetAttribLocation(program,'a_pos'),
'color': glGetAttribLocation(program,'a_color'),
'normal': glGetAttribLocation(program,'a_normal'),
}
print(attrs)
global vao
vao = glGenVertexArrays(1)
glBindVertexArray(vao)
verts = [
-1, -1, 0, 1,
1, -1, 0, 1,
0, 1, 0, 1,
]
colors = [
1, 1, 1, 1,
1, 1, 1, 1,
1, 1, 1, 1,
]
normals = [
0, 0, 1, 0,
0, 0, 1, 0,
0, 0, 1, 0,
]
vbuf,cbuf,nbuf = glGenBuffers(3)
glBindBuffer(GL_ARRAY_BUFFER,vbuf)
glBufferData(GL_ARRAY_BUFFER,(ctypes.c_float*len(verts))(*verts),GL_STATIC_DRAW)
glVertexAttribPointer(attrs['pos'],4,GL_FLOAT,GL_FALSE,0,0)
glEnableVertexAttribArray(attrs['pos'])
glBindBuffer(GL_ARRAY_BUFFER,cbuf)
glBufferData(GL_ARRAY_BUFFER,(ctypes.c_float*len(colors))(*colors),GL_STATIC_DRAW)
glVertexAttribPointer(attrs['color'],4,GL_FLOAT,GL_FALSE,0,0)
glEnableVertexAttribArray(attrs['color'])
glBindBuffer(GL_ARRAY_BUFFER,nbuf)
glBufferData(GL_ARRAY_BUFFER,(ctypes.c_float*len(normals))(*normals),GL_STATIC_DRAW)
# glVertexAttribPointer(attrs['normal'],4,GL_FLOAT,GL_FALSE,0,0)
# glEnableVertexAttribArray(attrs['normal'])
glBindBuffer(GL_ARRAY_BUFFER,0)
glBindVertexArray(0)
identity = [
1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1,
]
# glUniformMatrix4fv(uniforms['model'],1,GL_FALSE,(ctypes.c_float*16)(*identity))
# glUniformMatrix4fv(uniforms['view'],1,GL_FALSE,(ctypes.c_float*16)(*identity))
glutMainLoop()
return
def display():
glUseProgram(program)
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT)
glViewport(0,0,600,400)
print(vao)
glBindVertexArray(vao)
glDrawArrays(GL_TRIANGLES,0,3)
glBindVertexArray(0)
glutSwapBuffers()
return
def callback(*args):
print(*args)
if __name__ == '__main__': main()
If a named array buffer object is bound then the last parameter (6th parameter) of glVertexAttribPointer is treated as a byte offset into the buffer object's data store. The data type of the parameter has to be ctypes.c_void_p.
This means you have to use a ctypes.cast:
e.g.
glVertexAttribPointer(attrs['pos'], 4, GL_FLOAT, GL_FALSE, 0,
ctypes.cast(0, ctypes.c_void_p))
or None:
glVertexAttribPointer(attrs['pos'], 4, GL_FLOAT, GL_FALSE, 0, None)
Further note, if you do matrix transformation in the GLSL code, then the vector has to be multiplied to the matrix from the right.
See GLSL Programming/Vector and Matrix Operations
e.g.
gl_Position = u_view * u_model * a_pos;
I am using Vispy to rotate a cube. I can use imageio to use a custom texture in the face of the rotating cube successfully, but I don't understand how to use a movie or gif image.
Full source code:
import numpy as np
from vispy import gloo, app
from vispy.gloo import Program, VertexBuffer, IndexBuffer
from vispy.util.transforms import perspective, translate, rotate
from vispy.geometry import create_cube
import imageio
im = imageio.imread('C:\\vhosts\\VIDEO_TWO_CLONE\\shape.jpg')
vertex = """
uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;
uniform sampler2D texture;
attribute vec3 position;
attribute vec2 texcoord;
attribute vec3 normal;
attribute vec4 color;
varying vec2 v_texcoord;
void main()
{
gl_Position = projection * view * model * vec4(position,1.0);
v_texcoord = texcoord;
}
"""
fragment = """
uniform sampler2D texture;
varying vec2 v_texcoord;
void main()
{
gl_FragColor = texture2D(texture, v_texcoord);
}
"""
def checkerboard(grid_num=8, grid_size=32):
row_even = grid_num // 2 * [0, 1]
row_odd = grid_num // 2 * [1, 0]
Z = np.row_stack(grid_num // 2 * (row_even, row_odd)).astype(np.uint8)
return 255 * Z.repeat(grid_size, axis=0).repeat(grid_size, axis=1)
class Canvas(app.Canvas):
def __init__(self):
app.Canvas.__init__(self, size=(750, 750), title='Textured cube',
keys='interactive')
self.timer = app.Timer('auto', self.on_timer)
# Build cube data
V, I, _ = create_cube()
vertices = VertexBuffer(V)
self.indices = IndexBuffer(I)
# Build program
self.program = Program(vertex, fragment)
self.program.bind(vertices)
# Build view, model, projection & normal
view = translate((0, 0, -5))
model = np.eye(4, dtype=np.float32)
self.program['model'] = model
self.program['view'] = view
self.program['texture'] = im # checkerboard()
self.activate_zoom()
self.phi, self.theta = 0, 0
# OpenGL initalization
gloo.set_state(clear_color=(0.30, 0.30, 0.35, 1.00), depth_test=True)
self.timer.start()
self.show()
def on_draw(self, event):
gloo.clear(color=True, depth=True)
self.program.draw('triangles', self.indices)
def on_resize(self, event):
self.activate_zoom()
def activate_zoom(self):
gloo.set_viewport(0, 0, *self.physical_size)
projection = perspective(45.0, self.size[0] / float(self.size[1]),
2.0, 10.0)
self.program['projection'] = projection
def on_timer(self, event):
self.theta += .5
self.phi += .5
self.program['model'] = np.dot(rotate(self.theta, (0, 1, 0)),
rotate(self.phi, (0, 1, 0)))
self.update()
if __name__ == '__main__':
c = Canvas()
app.run()
A gif or a movie is essentially many image frames, coming one after the other.
What you would need to do is read the images from the GIF into an array containing the RGB(A) values of each image (for GIF, you can use the code from e.g. https://gist.github.com/BigglesZX/4016539), and then advance self.program['texture'] to the next frame of the GIF in on_timer.
In case you want to loop the texture, you could use the modulo operator %, so you don't run over the array boundaries, with something like
# gif_array is the array populated with GIF frames
self.program['texture'] = gif_array[index % (len(gif_array) - 1)]
index = index + 1
Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
Questions asking us to recommend or find a tool, library or favorite off-site resource are off-topic for Stack Overflow as they tend to attract opinionated answers and spam. Instead, describe the problem and what has been done so far to solve it.
Closed 9 years ago.
Improve this question
I am looking for a simple modern OpenGL (3.2+)example in Python.
I tried with GLUT and freeGLUT, but I am not able to get a 3.2 context on OS X (Mavericks). (This seems to be a known issue with GLUT/freeGLUT).
GLFW seems to be a modern lightweight alternative to GLUT, but it doesn't seem to have an official Python binding, and I could not find a simple example that uses 3.2 core profile features of OpenGL with GLFW and Python.
(I struggled with this problem, and so it could be useful for others, I am answering below as per SO guidelines.)
The code below uses PyOpenGL, PIL (for textures), numpy, GLFW and the corresponding Python binding cyglfw3.
Here is a screenshot of the output:
The main code is appended below. It uses some utility methods from a file called glutils.py (for loading texture, compiling shaders, etc.) which you can find here:
https://github.com/electronut/pp/tree/master/simplegl
Code listing follows:
import OpenGL
from OpenGL.GL import *
from OpenGL.GLUT import *
import numpy, math, sys, os
import glutils
import cyglfw3 as glfw
strVS = """
#version 330 core
layout(location = 0) in vec3 aVert;
uniform mat4 uMVMatrix;
uniform mat4 uPMatrix;
uniform vec4 uColor;
uniform float uTheta;
out vec4 vCol;
out vec2 vTexCoord;
void main() {
// rotational transform
mat4 rot = mat4(
vec4( cos(uTheta), sin(uTheta), 0.0, 0.0),
vec4(-sin(uTheta), cos(uTheta), 0.0, 0.0),
vec4(0.0, 0.0, 1.0, 0.0),
vec4(0.0, 0.0, 0.0, 1.0)
);
// transform vertex
gl_Position = uPMatrix * uMVMatrix * rot * vec4(aVert, 1.0);
// set color
vCol = vec4(uColor.rgb, 1.0);
// set texture coord
vTexCoord = aVert.xy + vec2(0.5, 0.5);
}
"""
strFS = """
#version 330 core
in vec4 vCol;
in vec2 vTexCoord;
uniform sampler2D tex2D;
uniform bool showCircle;
out vec4 fragColor;
void main() {
if (showCircle) {
// discard fragment outside circle
if (distance(vTexCoord, vec2(0.5, 0.5)) > 0.5) {
discard;
}
else {
fragColor = texture(tex2D, vTexCoord);
}
}
else {
fragColor = texture(tex2D, vTexCoord);
}
}
"""
class Scene:
""" OpenGL 3D scene class"""
# initialization
def __init__(self):
# create shader
self.program = glutils.loadShaders(strVS, strFS)
glUseProgram(self.program)
self.pMatrixUniform = glGetUniformLocation(self.program,
'uPMatrix')
self.mvMatrixUniform = glGetUniformLocation(self.program,
"uMVMatrix")
self.colorU = glGetUniformLocation(self.program, "uColor")
# color
self.col0 = [1.0, 0.0, 0.0, 1.0]
# texture
self.tex2D = glGetUniformLocation(self.program, "tex2D")
# define quad vertices
quadV = [
-0.5, -0.5, 0.0,
0.5, -0.5, 0.0,
-0.5, 0.5, 0.0,
0.5, 0.5, 0.0
]
# set up vertex array object (VAO)
self.vao = glGenVertexArrays(1)
glBindVertexArray(self.vao)
# vertices
self.vertexBuffer = glGenBuffers(1)
glBindBuffer(GL_ARRAY_BUFFER, self.vertexBuffer)
vertexData = numpy.array(quadV, numpy.float32)
glBufferData(GL_ARRAY_BUFFER, 4*len(vertexData), vertexData,
GL_STATIC_DRAW)
# enable vertex array
glEnableVertexAttribArray(0)
# set buffer data
glBindBuffer(GL_ARRAY_BUFFER, self.vertexBuffer)
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, None)
# unbind VAO
glBindVertexArray(0)
# time
self.t = 0
# texture
self.texId = glutils.loadTexture('test.png')
# show circle?
self.showCircle = False
# step
def step(self):
# increment angle
self.t = (self.t + 1) % 360
# set shader angle in radians
glUniform1f(glGetUniformLocation(self.program, 'uTheta'),
math.radians(self.t))
# render
def render(self, pMatrix, mvMatrix):
# use shader
glUseProgram(self.program)
# set proj matrix
glUniformMatrix4fv(self.pMatrixUniform, 1, GL_FALSE, pMatrix)
# set modelview matrix
glUniformMatrix4fv(self.mvMatrixUniform, 1, GL_FALSE, mvMatrix)
# show circle?
glUniform1i(glGetUniformLocation(self.program, 'showCircle'),
self.showCircle)
# enable texture
glActiveTexture(GL_TEXTURE0)
glBindTexture(GL_TEXTURE_2D, self.texId)
glUniform1i(self.tex2D, 0)
# bind VAO
glBindVertexArray(self.vao)
# draw
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4)
# unbind VAO
glBindVertexArray(0)
class RenderWindow:
"""GLFW Rendering window class"""
def __init__(self):
# save current working directory
cwd = os.getcwd()
# initialize glfw - this changes cwd
glfw.Init()
# restore cwd
os.chdir(cwd)
# version hints
glfw.WindowHint(glfw.CONTEXT_VERSION_MAJOR, 3)
glfw.WindowHint(glfw.CONTEXT_VERSION_MINOR, 3)
glfw.WindowHint(glfw.OPENGL_FORWARD_COMPAT, GL_TRUE)
glfw.WindowHint(glfw.OPENGL_PROFILE, glfw.OPENGL_CORE_PROFILE)
# make a window
self.width, self.height = 640, 480
self.aspect = self.width/float(self.height)
self.win = glfw.CreateWindow(self.width, self.height, "test")
# make context current
glfw.MakeContextCurrent(self.win)
# initialize GL
glViewport(0, 0, self.width, self.height)
glEnable(GL_DEPTH_TEST)
glClearColor(0.5, 0.5, 0.5,1.0)
# set window callbacks
glfw.SetMouseButtonCallback(self.win, self.onMouseButton)
glfw.SetKeyCallback(self.win, self.onKeyboard)
glfw.SetWindowSizeCallback(self.win, self.onSize)
# create 3D
self.scene = Scene()
# exit flag
self.exitNow = False
def onMouseButton(self, win, button, action, mods):
#print 'mouse button: ', win, button, action, mods
pass
def onKeyboard(self, win, key, scancode, action, mods):
#print 'keyboard: ', win, key, scancode, action, mods
if action == glfw.PRESS:
# ESC to quit
if key == glfw.KEY_ESCAPE:
self.exitNow = True
else:
# toggle cut
self.scene.showCircle = not self.scene.showCircle
def onSize(self, win, width, height):
#print 'onsize: ', win, width, height
self.width = width
self.height = height
self.aspect = width/float(height)
glViewport(0, 0, self.width, self.height)
def run(self):
# initializer timer
glfw.SetTime(0.0)
t = 0.0
while not glfw.WindowShouldClose(self.win) and not self.exitNow:
# update every x seconds
currT = glfw.GetTime()
if currT - t > 0.1:
# update time
t = currT
# clear
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
# build projection matrix
pMatrix = glutils.perspective(45.0, self.aspect, 0.1, 100.0)
mvMatrix = glutils.lookAt([0.0, 0.0, -2.0], [0.0, 0.0, 0.0],
[0.0, 1.0, 0.0])
# render
self.scene.render(pMatrix, mvMatrix)
# step
self.scene.step()
glfw.SwapBuffers(self.win)
# Poll for and process events
glfw.PollEvents()
# end
glfw.Terminate()
# main() function
def main():
print 'starting simpleglfw...'
rw = RenderWindow()
rw.run()
# call main
if __name__ == '__main__':
main()