2022/09/22(木) [n年前の日記]
#1 [python] PyOpenGLでビルボードにフォグをかけてみた
Python + PyOpenGL (OpenGL) を使ってビルボード関係の実験中。環境は、Windows10 x64 21H2 + Python 3.9.13 64bit + PyOpenGL 3.1.6。
今回は、ビルボードにフォグをかけることができるのかどうかを試してみた。フォグというのは霧のこと。この場合、カメラとの距離に基づいてポリゴンの色を変えていくことで、遠近感があるように感じさせつつ、遠くのものははっきり見えない状態にして見た目を誤魔化す手法をフォグと呼んでいる。
動作させた結果は以下。フォグがかかっていることが分かるかと。
ちなみに、このサンプルは、ビルボードにとっては厳しい感じのカメラの動かし方をしている。回転させたり、上から見たりすると、立て看板らしさが実にハッキリと分かる…。小さいウインドウで見てる分には気にならないかもしれないけど、スクリプトを動かして、全画面表示にして眺めてみると、なかなか厳しいものが…。
今回は、ビルボードにフォグをかけることができるのかどうかを試してみた。フォグというのは霧のこと。この場合、カメラとの距離に基づいてポリゴンの色を変えていくことで、遠近感があるように感じさせつつ、遠くのものははっきり見えない状態にして見た目を誤魔化す手法をフォグと呼んでいる。
動作させた結果は以下。フォグがかかっていることが分かるかと。
ちなみに、このサンプルは、ビルボードにとっては厳しい感じのカメラの動かし方をしている。回転させたり、上から見たりすると、立て看板らしさが実にハッキリと分かる…。小さいウインドウで見てる分には気にならないかもしれないけど、スクリプトを動かして、全画面表示にして眺めてみると、なかなか厳しいものが…。
◎ ソース。 :
ソースは以下のような感じになった。python 05_draw_billboard_trees.py で実行できる。
_05_draw_billboard_trees.py
使用画像は以下。CC0 / Public Domain ってことで。前述のスクリプトと同じ場所に置いておくこと。
_tex.png
_05_draw_billboard_trees.py
import sys import math import random from OpenGL.GL import * from OpenGL.GLU import * from OpenGL.GLUT import * from PIL import Image IMG_NAME = "tex.png" # SCRW, SCRH = 1600, 900 SCRW, SCRH = 512, 512 FPS = 60 FLOOR_W = 10.0 TREES_MAX = 256 # USE_DEPTHMASK = True USE_DEPTHMASK = False # GET_DISTANCE = True GET_DISTANCE = False scr_w, scr_h = SCRW, SCRH window = 0 # Rotation angle for the Quads rotx = 0.0 roty = 0.0 objs = [] texture = 0 # lighting light_ambient = [1.0, 1.0, 1.0, 1.0] light_diffuse = [1.0, 1.0, 1.0, 1.0] light_specular = [1.0, 1.0, 1.0, 1.0] light_position = [5.0, 5.0, 10.0, 1.0] # material no_mat = [0.0, 0.0, 0.0, 1.0] mat_ambient = [0.05, 0.1, 0.25, 1.0] mat_diffuse = [0.0, 1.0, 0.0, 1.0] mat_specular = [0.5, 0.5, 0.5, 1.0] mat_emission = [0.2, 0.2, 0.2, 0.0] no_shininess = [0.0] low_shininess = [5.0] high_shininess = [100.0] class Obj: def __init__(self, x, y, z, scale, kind): self.x = x self.y = y self.z = z self.scale = scale uv = [ (0.5, 0.0), (0.75, 0.0), (0.5, 0.25), (0.75, 0.25), (0.5, 0.5), (0.75, 0.5), (0.5, 0.75), (0.75, 0.75), ] self.u, self.v = uv[kind] def draw(self): glAlphaFunc(GL_GREATER, 0.5) glEnable(GL_ALPHA_TEST) glEnable(GL_BLEND) glEnable(GL_TEXTURE_2D) glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) glDisable(GL_LIGHTING) glColor4f(1.0, 1.0, 1.0, 1.0) # color glPushMatrix() glTranslatef(self.x, self.y, self.z) # translate 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) w = 1.0 * self.scale u, v = self.u, self.v uw = 0.25 glBegin(GL_QUADS) glTexCoord2f(u, v) glVertex3f(-w, w * 2, 0) # Top Left glTexCoord2f(u + uw, v) glVertex3f(w, w * 2, 0.0) # Top Right glTexCoord2f(u + uw, v + uw) glVertex3f(w, 0.0, 0.0) # Bottom Right glTexCoord2f(u, v + uw) glVertex3f(-w, 0.0, 0.0) # Bottom Left glEnd() glPopMatrix() glDisable(GL_ALPHA_TEST) glDisable(GL_TEXTURE_2D) def init_objs(): global objs w = FLOOR_W n = 4.0 kind = 0 i = 0 while i < TREES_MAX: x = random.uniform(-w, w) y = 0 z = random.uniform(-w, w) if (x * x + z * z <= n * n): continue scale = random.uniform(1.0, 1.5) objs.append(Obj(x, y, z, scale, kind)) kind = (kind + 1) % 4 i += 1 def draw_objs(): global objs for obj in objs: obj.draw() 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(): """Draw floor quads.""" w = FLOOR_W u, v = 0.0, 0.0 uw = 0.5 glDisable(GL_LIGHTING) 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 glPushMatrix() glBegin(GL_QUADS) glTexCoord2f(u, v) glVertex3f(-w, 0.0, -w) # Top Left glTexCoord2f(u + uw, v) glVertex3f(w, 0.0, -w) # Top Right glTexCoord2f(u + uw, v + uw) glVertex3f(w, 0.0, w) # Bottom Right glTexCoord2f(u, v + uw) glVertex3f(-w, 0.0, w) # Bottom Left glEnd() glPopMatrix() def draw_cube(): """ Draw cube.""" glEnable(GL_LIGHTING) glEnable(GL_LIGHT0) glMaterialfv(GL_FRONT, GL_AMBIENT, mat_ambient) glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse) glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular) glMaterialfv(GL_FRONT, GL_SHININESS, high_shininess) glMaterialfv(GL_FRONT, GL_EMISSION, no_mat) glPushMatrix() glDisable(GL_TEXTURE_2D) glColor3f(0.0, 1.0, 0.0) if False: glTranslatef(0.0, 2.0, 0.0) glutSolidCube(4.0) else: glTranslatef(0.0, 1.2, 0.0) glutSolidTeapot(2.0) glPopMatrix() def draw_gl(): global rotx, roty glClearColor(0.85, 0.85, 0.85, 0.0) # background color glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) # set light glLightfv(GL_LIGHT0, GL_AMBIENT, light_ambient) glLightfv(GL_LIGHT0, GL_DIFFUSE, light_diffuse) glLightfv(GL_LIGHT0, GL_SPECULAR, light_specular) glLightfv(GL_LIGHT0, GL_POSITION, light_position) glEnable(GL_LIGHTING) glEnable(GL_LIGHT0) glLoadIdentity() # Reset The View # move camera r = 16.0 ex = r * math.cos(math.radians(roty + 90.0)) ey = 6.0 + 4.0 * math.cos(math.radians(roty * 3)) 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_cube() draw_objs() 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(45.0, float(width) / float(height), 0.1, 100.0) glMatrixMode(GL_MODELVIEW) def InitGL(width, height): glClearColor(0.85, 0.85, 0.85, 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 # setiing fog fog_color = [0.85, 0.85, 0.85] glEnable(GL_FOG) glFogi(GL_FOG_MODE, GL_LINEAR) glFogfv(GL_FOG_COLOR, fog_color) glFogf(GL_FOG_DENSITY, 0.1) glHint(GL_FOG_HINT, GL_DONT_CARE) glFogf(GL_FOG_START, 8.0) glFogf(GL_FOG_END, 25.0) 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.25 glutPostRedisplay() glutTimerFunc(int(1000 / FPS), on_timer, 0) def key_pressed(key, x, y): # If escape is pressed, kill everything. ESCAPE = b"\x1b" if key == ESCAPE or key == b'q': if glutLeaveMainLoop: glutLeaveMainLoop() else: sys.exit() 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() init_objs() glutMainLoop() if __name__ == "__main__": print("Hit ESC key to quit.") main()
使用画像は以下。CC0 / Public Domain ってことで。前述のスクリプトと同じ場所に置いておくこと。
_tex.png
◎ 覚書。 :
フォグをかけるように指定している部分は以下。
# setiing fog fog_color = [0.85, 0.85, 0.85] glEnable(GL_FOG) glFogi(GL_FOG_MODE, GL_LINEAR) glFogfv(GL_FOG_COLOR, fog_color) glFogf(GL_FOG_DENSITY, 0.1) glHint(GL_FOG_HINT, GL_DONT_CARE) glFogf(GL_FOG_START, 8.0) glFogf(GL_FOG_END, 25.0)
- glEnable(GL_FOG)、glDisable(GL_FOG) で、フォグの有効/無効を切り替えられる。
- fog_color がフォグの色。[1.0, 1.0, 1.0] なら真っ白になるし、[0.0, 0.0, 0.0] なら真っ黒になる。
- glFogfv(GL_FOG_COLOR, fog_color) で、フォグの色を指定している。
- glFogi(GL_FOG_MODE, GL_LINEAR) で、フォグのかかり方の種類を指定している。
- フォグのかかり方が GL_LINEAR の場合、GL_FOG_START、GL_FOG_END で、フォグがかかり始める距離を指定できるらしい。
- GL_FOG_DENSITY の値は、フォグのかかり方 (GL_FOG_MODE) が、GL_EXP、GL_EXP2 の場合に絡んでくるらしい。今回は GL_LINEAR にしているから、関係してこないっぽい。
◎ 画像の入手先について。 :
今回、以下のblogで、CC0 として公開されてる木の画像を、テクスチャとして使わせてもらった。ありがたや。blenderでレンダリングした画像らしい。
_Tree 01 Free CC0 Image | Graphics Learning
_Tree 02 Free CC0 Image | Graphics Learning
_Tree 03 Free CC0 Image | Graphics Learning
_Tree 04 Free CC0 Image | Graphics Learning
余談。一応自分も木の画像を公開していたりするのだけど、見た目のスタイルがバラバラなものだから、自分で作っておきながら使い辛くて…。
_trees - OneDrive
_mieki256's diary - 木のテクスチャをレンダリング中
_Tree 01 Free CC0 Image | Graphics Learning
_Tree 02 Free CC0 Image | Graphics Learning
_Tree 03 Free CC0 Image | Graphics Learning
_Tree 04 Free CC0 Image | Graphics Learning
余談。一応自分も木の画像を公開していたりするのだけど、見た目のスタイルがバラバラなものだから、自分で作っておきながら使い辛くて…。
_trees - OneDrive
_mieki256's diary - 木のテクスチャをレンダリング中
◎ 参考ページ :
[ ツッコむ ]
以上です。