mieki256's diary



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
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 - 木のテクスチャをレンダリング中

参考ページ :


以上です。

過去ログ表示

Prev - 2022/09 - Next
1 2 3
4 5 6 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29 30

カテゴリで表示

検索機能は Namazu for hns で提供されています。(詳細指定/ヘルプ


注意: 現在使用の日記自動生成システムは Version 2.19.6 です。
公開されている日記自動生成システムは Version 2.19.5 です。

Powered by hns-2.19.6, HyperNikkiSystem Project