#!python # -*- mode: python; Encoding: utf-8; coding: utf-8 -*- # Last updated: <2022/09/23 20:11:10 +0900> """ Darw billboard. Use GL_ALPHA_TEST. Key a, d : rotation key x : alpha_test ON/OFF key z : Sort billboards ON/OFF Windows10 x64 21H2 + Python 3.9.13 64bit + PyOpenGL 3.1.6 """ import sys import math from OpenGL.GL import * from OpenGL.GLU import * from OpenGL.GLUT import * from PIL import Image IMG_NAME = "img_rgba2.png" SCRW, SCRH = 800, 600 FPS = 60 scr_w, scr_h = SCRW, SCRH window = 0 cam_y = 5.0 roty = 0.0 cam_pos = (0, 0, 0) texture = 0 alpha_test_enable = True sort_enable = False # GET_DISTANCE = True GET_DISTANCE = False def load_texture(): global texture # load image by using PIL im = Image.open(IMG_NAME) w, h = im.size print("Image: %d x %d, %s" % (w, h, im.mode)) if im.mode == "RGB": # RGB convert to RGBA im.putalpha(alpha=255) elif im.mode == "L" or im.mode == "P": # Grayscale, Index Color convert to RGBA im = im.convert("RGBA") raw_image = im.tobytes() ttype = GL_RGBA if im.mode == "RGB": ttype = GL_RGB print("Set GL_RGB") elif im.mode == "RGBA": ttype = GL_RGBA print("Set GL_RGBA") glBindTexture(GL_TEXTURE_2D, glGenTextures(1)) # glPixelStorei(GL_UNPACK_ALIGNMENT, 1) glPixelStorei(GL_UNPACK_ALIGNMENT, 4) # set texture glTexImage2D( GL_TEXTURE_2D, # target 0, # MIPMAP level ttype, # texture type (RGB, RGBA) w, # texture image width h, # texture image height 0, # border width ttype, # texture type (RGB, RGBA) GL_UNSIGNED_BYTE, # data is unsigne char raw_image, # texture data pointer ) glClearColor(0, 0, 0, 0) glShadeModel(GL_SMOOTH) # set texture repeat glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT) glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT) # set texture filter glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR) glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR) glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE) # glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL) # glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE) # glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_BLEND) def draw_floor(): glEnable(GL_BLEND) glEnable(GL_TEXTURE_2D) glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) # glColor4f(1.0, 1.0, 1.0, 1.0) # color glColor4f(0.0, 1.0, 0.0, 1.0) # color # glAlphaFunc(GL_GREATER, 0.5) # glEnable(GL_ALPHA_TEST) glDisable(GL_ALPHA_TEST) glPushMatrix() w = 7.0 glBegin(GL_QUADS) glTexCoord2f(0.0, 0.0) # set u, v glVertex3f(-w, 0.0, -w) # Top Left glTexCoord2f(1.0, 0.0) glVertex3f(w, 0.0, -w) # Top Right glTexCoord2f(1.0, 1.0) glVertex3f(w, 0.0, w) # Bottom Right glTexCoord2f(0.0, 1.0) glVertex3f(-w, 0.0, w) # Bottom Left glEnd() glPopMatrix() def set_billboard_matrix(): m = glGetDoublev(GL_MODELVIEW_MATRIX) m[0][0] = m[1][1] = m[2][2] = 1.0 m[0][1] = m[0][2] = 0.0 m[1][0] = m[1][2] = 0.0 m[2][0] = m[2][1] = 0.0 glLoadMatrixd(m) def draw_billboards(): global alpha_test_enable, sort_enable, cam_pos glEnable(GL_BLEND) glEnable(GL_TEXTURE_2D) glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) glColor4f(1.0, 1.0, 1.0, 1.0) # color if alpha_test_enable: glEnable(GL_ALPHA_TEST) glAlphaFunc(GL_GREATER, 0.1) else: glDisable(GL_ALPHA_TEST) if sort_enable: glDisable(GL_DEPTH_TEST) objs = [] for i in range(0, 300, 10): x = 5.0 * math.cos(math.radians(i)) y = 0.0 z = 5.0 * math.sin(math.radians(i)) objs.append([x, y, z, 0]) if sort_enable: # sort billboards for i in range(len(objs)): x, y, z, _ = objs[i] value = 0 if GET_DISTANCE: dx = cam_pos[0] - x dy = cam_pos[1] - y dz = cam_pos[2] - z value = dx * dx + dy * dy + dz * dz else: glPushMatrix() glTranslatef(x, y, z) # translate m = glGetDoublev(GL_MODELVIEW_MATRIX) tx, ty, tz = m[3][0], m[3][1], m[3][2] value = tz glPopMatrix() objs[i][3] = value objs = sorted(objs, key=lambda x: x[3]) if GET_DISTANCE: objs.reverse() for obj in objs: x, y, z = obj[0], obj[1], obj[2] glPushMatrix() glTranslatef(x, y, z) # translate set_billboard_matrix() glBegin(GL_QUADS) glTexCoord2f(0.0, 0.0) # set u, v glVertex3f(-1.0, 2.0, 0) # Top Left glTexCoord2f(1.0, 0.0) glVertex3f(1.0, 2.0, 0.0) # Top Right glTexCoord2f(1.0, 1.0) glVertex3f(1.0, 0.0, 0.0) # Bottom Right glTexCoord2f(0.0, 1.0) glVertex3f(-1.0, 0.0, 0.0) # Bottom Left glEnd() glPopMatrix() glEnable(GL_DEPTH_TEST) def draw_gl(): global rotx, roty, cam_pos, cam_y glClearColor(0.2, 0.4, 0.8, 0.0) # background color glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) glLoadIdentity() # Reset The View # move camera r = 8.0 ex = r * math.cos(math.radians(roty + 90.0)) ey = cam_y ez = r * math.sin(math.radians(roty + 90.0)) tx, ty, tz = 0.0, 0.0, 0.0 gluLookAt(ex, ey, ez, tx, ty, tz, 0, 1, 0) cam_pos = (ex, ey, ez) draw_floor() draw_billboards() glDisable(GL_TEXTURE_2D) glDisable(GL_ALPHA_TEST) glutSwapBuffers() def init_viewport_and_pers(width, height): # Prevent A Divide By Zero If The Window Is Too Small if height == 0: height = 1 scr_w, scr_h = width, height # Reset The Current Viewport And Perspective Transformation glViewport(0, 0, width, height) glMatrixMode(GL_PROJECTION) glLoadIdentity() # Reset The Projection Matrix # Calculate The Aspect Ratio Of The Window # gluPerspective(fovy, aspect, zNear, zFar ) gluPerspective(60.0, float(width) / float(height), 0.1, 100.0) glMatrixMode(GL_MODELVIEW) def InitGL(width, height): glClearColor(0.2, 0.4, 0.8, 0.0) # background color glClearDepth(1.0) # Enables Clearing Of The Depth Buffer glEnable(GL_DEPTH_TEST) # Enables Depth Testing glDepthFunc(GL_LESS) # The Type Of Depth Test To Do glShadeModel(GL_SMOOTH) # Enables Smooth Color Shading init_viewport_and_pers(width, height) def resize_gl(width, height): init_viewport_and_pers(width, height) def on_timer(value): global rotx, roty # rotx = 0.0 # roty = roty + 0.5 glutPostRedisplay() glutTimerFunc(int(1000 / FPS), on_timer, 0) def key_pressed(key, x, y): global roty, alpha_test_enable, sort_enable, cam_y # If escape is pressed, kill everything. ESCAPE = b"\x1b" if key == ESCAPE or key == b'q': if glutLeaveMainLoop: glutLeaveMainLoop() else: sys.exit() if key == b'd': roty += 1 if key == b'a': roty -= 1 if key == b'w': cam_y += 0.1 if key == b's': cam_y -= 0.1 if key == b'x': alpha_test_enable = not alpha_test_enable if key == b'z': sort_enable = not sort_enable def main(): global window glutInit(sys.argv) glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH) glutInitWindowSize(SCRW, SCRH) # glutInitWindowPosition(0, 0) window = glutCreateWindow(b"Draw texture") glutDisplayFunc(draw_gl) glutReshapeFunc(resize_gl) glutKeyboardFunc(key_pressed) # glutFullScreen() # glutIdleFunc(draw_gl) glutTimerFunc(int(1000 / FPS), on_timer, 0) InitGL(SCRW, SCRH) load_texture() glutMainLoop() if __name__ == "__main__": print("Hit ESC key to quit.") main()