I am writing a opengl application that renders a simple green colored cube, but unfortunately it does not. So far the project consists of a camera and obviously, the cube. I do not think the underlying problem is the camera as the projection does not affect the vertices of the cube in any way and I have tested my camera with glm's camera.
I think the underlying problem is how I have setup my vertices and vbo, but I do not know how to fix this.
Here is my code to retrieve a cube model:
import numpy
import ctypes
from OpenGL.GL import *
from OpenGL.GL import shaders
class Model:
def __init__(self, app):
self.app = app
self.vertex_data = self.get_vertex_data()
self.vbo = self.get_vbo()
self.program = self.get_shader_program()
self.vao = self.get_vao()
self.m_model = self.get_model_matrix()
self.locations = {}
self.initialize_uniforms()
def get_model_matrix(self):
return numpy.matrix(numpy.identity(4), copy = False, dtype = numpy.float32)
def SetUniformLocation(self, program, variable):
self.locations[variable] = glGetUniformLocation(program, variable)
def get_vertex_data(self):
vertices = [(-1, -1, 1), ( 1, -1, 1), ( 1, 1, 1), (-1, 1, 1),
(-1, 1, -1), (-1, -1, -1), ( 1, -1, -1), ( 1, 1, -1)]
indices = [(0, 2, 3), (0, 1, 2),
(1, 7, 2), (1, 6, 7),
(6, 5, 4), (4, 7, 6),
(3, 4, 5), (3, 5, 0),
(3, 7, 4), (3, 2, 7),
(0, 6, 1), (0, 5, 6)]
vertex_data = self.get_data(vertices, indices)
return vertex_data
#staticmethod
def get_data(vertices, indices):
data = [vertices[index] for triangle in indices for index in triangle]
return numpy.array(data, dtype = numpy.float32)
def get_shader_program(self, vertex_shader_filename = 'vertex', fragment_shader_filename = 'fragment'):
with open(f'shaders/{vertex_shader_filename}.glsl') as file:
vertex_shader = file.read()
with open(f'shaders/{fragment_shader_filename}.glsl') as file:
fragment_shader = file.read()
vertex_shader = shaders.compileShader(vertex_shader, GL_VERTEX_SHADER)
fragment_shader = shaders.compileShader(fragment_shader, GL_FRAGMENT_SHADER)
program = shaders.compileProgram(vertex_shader, fragment_shader)
return program
def get_vbo(self):
vertex_data = self.vertex_data
# Generate a buffer and bind it to the program
vbo = glGenBuffers(1)
glBindBuffer(GL_ARRAY_BUFFER, vbo)
glBufferData(GL_ARRAY_BUFFER, 24 * vertex_data.nbytes, vertex_data, GL_STATIC_DRAW)
return vbo
def get_vao(self):
vertex_data = self.vertex_data
vao = glGenVertexArrays(1)
glBindVertexArray(vao)
glEnableVertexAttribArray(0)
glBindBuffer(GL_ARRAY_BUFFER, self.vbo)
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, ctypes.c_void_p(0))
return vao
def initialize_uniforms(self):
program = self.program
# Make sure the program exists
glUseProgram(program)
# Matrix uniforms
self.SetUniformLocation(program, "m_perspective")
self.SetUniformLocation(program, "m_view")
self.SetUniformLocation(program, "m_model")
self.SetUniformLocation(program, "rotation")
def render(self):
locations = self.locations
glBindVertexArray(self.vao)
glUniformMatrix4fv(locations["m_perspective"], 1, GL_FALSE, self.app.camera.m_perspective)
glUniformMatrix4fv(locations["m_view"], 1, GL_FALSE, self.app.camera.m_view)
glUniformMatrix4fv(locations["m_model"], 1, GL_FALSE, self.m_model)
glDrawArrays(GL_TRIANGLES, 0, len(self.vertex_data))
I strongly believe the error lies in the vbo but I do not know how to fix it.
Also here is the code to the camera in case I am wrong about my judegment:
import numpy
import math
class Camera:
FAR = 0.1
NEAR = 100
FOV = 90
def __init__(self, app, position = (0, 0, 1), target = (0, 0, 0)):
self.app = app
self.aspect_ratio = self.app.WINDOWWIDTH / self.app.WINDOWHEIGHT
self.position = numpy.array(position)
self.target = numpy.array(target)
self.forward = numpy.array([ 0, 0, -1])
self.right = numpy.array([ 1, 0, 0])
self.up = numpy.array([ 0, 1, 0])
self.m_perspective = self.get_perspective_matrix()
self.m_view = self.get_view_matrix()
def Normalize(self, v):
return v / numpy.linalg.norm(v)
def Dot(self, a, b):
return numpy.dot(a, b)
def Cross(self, a, b):
return numpy.cross(a, b)
def get_perspective_matrix(self):
fn = self.FAR + self.NEAR
f_n = self.FAR - self.NEAR
r = self.aspect_ratio
t = 1 / math.tan(math.radians(self.FOV) / 2)
perspective_matrix = numpy.matrix([
[ t/r, 0, 0, 0],
[ 0, t, 0, 0],
[ 0, 0, -fn/f_n, -1],
[ 0, 0, -2 * self.FAR * self.NEAR / f_n, 0]], dtype = numpy.float32)
return perspective_matrix
def get_view_matrix(self):
f = self.Normalize(self.position - self.target)
l = self.Normalize(self.Cross(self.up, f))
u = self.Cross(f, l)
dl = -self.Dot(l, self.position)
du = -self.Dot(u, self.position)
df = -self.Dot(f, self.position)
view_matrix = numpy.matrix([
[l[0], l[1], l[2], dl],
[u[0], u[1], u[2], du],
[f[0], f[1], f[2], df],
[ 0, 0, 0, 1]], dtype = numpy.float32)
return view_matrix
And the two respective shaders:
Vert:
# version 330 core
layout (location = 0) in vec3 in_position;
uniform mat4 m_perspective;
uniform mat4 m_view;
uniform mat4 m_model;
void main () {
gl_Position = m_view * m_perspective * m_model * vec4(in_position.xy, in_position.z - 2.5, 1.0);
}
Frag:
# version 330 core
layout (location = 0) out vec4 fragColor;
void main() {
vec3 color = vec3(0.0, 1.0, 0.0);
fragColor = vec4(color, 1.0);
}
Related
I am using PyQt5 QOpenGLWidget to simply draw some points on the screen with different colors. I prepared two datasets, each containing their coordinates and their colors, 4 arrays on total, and I want the program to paint one of the datasets for each 100ms (by using a QTimer). I put these 4 arrays into 4 VBOs, and for each paintGL() call I want to switch from a dataset to another, that is, if this frame uses VBO0(coordinate), VBO1(color), then next frame will use VBO2(coordinate), VBO3(color), and vice versa.
My idea is to use a VAO, and since VBO0/1 has exactly same structure with VBO2/3, for each new frame I can just rebind the VAO to another VBO. However this does not work as expected, so what is the correct way of achieving my goal?
Minimum Example:
import numpy as np
from PyQt5.QtWidgets import QOpenGLWidget, QApplication, QWidget, QHBoxLayout
from PyQt5.QtCore import QTimer
from OpenGL.GL import *
def compileShader(vsSource, fsSource):
vertexShader = glCreateShader(GL_VERTEX_SHADER)
glShaderSource(vertexShader, vsSource)
glCompileShader(vertexShader)
fragmentShader = glCreateShader(GL_FRAGMENT_SHADER)
glShaderSource(fragmentShader, fsSource)
glCompileShader(fragmentShader)
program = glCreateProgram()
glAttachShader(program, vertexShader)
glAttachShader(program, fragmentShader)
glLinkProgram(program)
glDeleteShader(vertexShader)
glDeleteShader(fragmentShader)
return program
class MyOpenGLWidget(QOpenGLWidget):
def __init__(self):
super().__init__()
def initializeGL(self):
# with open('./shaders/vertexShader.shader', 'r') as f:
# vsSource = f.read()
# with open('./shaders/fragmentShader.shader', 'r') as f:
# fsSource = f.read()
vsSource = """
#version 450 core
layout (location = 0) in vec4 position;
layout (location = 1) in vec4 color;
out vec4 vs_Color;
void main() {
gl_Position = position;
gl_PointSize = 5.;
vs_Color = color;
}
"""
fsSource = """
#version 450 core
out vec4 FragColor;
in vec4 vs_Color;
void main() {
FragColor = vs_Color;
}
"""
self.program = compileShader(vsSource, fsSource)
self.initBuffers()
self.timer = QTimer(self)
self.timer.timeout.connect(self.onTimeout)
self.timer.start(100)
self.dataset = 1
def paintGL(self):
bgColor = np.array([0, 0, 0, 1], dtype=np.float32)
glClearBufferfv(GL_COLOR, 0, bgColor)
glUseProgram(self.program)
glEnable(GL_PROGRAM_POINT_SIZE)
glDrawArrays(GL_POINTS, 0, self.n)
def generateRandomData(self, n, mode='vertex'):
if mode == 'vertex':
dx = np.random.rand(n) * 2 - 1
dy = np.random.rand(n) * 2 - 1
zeros = np.zeros(n)
ones = np.ones(n)
data = np.vstack([dx, dy, zeros, ones]).T.astype(np.float32)
return data.flatten()
elif mode == 'color':
r = np.random.rand(n)
g = np.random.rand(n)
b = np.random.rand(n)
ones = np.ones(n)
data = np.vstack([r, g, b, ones]).T.astype(np.float32)
return data.flatten()
def initBuffers(self):
self.n = 100
self.vertexData1 = self.generateRandomData(self.n, mode='vertex')
self.colorData1 = self.generateRandomData(self.n, mode='color')
self.vertexData2 = self.generateRandomData(self.n, mode='vertex')
self.colorData2 = self.generateRandomData(self.n, mode='color')
self.buffers = np.empty(4, dtype=np.uint32)
glCreateBuffers(4, self.buffers)
for buffer in self.buffers:
glBindBuffer(GL_ARRAY_BUFFER, buffer)
glNamedBufferStorage(buffer, self.vertexData1.nbytes, None,
GL_DYNAMIC_STORAGE_BIT)
glNamedBufferSubData(self.buffers[0], 0, self.vertexData1.nbytes, self.vertexData1)
glNamedBufferSubData(self.buffers[1], 0, self.colorData1.nbytes, self.colorData1)
glNamedBufferSubData(self.buffers[2], 0, self.vertexData2.nbytes, self.vertexData2)
glNamedBufferSubData(self.buffers[3], 0, self.colorData2.nbytes, self.colorData2)
self.VAO = GLuint(0)
glCreateVertexArrays(1, self.VAO)
glEnableVertexArrayAttrib(self.VAO, 0)
glEnableVertexArrayAttrib(self.VAO, 1)
glVertexArrayAttribFormat(self.VAO, 0, 4, GL_FLOAT, GL_FALSE, 0)
glVertexArrayAttribFormat(self.VAO, 1, 4, GL_FLOAT, GL_FALSE, 0)
glVertexArrayAttribBinding(self.VAO, 0, 0)
glVertexArrayAttribBinding(self.VAO, 1, 1)
glVertexArrayVertexBuffer(self.VAO, 0, self.buffers[0], 0, 4 * 4)
glVertexArrayVertexBuffer(self.VAO, 1, self.buffers[1], 0, 4 * 4)
glBindVertexArray(self.VAO)
def onTimeout(self):
if self.dataset == 1:
glVertexArrayVertexBuffer(self.VAO, 0, self.buffers[2], 0, 4 * 4)
glVertexArrayVertexBuffer(self.VAO, 1, self.buffers[3], 0, 4 * 4)
self.dataset = 2
elif self.dataset == 2:
glVertexArrayVertexBuffer(self.VAO, 0, self.buffers[0], 0, 4 * 4)
glVertexArrayVertexBuffer(self.VAO, 1, self.buffers[1], 0, 4 * 4)
self.dataset = 1
self.update()
app = QApplication([])
win = QWidget()
win.showMaximized()
layout = QHBoxLayout()
win.setLayout(layout)
layout.addWidget(MyOpenGLWidget())
win.show()
app.exec_()
Expected Output:
Two datsets switches on the screen every 100ms (as set in QTimer) like:
Real Output:
First frame is correct, but after first switching there is a white triangle on the screen instead of points, and there's no further effect.
Rebind your vertex array object (self.VAO) each time in paintGL rather than just once in initBuffers. The official doco is not very clear, but these earlier answers suggest that changing attribs on a VAO probably won't take effect on the current binding.
When is What bound to a VAO?
Changing vertex array object
When I try to draw a cube using the GLSL programmable pipeline of OpenGL I get a fully yellow screen, which is the color of the cube, and even if I used glTranslatef() and tried to zoom out by any value, the screen is just fully yellow. How can I zoom out so I can see the entire cube, not just a pure yellow screen?
Full replicable code:
import time
import pygame
from OpenGL.GL import *
from OpenGL.GLU import *
from OpenGL.GLUT import *
from OpenGL.GL.shaders import *
import numpy
width = 500
height = 500
vertices = [(-1, -1, -1), (1, -1, -1), (1, 1, -1), (-1, 1, -1), (-1, -1, 1), (1, -1, 1), (1, 1, 1), (-1, 1, 1)]
faces = [(4, 0, 3, 7), (1, 0, 4, 5), (0, 1, 2, 3), (1, 5, 6, 2), (3, 2, 6, 7), (5, 4, 7, 6)]
def draw_shapes():
cube = []
for i, face in enumerate(faces):
for vertex in face:
cube.append(vertices[vertex])
cube = numpy.array(cube, dtype=numpy.float32)
vertex_shader = """
#version 140
in vec4 position;
void main(){
gl_Position = position;
}
"""
frag_shader = """
#version 140
void main(){
gl_FragColor = vec4(1.0f, 1.0f, 0.0f, 1.0f);
}
"""
shaders = compileProgram(compileShader(vertex_shader, GL_VERTEX_SHADER),
compileShader(frag_shader, GL_FRAGMENT_SHADER))
VBO = glGenBuffers(1)
glBindBuffer(GL_ARRAY_BUFFER, VBO)
glBufferData(GL_ARRAY_BUFFER, 192, cube, GL_STATIC_DRAW)
position = glGetAttribLocation(shaders, "position")
glVertexAttribPointer(position, 3, GL_FLOAT, GL_FALSE, 0, None)
glEnableVertexAttribArray(position)
glUseProgram(shaders)
def showScreen():
global width, height
glClearColor(0, 0, 0, 1)
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
glDrawArrays(GL_TRIANGLES, 0, 24)
draw_shapes()
glEnable(GL_DEPTH_TEST)
glEnable(GL_LIGHTING)
glEnable(GL_LIGHT0)
glEnable(GL_COLOR_MATERIAL)
glLightfv(GL_LIGHT0, GL_POSITION, (0, 0, 0, 1)) # point light from the left, top, front
glLightfv(GL_LIGHT0, GL_AMBIENT, (1, 1, 1, 1))
glutSwapBuffers()
def reshapeWindow(x, y):
global width, height
width = x
height = y
print(x, y)
glutReshapeWindow(width, height)
glMatrixMode(GL_PROJECTION)
glLoadIdentity()
gluPerspective(45, (width / height), 0.0001, 1000)
glViewport(0, 0, width, height)
glMatrixMode(GL_MODELVIEW)
glLoadIdentity()
glTranslatef(0, 0, -5)
glRotatef(3, 1, 0, 0)
glutInit()
glutInitDisplayMode(GLUT_RGBA)
glutInitWindowSize(500, 500)
wind = glutCreateWindow("OpenGL")
glutDisplayFunc(showScreen)
glutIdleFunc(showScreen)
glutReshapeFunc(reshapeWindow)
gluPerspective(45, (width / height), 0.0001, 100)
while True:
glutMainLoopEvent()
glutPostRedisplay()
time.sleep(0.01)
As already indicated in the comments by user253751 and derhass, the code mixes the fixed pipeline OpenGL approach with the programmable shader stages approach of modern OpengL, while it should be either one or the other. The legacy functions currently have no effect. It's probably best to start over using a book or tutorial on modern OpenGL, instead of converting a legacy project.
A modern OpenGL version of this project will involve:
Defining the transformation matrices (model/view/projection) on the CPU.
Uploading one or more transformation matrices as uniforms to the GPU.
Uploading the light parameters as uniforms to the GPU.
Multiplying each vertex with the transformation matrix using the vertex shader.
Manually programming the lighting/shading calculations in the vertex shader (for Gouraud shading) or fragment shader (for Blinn-Phong/Phong shading).
I am working on a project and I need to use texture arrays to apply textures. I have asked many questions about this, none of which I got an answer I was completely satisfied with (Get access to later versions of GLSL , OpenGL: Access Array Texture in GLSL , and OpenGL: How would I implement texture arrays?) so I'm asking a more broad question to hopefully get a response. Anyways, How would I texture an object in OpenGL (PyOpenGL more specifically, but it's fine if you put your answer in C++). I already have a way to load the texture arrays, just not a way to apply it. This is the desired result:
Image from opengl-tutorial
and this is what I currently have for loading array textures:
def load_texture_array(path,width,height):
teximg = pygame.image.load(path)
texels = teximg.get_buffer().raw
texture = GLuint(0)
layerCount = 6
mipLevelCount = 1
glGenTextures(1, texture)
glBindTexture(GL_TEXTURE_2D_ARRAY, texture)
glTexStorage3D(GL_TEXTURE_2D_ARRAY, mipLevelCount, GL_RGBA8, width, height, layerCount)
glTexSubImage3D(GL_TEXTURE_2D_ARRAY, 0, 0, 0, 0, width, height, layerCount, GL_RGBA, GL_UNSIGNED_BYTE, texels)
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_LINEAR)
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_LINEAR)
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)
TLDR: How would I apply textures to objects in OpenGL using texture arrays?
I will happily provide any other information if necessary.
If you want to use a 2D Array Texture for a cube, each of the 6 textures for the 6 side must be the same size.
You can lookup the texture by 3 dimensional texture coordinates. The 3rd component of the texture coordinate is the index of the 2d texture in the 2d texture array.
Hence the texture coordinates for the 6 sides are
0: [(0, 0, 0), (1, 0, 0), (1, 1, 0), (0, 1, 0)]
1: [(0, 0, 1), (1, 0, 1), (1, 1, 1), (0, 1, 1)]
2: [(0, 0, 2), (1, 0, 2), (1, 1, 2), (0, 1, 2)]
3: [(0, 0, 3), (1, 0, 3), (1, 1, 3), (0, 1, 3)]
4: [(0, 0, 4), (1, 0, 4), (1, 1, 4), (0, 1, 4)]
5: [(0, 0, 5), (1, 0, 5), (1, 1, 5), (0, 1, 5)]
Get the 3 dimensional texture coordinate attribute in the vertex shader and pass it to the fragment shader:
in a_uv;
out v_uv;
// [...]
void main()
{
v_uv = a_uv;
// [...]
}
Use the 3 dimensional texture coordinate to look up the sampler2DArray in the fragment shader:
out v_uv;
uniform sampler2DArray u_texture;
// [...]
void main()
{
vec4 texture(u_texture, v_uv.xyz);
// [...]
}
Create a GL_TEXTURE_2D_ARRAY and use glTexSubImage3D to load 6 2-dimensional images to the 6 planes of the 2D Array Texture. In the following image_planes is a list with the 6 2-dimensional image planes:
tex_obj = glGenTextures(1)
glBindTexture(GL_TEXTURE_2D_ARRAY, self.tex_obj)
glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_RGBA, sizeX, sizeY, 6, 0, GL_RGBA, GL_UNSIGNED_BYTE, None)
for i in range(6):
glTexSubImage3D(GL_TEXTURE_2D_ARRAY, 0, 0, 0, i, sizeX, sizeY, 1, GL_RGBA, GL_UNSIGNED_BYTE, image_planes[i])
glTexParameterf(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_LINEAR)
glTexParameterf(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_LINEAR)
See also PyGame and OpenGL 4.
Minimal example:
import os, math, ctypes
import glm
from OpenGL.GL import *
from OpenGL.GL.shaders import *
from OpenGL.arrays import *
import pygame
pygame.init()
image_path = r"images"
image_names = ["banana64.png", "apple64.png", "fish64.png", "rocket64.png", "ice64.png", "boomerang64.png"]
image_planes = [
(GLubyte * 4)(255, 0, 0, 255), (GLubyte * 4)(0, 255, 0, 255), (GLubyte * 4)(0, 0, 255, 255),
(GLubyte * 4)(255, 255, 0, 255), (GLubyte * 4)(0, 255, 255, 255), (GLubyte * 4)(255, 0, 255, 255)]
image_size = (1, 1)
for i, filename in enumerate(image_names):
try:
image = pygame.image.load(os.path.join(image_path, filename))
image_size = image.get_size()
image_planes[i] = pygame.image.tostring(image, 'RGBA')
except:
pass
class MyWindow:
__glsl_vert = """
#version 130
in vec3 a_pos;
in vec3 a_nv;
in vec3 a_uv;
out vec3 v_pos;
out vec3 v_nv;
out vec3 v_uv;
uniform mat4 u_proj;
uniform mat4 u_view;
uniform mat4 u_model;
void main()
{
mat4 model_view = u_view * u_model;
mat3 normal = mat3(model_view);
vec4 view_pos = model_view * vec4(a_pos.xyz, 1.0);
v_pos = view_pos.xyz;
v_nv = normal * a_nv;
v_uv = a_uv;
gl_Position = u_proj * view_pos;
}
"""
__glsl_frag = """
#version 130
out vec4 frag_color;
in vec3 v_pos;
in vec3 v_nv;
in vec3 v_uv;
uniform sampler2DArray u_texture;
void main()
{
vec3 N = normalize(v_nv);
vec3 V = -normalize(v_pos);
float ka = 0.1;
float kd = max(0.0, dot(N, V)) * 0.9;
vec4 color = texture(u_texture, v_uv.xyz);
frag_color = vec4(color.rgb * (ka + kd), color.a);
}
"""
def __init__(self, w, h):
self.__caption = 'OpenGL Window'
self.__vp_size = [w, h]
pygame.display.gl_set_attribute(pygame.GL_DEPTH_SIZE, 24)
self.__screen = pygame.display.set_mode(self.__vp_size, pygame.DOUBLEBUF| pygame.OPENGL)
self.__program = compileProgram(
compileShader( self.__glsl_vert, GL_VERTEX_SHADER ),
compileShader( self.__glsl_frag, GL_FRAGMENT_SHADER ),
)
self.___attrib = { a : glGetAttribLocation (self.__program, a) for a in ['a_pos', 'a_nv', 'a_uv'] }
print(self.___attrib)
self.___uniform = { u : glGetUniformLocation (self.__program, u) for u in ['u_model', 'u_view', 'u_proj'] }
print(self.___uniform)
v = [[-1,-1,1], [1,-1,1], [1,1,1], [-1,1,1], [-1,-1,-1], [1,-1,-1], [1,1,-1], [-1,1,-1]]
n = [[0,0,1], [1,0,0], [0,0,-1], [-1,0,0], [0,1,0], [0,-1,0]]
e = [[0,1,2,3], [1,5,6,2], [5,4,7,6], [4,0,3,7], [3,2,6,7], [1,0,4,5]]
t = [[0, 0], [1, 0], [1, 1], [0, 1]]
index_array = [si*4+[0, 1, 2, 0, 2, 3][vi] for si in range(6) for vi in range(6)]
attr_array = []
for si in range(len(e)):
for i, vi in enumerate(e[si]):
attr_array += [*v[vi], *n[si], *t[i], si]
self.__no_vert = len(attr_array) // 10
self.__no_indices = len(index_array)
vertex_attributes = (ctypes.c_float * len(attr_array))(*attr_array)
indices = (ctypes.c_uint32 * self.__no_indices)(*index_array)
self.__vao = glGenVertexArrays(1)
self.__vbo, self.__ibo = glGenBuffers(2)
glBindVertexArray(self.__vao)
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, self.__ibo)
glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices, GL_STATIC_DRAW)
glBindBuffer(GL_ARRAY_BUFFER, self.__vbo)
glBufferData(GL_ARRAY_BUFFER, vertex_attributes, GL_STATIC_DRAW)
float_size = ctypes.sizeof(ctypes.c_float)
glVertexAttribPointer(self.___attrib['a_pos'], 3, GL_FLOAT, False, 9*float_size, None)
glVertexAttribPointer(self.___attrib['a_nv'], 3, GL_FLOAT, False, 9*float_size, ctypes.c_void_p(3*float_size))
glVertexAttribPointer(self.___attrib['a_uv'], 3, GL_FLOAT, False, 9*float_size, ctypes.c_void_p(6*float_size))
glEnableVertexAttribArray(self.___attrib['a_pos'])
glEnableVertexAttribArray(self.___attrib['a_nv'])
glEnableVertexAttribArray(self.___attrib['a_uv'])
glEnable(GL_DEPTH_TEST)
glUseProgram(self.__program)
glActiveTexture(GL_TEXTURE0)
sizeX, sizeY = image_size
self.tex_obj = glGenTextures(1)
glBindTexture(GL_TEXTURE_2D_ARRAY, self.tex_obj)
glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_RGBA, sizeX, sizeY, 6, 0, GL_RGBA, GL_UNSIGNED_BYTE, None)
for i in range(6):
glTexSubImage3D(GL_TEXTURE_2D_ARRAY, 0, 0, 0, i, sizeX, sizeY, 1, GL_RGBA, GL_UNSIGNED_BYTE, image_planes[i])
glTexParameterf(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_LINEAR)
glTexParameterf(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_LINEAR)
def run(self):
self.__starttime = 0
self.__starttime = self.elapsed_ms()
run = True
while run:
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
self.__mainloop()
pygame.display.flip()
pygame.quit()
def elapsed_ms(self):
return pygame.time.get_ticks() - self.__starttime
def __mainloop(self):
proj, view, model = glm.mat4(1), glm.mat4(1), glm.mat4(1)
aspect = self.__vp_size[0]/self.__vp_size[1]
proj = glm.perspective(glm.radians(90.0), aspect, 0.1, 10.0)
view = glm.lookAt(glm.vec3(0,-3,0), glm.vec3(0, 0, 0), glm.vec3(0,0,1))
angle1 = self.elapsed_ms() * math.pi * 2 / 5000.0
angle2 = self.elapsed_ms() * math.pi * 2 / 7333.0
model = glm.rotate(model, angle1, glm.vec3(1, 0, 0))
model = glm.rotate(model, angle2, glm.vec3(0, 1, 0))
glUniformMatrix4fv(self.___uniform['u_proj'], 1, GL_FALSE, glm.value_ptr(proj) )
glUniformMatrix4fv(self.___uniform['u_view'], 1, GL_FALSE, glm.value_ptr(view) )
glUniformMatrix4fv(self.___uniform['u_model'], 1, GL_FALSE, glm.value_ptr(model) )
glClearColor(0.2, 0.3, 0.3, 1.0)
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
glDrawElements(GL_TRIANGLES, self.__no_indices, GL_UNSIGNED_INT, None)
window = MyWindow(800, 600)
window.run()
I feel kind of stupid for asking this, but I've tried almost every example and proved every solution.
I'm trying to create a endless runner game using pyOpenGL and pygames, and facing many troubles, one of them is the following: I'm trying to render 2 textures for differents objects using pyOpenGL glBindTexture(GL_TEXTURE_2D, id). I have a main object, that is a cube, called cube and I also have an array of cube. I want to render a texture in cube and a different one all the cubes in the array, but everytime I try to do this the last texture "rendered" overlaps the previus one. What could I do?
Here is my code:
# Execute with Python 3
import os
import pygame
import random
import math
from pygame.locals import *
from OpenGL.GL import *
from OpenGL.GLU import *
camera_x = 0
camera_z = 0
camera_y = 0
x = 0
Vertices = [
[1, -1, 1],
[-1, -1, 1],
[-1, -1, -1],
[1, -1, -1],
[1, 1, -1],
[-1, 1, -1],
[-1, 1, 1],
[1, 1, 1]
]
textureSurface = pygame.image.load('./textures/ursa.png'), pygame.image.load('./textures/caja.png')
textureData = pygame.image.tostring(textureSurface[0], "RGBA"), pygame.image.tostring(textureSurface[1], "RGBA")
width = textureSurface[0].get_width(), textureSurface[1].get_width()
height = textureSurface[0].get_height(), textureSurface[1].get_height()
class Ground:
global camera_z
def __init__(self):
self.ground_vertices = (
(-11, -2.01, 20),
(11, -2.01, 20),
(11, -2.01, -300),
(-11, -2.01, -300)
)
def draw(self):
glPushMatrix()
glTranslatef(0, 0, camera_z)
glBegin(GL_QUADS)
for vertex in self.ground_vertices:
glColor3fv((0, 0.5, 0.5))
glVertex3fv(vertex)
glEnd()
glPopMatrix()
class Cube:
def __init__(self, texture=False):
self.vertices = [
[1, -1, 1],
[-1, -1, 1],
[-1, -1, -1],
[1, -1, -1],
[1, 1, -1],
[-1, 1, -1],
[-1, 1, 1],
[1, 1, 1]
]
self.surfaces = (
(0, 1, 6, 7),
(0, 1, 2, 3),
(0, 3, 4, 7),
(1, 2, 6, 5),
(2, 3, 4, 5),
(4, 5, 6, 7)
)
self.colors = (
(105 / 255, 210 / 255, 231 / 255),
(167 / 255, 219 / 255, 216 / 255),
(224 / 255, 228 / 255, 204 / 255),
(243 / 255, 134 / 255, 48 / 255)
)
self.vertices_texture = (
(0.0, 0.0),
(1.0, 0.0),
(1.0, 1.0),
(0.0, 1.0),
)
self.texture = texture
self.center = [0, 0, 0]
def draw(self):
if self.texture:
glEnable(GL_TEXTURE_2D)
glColor3f(1, 1, 1)
glBegin(GL_QUADS)
if self.texture:
for surface in self.surfaces:
for x, vertex in enumerate(surface):
glTexCoord2fv(self.vertices_texture[x])
glVertex3fv(self.vertices[vertex])
else:
for surface in self.surfaces:
for x, vertex in enumerate(surface):
glColor3fv(self.colors[x])
glVertex3fv(self.vertices[vertex])
self.center = [
(self.vertices[2][0]+self.vertices[7][0])/2,
(self.vertices[2][1]+self.vertices[7][1])/2,
(self.vertices[2][2]+self.vertices[7][2])/2
]
glEnd()
if self.texture:
glDisable(GL_TEXTURE_2D)
def set_vertices(self, max_distance, min_distance=-40):
x_value_change = random.randrange(-10, 10)
y_value_change = -1
z_value_change = random.randrange(-1 * max_distance, min_distance)
new_vertices = []
for vertex in Vertices:
new_vertex = []
new_x = vertex[0] + x_value_change
new_y = vertex[1] + y_value_change
new_z = vertex[2] + z_value_change
new_vertex.append(new_x)
new_vertex.append(new_y)
new_vertex.append(new_z)
new_vertices.append(new_vertex)
self.vertices = new_vertices
def rotate(self):
glPushMatrix()
glRotatef(25, 1, 0, 0)
glPopMatrix()
def loadTexture(self, file):
glEnable(GL_TEXTURE_2D)
id = [0]*2
glGenTextures(2, id)
if file == 0:
glBindTexture(GL_TEXTURE_2D, id[0])
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width[0], height[0], 0, GL_RGBA, GL_UNSIGNED_BYTE, textureData[0])
if file == 1:
glBindTexture(GL_TEXTURE_2D, id[1])
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width[1], height[1], 0, GL_RGBA, GL_UNSIGNED_BYTE, textureData[1])
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)
glDisable(GL_TEXTURE_2D)
#return texid
def leave(event):
if event.type == pygame.QUIT or (event.type == pygame.KEYDOWN and event.key == K_ESCAPE):
pygame.quit()
quit()
def main():
global camera_x, camera_y, camera_z, x
pygame.init()
display = (800, 600)
pygame.display.set_mode(display, DOUBLEBUF | OPENGL | OPENGLBLIT)
max_distance = 300
gluPerspective(45, (display[0] / display[1]), 0.1, max_distance)
glEnable(GL_DEPTH_TEST)
glEnable(GL_BLEND)
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
glTranslatef(0, -10 / 2.4, -50)
ground = Ground()
cube = Cube(True)
cube.loadTexture(0)
my_cubes = []
for i in range(20):
tmp_cube = Cube(True)
tmp_cube.loadTexture(1)
tmp_cube.set_vertices(max_distance)
my_cubes.append(tmp_cube)
while True:
for event in pygame.event.get():
leave(event)
# movInverse(event)
M = glGetDoublev(GL_MODELVIEW_MATRIX)
# print(M)
camera_x = M[3][0]
camera_y = M[3][1]
camera_z = M[3][2]
glTranslatef(0, 0, 1.5)
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
ground.draw()
glPushMatrix()
if(math.fabs(x) < 11):
glTranslatef(x, -1.5, camera_z-20)
glScalef(1 / 2, 1 / 2, 0)
cube.draw()
glPopMatrix()
for tmp_cube in my_cubes:
tmp_cube.draw()
print(tmp_cube.center)
for tmp_cube in my_cubes:
if camera_z <= tmp_cube.vertices[0][2]:
new_max = int(-1 * (camera_z - max_distance * 2))
tmp_cube.set_vertices(new_max, int(camera_z - max_distance))
pygame.display.flip()
if __name__ == '__main__':
main()
The method loadTexture is supposed to choose what texture is going to apply, but it doesn't, I don't know why. I hope someone could help me. Thanks!
When each cube has its own texture, then add an attribute self.id , which holds the texture for the cube.
Create this texture object and load the texture in the method loadTexture():
class Cube:
# [...]
def loadTexture(self, file):
self.id = glGenTextures(1)
glBindTexture(GL_TEXTURE_2D, self.id)
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width[file], height[file], 0, GL_RGBA, GL_UNSIGNED_BYTE, textureData[file])
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)
Bind the texture before the cube is draw (glBindTexture):
class Cube:
# [...]
def draw(self):
if self.texture:
glEnable(GL_TEXTURE_2D)
glColor3f(1, 1, 1)
glBindTexture(GL_TEXTURE_2D, self.id) # <-----
glBegin(GL_QUADS)
# [...]
Given this snippet:
import textwrap
import math
import numpy as np
from ctypes import *
from OpenGL.GL import *
from OpenGL.GL.ARB.multitexture import *
from OpenGL.GL.ARB.debug_output import *
from OpenGL.GLU import *
from OpenGL.GLUT import *
def make_program(vs, fs):
id_program = glCreateProgram()
glAttachShader(id_program, vs)
glAttachShader(id_program, fs)
glLinkProgram(id_program)
result = glGetProgramiv(id_program, GL_LINK_STATUS)
if not(result):
raise RuntimeError(glGetProgramInfoLog(id_program))
return id_program
def make_fs(source):
id_fs = glCreateShader(GL_FRAGMENT_SHADER)
glShaderSource(id_fs, source)
glCompileShader(id_fs)
result = glGetShaderiv(id_fs, GL_COMPILE_STATUS)
if not(result):
raise Exception("Error: {0}".format(
glGetShaderInfoLog(id_fs)
))
return id_fs
def make_vs(source):
id_vs = glCreateShader(GL_VERTEX_SHADER)
glShaderSource(id_vs, source)
glCompileShader(id_vs)
result = glGetShaderiv(id_vs, GL_COMPILE_STATUS)
if not(result):
raise Exception("Error: {0}".format(
glGetShaderInfoLog(id_vs)
))
return id_vs
def v_length(a):
return math.sqrt(a[0] * a[0] + a[1] * a[1] + a[2] * a[2])
def v_normalize(a):
v = v_length(a)
inv_length = 1.0 / v_length(a)
return [a[0] * inv_length, a[1] * inv_length, a[2] * inv_length]
def v_cross(a, b):
return [
a[1] * b[2] - b[1] * a[2],
a[2] * b[0] - b[2] * a[0],
a[0] * b[1] - b[0] * a[1]
]
def identity():
return [
1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1
]
def v_dot(a, b):
return a[0] * b[0] + a[1] * b[1] + a[2] * b[2]
def transpose(a):
return [
a[0], a[4], a[8], a[12],
a[1], a[5], a[9], a[13],
a[2], a[6], a[10], a[14],
a[3], a[7], a[11], a[15]
]
def perspective(fovy, aspect, znear, zfar):
tan_half_fovy = math.tan(fovy / 2.0)
m00 = 1.0 / (aspect * tan_half_fovy)
m11 = 1.0 / (tan_half_fovy)
m22 = - (zfar + znear) / (zfar - znear)
m23 = -1.0
m32 = -(2.0 * zfar * znear) / (zfar - znear)
return transpose([
m00, 0.0, 0.0, 0.0,
0.0, m11, 0.0, 0.0,
0.0, 0.0, m22, m32,
0.0, 0.0, m23, 0.0
])
def lookat(eye, target, up):
zaxis = v_normalize(
[target[0] - eye[0], target[1] - eye[1], target[2] - eye[2]]
)
xaxis = v_normalize(v_cross(zaxis, up))
yaxis = v_cross(xaxis, zaxis)
return transpose([
xaxis[0], xaxis[1], xaxis[2], -v_dot(xaxis, eye),
yaxis[0], yaxis[1], yaxis[2], -v_dot(yaxis, eye),
-zaxis[0], -zaxis[1], -zaxis[2], v_dot(zaxis, eye),
0, 0, 0, 1
])
def add_line(data, p0, p1, color):
data += [p0[0], p0[1], p0[2], color[0], color[1], color[2]]
data += [p1[0], p1[1], p1[2], color[0], color[1], color[2]]
def make_axis(k=25.0):
data = []
add_line(data, [k, 0.0, 0.0], [0, 0, 0], [1.0, 0.0, 0.0])
add_line(data, [0.0, k, 0.0], [0, 0, 0], [0.0, 1.0, 0.0])
add_line(data, [-k, 0.0, 0.0], [0, 0, 0], [0.0, 0.0, 1.0])
data = np.array(data, dtype=np.float32)
print("len(data)={} bytes(data)={}".format(
len(data), ArrayDatatype.arrayByteCount(data)))
vao = glGenVertexArrays(1)
glBindVertexArray(vao)
vbo = glGenBuffers(1)
glBindBuffer(GL_ARRAY_BUFFER, vbo)
glBufferData(
GL_ARRAY_BUFFER,
ArrayDatatype.arrayByteCount(data),
data, GL_STATIC_DRAW
)
glEnableVertexAttribArray(0)
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * 4, c_void_p(0))
glEnableVertexAttribArray(1)
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * 4, c_void_p(3 * 4))
glBindBuffer(GL_ARRAY_BUFFER, 0)
glBindVertexArray(0)
return vao
def draw_axis(id_vao):
glBindVertexArray(id_vao)
glDrawArrays(GL_LINES, 0, 6)
glBindVertexArray(0)
class Mcve():
def __init__(self, window_name, width, height):
self.window_width = width
self.window_height = height
self.window_name = window_name
def init(self):
glEnable(GL_DEPTH_TEST)
glEnable(GL_TEXTURE_2D)
glClearColor(0.0, 0.0, 0.0, 0.0)
self.prog_axis = make_program(
make_vs(textwrap.dedent("""
#version 410
layout (location = 0) in vec2 a_position;
layout (location = 1) in vec3 a_color;
out vec3 color;
uniform mat4 projection;
uniform mat4 view;
uniform mat4 model;
void main () {
color=a_color;
gl_Position = projection*view*model*vec4(a_position, 0.0, 1.0);
}
""")),
make_fs(textwrap.dedent("""
#version 410
in vec3 color;
out vec4 frag_colour;
void main () {
frag_colour = vec4(color,1.0);
}
"""))
)
self.axis = make_axis()
def display(self):
glClearColor(1.0, 1.0, 1.0, 1.0)
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
eye = [50.0, 50.0, 50.0]
target = [0, 0, 0]
up = [0, 1, 0]
view = lookat(eye, target, up)
projection = perspective(45.0, 1.0, 0.1, 1000.0)
prog = self.prog_axis
glUseProgram(prog)
glUniformMatrix4fv(glGetUniformLocation(prog, "model"), 1, False,
np.array(identity(), dtype=np.float32)
)
glUniformMatrix4fv(glGetUniformLocation(prog, "view"), 1, False,
np.array(view, dtype=np.float32)
)
glUniformMatrix4fv(glGetUniformLocation(prog, "projection"), 1, False,
np.array(projection, dtype=np.float32)
)
draw_axis(self.axis)
glUseProgram(0)
glutSwapBuffers()
def reshape(self, w, h):
self.window_width = w
self.window_height = h
glViewport(0, 0, w, h)
def animate(self):
glutPostRedisplay()
def visible(self, vis):
if (vis == GLUT_VISIBLE):
glutIdleFunc(self.animate)
else:
glutIdleFunc(0)
def key_pressed(self, *args):
key = args[0].decode("utf8")
if key == "\x1b":
sys.exit()
def run(self):
glutInit(sys.argv)
glutInitDisplayMode(
GLUT_RGBA | GLUT_DOUBLE | GLUT_ALPHA | GLUT_DEPTH | GLUT_MULTISAMPLE)
glutInitWindowSize(self.window_width, self.window_height)
glutInitWindowPosition(800, 100)
glutCreateWindow(self.window_name)
glutDisplayFunc(self.display)
glutReshapeFunc(self.reshape)
glutIdleFunc(self.animate)
glutVisibilityFunc(self.visible)
glutKeyboardFunc(self.key_pressed)
self.init()
glutMainLoop()
if __name__ == "__main__":
Mcve(b"MCVE", 800, 600).run()
I get the next output:
At the moment i change the line add_line(data, [-k, 0.0, 0.0], [0, 0, 0], [0.0, 0.0, 1.0]) in the make_axis by add_line(data, [0.0, 0.0, k], [0, 0, 0], [0.0, 0.0, 1.0]) the 3rd axis/line won't be drawn anymore, as shown below:
What's wrong with the code? I've been trying to find out the issue already for quite a while.
The problem lies in the shader, where you assume that all input positions are just 2-dimensional vectors:
layout (location = 0) in vec2 a_position;
gl_Position = projection*view*model*vec4(a_position, 0.0, 1.0);
Since you then statically define the z-coordinate to be 0, [0, 0, -k] will be treated as [0,0,0].
To fix that, just use a vec3 in the shader and don't set the z-coordinate to 0.