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()
Related
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);
}
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).
The color of the texture changes when it is being rendered by OpenGL, and I have no other color predefined in the code, except during the glClear and once during the cube() function. Note: changing the glTexImage2D format parameter from GL_RGB to GL_BGR only swaps colors.
Original texture:
Rendered Texture:
Full code:
import time
import numpy
from OpenGL.GL import *
from OpenGL.GLU import *
from OpenGL.GLUT import *
from PIL import Image
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)]
colors = [(1, 0, 0), (0, 1, 0), (0, 0, 1), (1, 1, 0), (1, 0, 1), (0, 1, 1)]
def cube():
glRotatef(1, 3, 1, 1)
glColor3fv((1, 1, 1))
glBegin(GL_QUADS)
for i, face in enumerate(faces):
for surftex, vertex in enumerate(face):
if surftex == 0:
glTexCoord2f(0.0, 0.0)
elif surftex == 1:
glTexCoord2f(0.0, 1.0)
elif surftex == 2:
glTexCoord2f(1.0, 1.0)
elif surftex == 3:
glTexCoord2f(1.0, 0.0)
glVertex3fv(vertices[vertex])
glEnd()
def textureBind():
img = Image.open('Image.png')
img_data = numpy.array(list(img.getdata()), numpy.int8)
glEnable(GL_TEXTURE_2D)
texture_id = glGenTextures(1)
glBindTexture(GL_TEXTURE_2D, texture_id)
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER)
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER)
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, img.size[0], img.size[1],
0, GL_RGB, GL_FLOAT, img_data)
return texture_id
def showScreen():
global width, height
glClearColor(0, 0, 0, 1)
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
cube()
glEnable(GL_DEPTH_TEST)
glutSwapBuffers()
def mouseTracker(mousex, mousey):
print(f"Mouse pos: {mousex}, {mousey}")
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)
glutInit()
glutInitDisplayMode(GLUT_RGBA)
glutInitWindowSize(500, 500)
wind = glutCreateWindow("OpenGL")
glutDisplayFunc(showScreen)
glutIdleFunc(showScreen)
glutMotionFunc(mouseTracker)
glutPassiveMotionFunc(mouseTracker)
glutReshapeFunc(reshapeWindow)
gluPerspective(45, (width / height), 0.0001, 1000)
textureBind()
while True:
glutMainLoopEvent()
glutPostRedisplay()
time.sleep(0.001)
The format of this texture is BGR, but not RGB. Use GL_BGR instead of GL_RGB:
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, img.size[0], img.size[1],0, GL_RGB, GL_FLOAT, img_data)
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, img.size[0], img.size[1],
0, GL_BGR, GL_FLOAT, img_data)
The format of the image is stored in the mode attribute of the PIL.Image.Image object. See Mode.
When using gluPerspective in glutReshapeFunc function, the square image flashes while resizing and is gone after a few moments.
from OpenGL.GL import *
from OpenGL.GLU import *
from OpenGL.GLUT import *
width = 500
height = 500
def cube():
glBegin(GL_QUADS)
glColor3f(0, 1, 0)
glVertex3f(10, 0, 0)
glVertex3f(10, 10, 0)
glVertex3f(10, 0, 0)
glVertex3f(0, 0, 0)
glVertex3f(10, 0, 0)
glVertex3f(10, 0, 10)
glVertex3f(0, 10, 0)
glVertex3f(10, 10, 0)
glVertex3f(0, 10, 0)
glVertex3f(0, 0, 0)
glVertex3f(0, 10, 0)
glVertex3f(0, 10, 10)
glVertex3f(0, 0, 10)
glVertex3f(0, 0, 0)
glVertex3f(0, 0, 10)
glVertex3f(10, 0, 10)
glVertex3f(0, 0, 10)
glVertex3f(0, 10, 10)
glVertex3f(10, 10, 10)
glVertex3f(10, 10, 0)
glVertex3f(10, 10, 10)
glVertex3f(10, 0, 10)
glVertex3f(10, 10, 10)
glVertex3f(0, 10, 10)
glEnd()
def showScreen():
global width, height
glClearColor(0, 0, 0, 1)
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
cube()
glutSwapBuffers()
def mouseTracker(mousex, mousey):
print(f"Mouse pos: {mousex}, {mousey}")
def reshapeWindow(x, y):
global width, height
width = x
height = y
print(x, y)
gluPerspective(45, (width / height), 0.0001, 1000)
glutInit()
glutInitDisplayMode(GLUT_RGBA)
glutInitWindowSize(500, 500)
wind = glutCreateWindow("OpenGL")
glutDisplayFunc(showScreen)
glutIdleFunc(showScreen)
glutMotionFunc(mouseTracker)
glutPassiveMotionFunc(mouseTracker)
glutReshapeFunc(reshapeWindow)
gluPerspective(45, (width / height), 0.0001, 1000)
glTranslatef(0, 0, -5)
while True:
glutMainLoopEvent()
glutPostRedisplay()
If I put the gluPerspective into the showScreen function like so:
def showScreen():
global width, height
glClearColor(0, 0, 0, 1)
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
cube()
gluPerspective(45, (width / height), 0.0001, 1000)
glutSwapBuffers()
The square flashes without resizing but it is gone after a few moments. If I remove the gluPerspective entirely, the image turns into a triangle. Is there any way to change gluPerspective variables without making the image flash?
You have to call gluPerspective before drawing the cube. The matrix operations not only set the current matrix, but define a new matrix and multiply the current matrix by the new matrix. Therefore you must load the Identity matrix with glLoadIdentity before modifying the matrix. The legacy OpenGL provides different current matrices for the model view matrix and the projection matrix. Before changing a matrix, select the matrix mode with glMatrixMode:
Change the projection matrix in the reshape callback:
def reshapeWindow(x, y):
global width, height
width = x
height = y
print(x, y)
glMatrixMode(GL_PROJECTION)
glLoadIdentity()
gluPerspective(45, (width / height), 0.0001, 1000)
glMatrixMode(GL_MODELVIEW)
Set the model view matrix before the application loop or in the application loop:
glMatrixMode(GL_MODELVIEW)
glLoadIdentity()
glTranslatef(0, 0, -5)
gluPerspective defines a Viewing frustum. The center of the view is (0, 0). Hence you need to change the vertex coordinates.
I suggest enabling the Depth Test when drawing 3D meshes.
Minimale example based on your code:
from OpenGL.GL import *
from OpenGL.GLU import *
from OpenGL.GLUT import *
import time
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)]
colors = [(1, 0, 0), (0, 1, 0), (0, 0, 1), (1, 1, 0), (1, 0, 1), (0, 1, 1)]
def cube():
glRotatef(1, 3, 1, 1)
glBegin(GL_QUADS)
for i, face in enumerate(faces):
glColor3fv(colors[i])
for vertex in face:
glVertex3fv(vertices[vertex])
glEnd()
def showScreen():
glClearColor(0, 0, 0, 1)
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
cube()
glutSwapBuffers()
def mouseTracker(mousex, mousey):
print(f"Mouse pos: {mousex}, {mousey}")
def reshapeWindow(x, y):
global width, height
width = x
height = y
print(x, y)
glMatrixMode(GL_PROJECTION)
glLoadIdentity()
gluPerspective(45, (width / height), 0.0001, 1000)
glMatrixMode(GL_MODELVIEW)
glutInit()
glutInitDisplayMode(GLUT_RGBA)
glutInitWindowSize(500, 500)
wind = glutCreateWindow("OpenGL")
glutDisplayFunc(showScreen)
glutIdleFunc(showScreen)
glutMotionFunc(mouseTracker)
glutPassiveMotionFunc(mouseTracker)
glutReshapeFunc(reshapeWindow)
glMatrixMode(GL_MODELVIEW)
glLoadIdentity()
glTranslatef(0, 0, -5)
glEnable(GL_DEPTH_TEST)
while True:
glutMainLoopEvent()
glutPostRedisplay()
time.sleep(0.01)
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)
# [...]