簡體   English   中英

在 OpenGL 的正交投影中使用紋理

[英]Using textures in orthographic projection in OpenGL

我目前正在使用 Python 和 OpenGL 對主要飛行顯示器進行編程。 由於我對 OpenGL 的經驗為零(並且通常很少有編碼經驗),因此我遵循了一個簡單的教程,介紹了用於 2D 應用程序的 OpenGL。 有了這些基本知識,我才開始用多邊形(GL_QUADS)和一些復制的代碼來繪制文本。 這給了我這個:

Pi2Fly

視覺效果是令人滿意的,但大約 450 次迭代/秒的性能是有問題的,其中一個核心 Ryzen 1700x 100% 和 GTX1080 75%,同時只運行這個小程序(它應該在 RaspberryPI 3 上運行)。

所以我想嘗試為整個人造地平線和無生命的物體使用紋理,比如速度計的比例、分隔線或“十字准線”。 我制作了一個 .png 文件來替換人工地平線(見底部),但我不知道如何渲染它。 我發現的所有教程都是針對 C++ 的,並且給整個程序增加了很多復雜性,所以我想問一下是否有一種“簡單”的方法來為我的應用程序實現可移動的紋理。

這是我目前如何計算地平線位置以及如何繪制它的模型(最后是完整的可運行代碼):

from OpenGL.GL import *
from OpenGL.GLUT import *
from OpenGL.GLU import *
from math import tan, radians

window_width = 800
window_height = 480

class Numbers:
    mp = 0 #vertical middle point of horizon in pixels
    rp = 0 #vertical right point of horizon in pixels
    lp = 0 #vertical left point of horizon in pixels

def calc():

    # mp- (pitch*(pixels/degree))
    Numbers.mp = 240 - (Data.pitch * 8)
    #offset is the vertical distance in pixels from mp
    #offset =tan(roll) * pixels from horizontal middle point of screen to edge of artifical horizon
    offset = tan(radians(Data.roll)) * 300

    Numbers.rp = Numbers.mp - offset
    Numbers.lp = Numbers.mp + offset

def horizon():

    #sky  
    glBegin(GL_QUADS)
    glColor3f(58/255, 109/255, 171/255)
    glVertex2f(100, Numbers.lp)
    glVertex2f(700, Numbers.rp)
    glColor3f(0/255, 83/255, 165/255)
    glVertex2f(700, 480) 
    glVertex2f(100, 480)
    glEnd()

    #ground
    glColor3f(150/255, 70/255, 0/255)
    glBegin(GL_QUADS)
    glVertex2f(100, 0)
    glVertex2f(700, 0)
    glColor3f(140/255, 90/255, 0/255)
    glVertex2f(700, Numbers.rp)
    glVertex2f(100, Numbers.lp)
    glEnd()

    #devider line
    glColor3f(255/255, 255/255, 255/255)
    glBegin(GL_QUADS)
    glVertex2f(100, Numbers.lp-1)
    glVertex2f(700, Numbers.rp-1)
    glVertex2f(700, Numbers.rp+1)
    glVertex2f(100, Numbers.lp+1)
    glEnd()

def iterate():
    glViewport(0, 0, window_width, window_height)
    glMatrixMode(GL_PROJECTION)
    glLoadIdentity()
    glOrtho(0.0, window_width, 0.0, window_height, 0.0, 1.0)
    glMatrixMode (GL_MODELVIEW)
    glLoadIdentity()

def showScreen():
    #glutFullScreen()
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
    glLoadIdentity()  
    iterate()

    calc()
    horizon()

    glutSwapBuffers()

if __name__ == "__main__":
    glutInit()
    glutInitDisplayMode(GLUT_RGBA)
    glutInitWindowSize(window_width, window_height)
    glutInitWindowPosition(10, 10)
    wind = glutCreateWindow(b"Pi2Fly")
    glutDisplayFunc(showScreen)
    glutIdleFunc(showScreen)
    glutMainLoop()

Horizo​​ n_cut.png 真實圖像從 180 到 180 到 但這對 SO 來說太大了


完整代碼:

from OpenGL.GL import glVertex2f, glColor3f, glBegin, glEnd, GL_QUADS, GL_TRIANGLES
from OpenGL.GL import *
from OpenGL.GLUT import *
from OpenGL.GLU import *
from time import sleep, perf_counter, time
from math import tan, radians
import pygame

window_width = 800
window_height = 480

pygame.font.init()
font1 = pygame.font.Font (None, 40)
font2 = pygame.font.Font (None, 20)
font3 = pygame.font.Font (None, 25)
font4 = pygame.font.Font (None, 45)

class Numbers:
    mp = 0
    rp = 0
    lp = 0

class Data:
    roll = 20
    pitch = 0
    airspeed = 160

    altitude = 4500

    altitude_last = 0
    altitude_now = 0
    altitude_delta = 0
    altitude_delta_table = 30 * [0]
    time_last = 0
    time_now = 0

    c1 = True
    c2 = True
    c3 = 0

class FPS:   
    c = 0
    fps = 0

def fps():

    FPS.c += 1
    try: FPS.last_clock = FPS.this_clock
    except:FPS.last_clock = perf_counter()
    FPS.this_clock = perf_counter()
    if round(FPS.this_clock) > round(FPS.last_clock):
        FPS.fps = FPS.c
        FPS.c = 0

    color = (255,198,0, 255)
    drawText(5, 9, str(FPS.fps), color, font1)
    drawText(55, 10, "FPS", color, font2)

def get_data():
    #dummy data for artifical horizon

    #pitch
    if Data.pitch < 20 and Data.c1: Data.pitch += 0.03
    if Data.pitch >= 20 and Data.c1: Data.c1 = False
    if Data.pitch < 21 and not Data.c1: Data.pitch = Data.pitch - 0.03
    if Data.pitch < -20: Data.c1 = True

    #roll
    if Data.roll < 20 and Data.c2: Data.roll += 0.02
    if Data.roll >= 20 and Data.c2: Data.c2 = False
    if Data.roll < 21 and not Data.c2: Data.roll = Data.roll - 0.02
    if Data.roll < -20: Data.c2 = True

    #airspeed
    if Data.pitch > 0: Data.airspeed = Data.airspeed - 0.004 * Data.pitch
    elif Data.pitch < 0: Data.airspeed = Data.airspeed + 0.004 * abs(Data.pitch)

    #altitude
    if Data.pitch > 0: Data.altitude = Data.altitude + 0.01 * Data.pitch
    elif Data.pitch < 0: Data.altitude = Data.altitude - 0.01 * abs(Data.pitch)

    #altitude_delta
    Data.time_now = perf_counter()
    if Data.time_now >= Data.time_last + 0.01:
        if Data.c3 == 30: Data.c3 = 0
        Data.time_last = Data.time_now        
        Data.altitude_last = Data.altitude_now
        Data.altitude_now = Data.altitude        
        Data.altitude_delta_table[Data.c3] = Data.altitude_now - Data.altitude_last
        Data.c3 +=1 
    Data.altitude_delta = (sum(Data.altitude_delta_table)/len(Data.altitude_delta_table))

def calc():

    Numbers.mp = 240 - (Data.pitch * 8)
    offset = tan(radians(Data.roll)) * 300
    Numbers.rp = Numbers.mp - offset
    Numbers.lp = Numbers.mp + offset

def frame():

    #frame color = grey
    glColor3f(100/255, 100/255, 100/255)

    #horizon right line
    glBegin(GL_QUADS)
    glVertex2f(99, 0)
    glVertex2f(100, 0)
    glVertex2f(100, window_height)
    glVertex2f(99, window_height)
    glEnd()

    #horizon left line
    glBegin(GL_QUADS)
    glVertex2f(700, 0)
    glVertex2f(701, 0)
    glVertex2f(701, window_height)
    glVertex2f(700, window_height)
    glEnd()

    #right upper divider
    glBegin(GL_QUADS)
    glVertex2f(0, 430)
    glVertex2f(100, 430)
    glVertex2f(100, 431)
    glVertex2f(0, 431)
    glEnd()

    #left upper divider
    glBegin(GL_QUADS)
    glVertex2f(700, 430)
    glVertex2f(800, 430)
    glVertex2f(800, 431)
    glVertex2f(700, 431)
    glEnd()

    #right lower divider
    glBegin(GL_QUADS)
    glVertex2f(0, 50)
    glVertex2f(100, 50)
    glVertex2f(100, 51)
    glVertex2f(0, 51)
    glEnd()

    #left lower divider
    glBegin(GL_QUADS)
    glVertex2f(700, 50)
    glVertex2f(800, 50)
    glVertex2f(800, 51)
    glVertex2f(700, 51)
    glEnd()

def horizon():
    #sky  
    glBegin(GL_QUADS)
    glColor3f(58/255, 109/255, 171/255)
    glVertex2f(100, Numbers.lp)
    glVertex2f(700, Numbers.rp)
    glColor3f(0/255, 83/255, 165/255)
    glVertex2f(700, 480) 
    glVertex2f(100, 480)
    glEnd()

    #ground
    glColor3f(150/255, 70/255, 0/255)
    glBegin(GL_QUADS)
    glVertex2f(100, 0)
    glVertex2f(700, 0)
    glColor3f(140/255, 90/255, 0/255)
    glVertex2f(700, Numbers.rp)
    glVertex2f(100, Numbers.lp)
    glEnd()

    #devider line
    glColor3f(255/255, 255/255, 255/255)
    glBegin(GL_QUADS)
    glVertex2f(100, Numbers.lp-1)
    glVertex2f(700, Numbers.rp-1)
    glVertex2f(700, Numbers.rp+1)
    glVertex2f(100, Numbers.lp+1)
    glEnd()

    #crosshair
    #indicator triangle shadow
    glColor3f(187/255, 107/255, 1/255)
    glBegin(GL_QUADS)
    glVertex2f(400, 230)
    glVertex2f(365, 215)
    glVertex2f(400, 227)
    glVertex2f(435, 215)
    glEnd()
    # indicator triangle
    glColor3f(255/255, 198/255, 0/255)
    glBegin(GL_QUADS)
    glVertex2f(400, 240)
    glVertex2f(365, 215)
    glVertex2f(400, 230)
    glVertex2f(435, 215)
    glEnd()
    #yellow indicator line
    glColor3f(255/255, 198/255, 0/255)
    glBegin(GL_QUADS)
    glVertex2f(320, 241)
    glVertex2f(320, 239)
    glVertex2f(350, 239)
    glVertex2f(350, 241)
    glEnd()
    glBegin(GL_QUADS)
    glVertex2f(450, 241)
    glVertex2f(450, 239)
    glVertex2f(480, 239)
    glVertex2f(480, 241)
    glEnd()


    color = (255,  255, 255, 0)
    drawText(110, 10, str("roll: "+str(round(Data.roll))), color, font3)
    drawText(620, 10, str("pitch: "+str(round(Data.pitch))), color, font3)


def speedometer():
    #dial
    ssp = 55 #start spacer
    sp = 27 #spacer
    color = (255,255,255,100)

    drawText(14, ssp+sp*0, "0", color, font3)
    drawText(10, ssp+sp*1, "20", color, font3)
    drawText(10, ssp+sp*2, "40", color, font3)
    drawText(10, ssp+sp*3, "60", color, font3)
    drawText(10, ssp+sp*4, "80", color, font3)
    drawText(5, ssp+sp*5, "100", color, font3)
    drawText(5, ssp+sp*6, "120", color, font3)
    drawText(5, ssp+sp*7, "140", color, font3)
    drawText(5, ssp+sp*8, "160", color, font3)
    drawText(5, ssp+sp*9, "180", color, font3)
    drawText(5, ssp+sp*10, "200", color, font3)
    drawText(5, ssp+sp*11, "220", color, font3)
    drawText(5, ssp+sp*12, "240", color, font3)
    drawText(5, ssp+sp*13, "260", color, font3)

    #green area
    glBegin(GL_QUADS)
    glColor3f(35/255, 150/255, 29/255)
    glVertex2f(40, 142)
    glVertex2f(99, 142)
    glVertex2f(99, 287)
    glVertex2f(40, 287)
    glEnd()
    #yellow area
    glBegin(GL_QUADS)
    glColor3f(150/255, 100/255, 27/255)
    glVertex2f(40, 287)
    glVertex2f(99, 287)
    glVertex2f(99, 360)
    glVertex2f(40, 360)
    glEnd()
    #red area
    glBegin(GL_QUADS)
    glColor3f(151/255, 43/255, 29/255)
    glVertex2f(40, 360)
    glVertex2f(99, 360)
    glVertex2f(99, 374)
    glVertex2f(40, 374)
    glEnd()

    ll=35
    l=15
    w=1
    ssp=64

    glColor3f(255/255, 255/255, 255/255)
    for x in range(0, 14):   
        ind_lines(ll, l, w, ssp+sp*x)

    #knots
    color = (0,  241, 250, 255)
    drawText(60, 435, "kn", color, font4)

def speedometer_indicator():

    ypos = 63 + round(Data.airspeed) * 1.35    
    glColor3f(0/255, 241/255, 250/255)
    ind_lines(40, 59, 3, ypos)
    color = (0,  241, 250, 255)
    drawText(5, 435, str(round(Data.airspeed)), color, font4)

def altimeter():

    color = (0,  241, 250, 255)
    drawText(705, 435, str(round(Data.altitude)), color, font4)
    drawText(784, 435, "m", color, font3)

def variometer():
    color = (0,  241, 250, 255)
    drawText(710, 10, str(round((abs(Data.altitude_delta*10)), 1)), color, font4)
    drawText(765, 10, "m/s", color, font3)
    if Data.altitude_delta*10 > 0:
        glBegin(GL_TRIANGLES)
        glVertex2f(767, 30)
        glVertex2f(793, 30)
        glVertex2f(780, 45)
        glEnd()

    if Data.altitude_delta*10 < 0:
        glBegin(GL_TRIANGLES)
        glVertex2f(765, 45)
        glVertex2f(795, 45)
        glVertex2f(780, 30)
        glEnd()

def ind_lines(ll, l, w, sp): #ll=left limit,l=lenght, w=width, sp=spacer

    #indicator lines
    glBegin(GL_QUADS)
    glVertex2f(ll, sp)
    glVertex2f(ll+l, sp)
    glVertex2f(ll+l, sp+w)
    glVertex2f(ll, sp+w)
    glEnd()

def drawText(xpos, ypos, textString, color, font):

    textSurface = font.render(textString, True, color, (0,0,0,1))
    textData = pygame.image.tostring(textSurface, "RGBA", True)    
    glRasterPos2d(xpos,ypos)
    glDrawPixels(textSurface.get_width(), textSurface.get_height(), GL_RGBA, GL_UNSIGNED_BYTE, textData)

def draw_once():
    speedometer()
    frame()

def draw():
    horizon()
    speedometer_indicator()
    altimeter()
    variometer()

def iterate():
    glViewport(0, 0, window_width, window_height)
    glMatrixMode(GL_PROJECTION)
    glLoadIdentity()
    glOrtho(0.0, window_width, 0.0, window_height, 0.0, 1.0)
    glMatrixMode (GL_MODELVIEW)
    glLoadIdentity()

def showScreen():
    #glutFullScreen()
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
    glLoadIdentity()  
    iterate()

    try:get_data()
    except Exception as e:print("get_data:\t", e)
    try:calc()
    except Exception as e:print("calc:\t", e)
    try:draw_once()
    except Exception as e:print("draw_once:\t", e)
    try:draw()
    except Exception as e:print("draw:\t", e)
    try:fps()
    except Exception as e:print("fps:\t", e)

    glutSwapBuffers()

if __name__ == "__main__":
    glutInit()
    glutInitDisplayMode(GLUT_RGBA)
    glutInitWindowSize(window_width, window_height)
    glutInitWindowPosition(10, 10)
    wind = glutCreateWindow(b"Pi2Fly")
    glutDisplayFunc(showScreen)
    glutIdleFunc(showScreen)
    glutMainLoop()

您可以使用 pygame 加載紋理圖像:

def LoadTexture(filename):
    image = pygame.image.load(filename)
    datas = pygame.image.tostring(image, 'RGBA')
    texID = glGenTextures(1)
    glBindTexture(GL_TEXTURE_2D, texID) 
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, image.get_width(), image.get_height(),
                 0, GL_RGBA, GL_UNSIGNED_BYTE, datas)
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR_MIPMAP_LINEAR)
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)
    glGenerateMipmap(GL_TEXTURE_2D)
    glBindTexture(GL_TEXTURE_2D, 0)

    return texID

或者,您也可以使用PillowNumpy來加載紋理圖像:

from PIL import Image
import numpy
image = Image.open(filename)
imageData = numpy.array(list(image.getdata()), numpy.uint8)
texID = glGenTextures(1)
glBindTexture(GL_TEXTURE_2D, texID)
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, image.size[0], image.size[1],
             0, GL_RGBA, GL_UNSIGNED_BYTE, imageData)
# [...]

當您想在Legacy OpenGL 中使用紋理時,您必須:

  • glVertex之前通過glTexCoord2f設置紋理坐標
  • 通過glBindTexture綁定紋理
  • 通過glEnable(GL_TEXTURE_2D)啟用二維紋理

下面的示例在整個視口上繪制一個四邊形並在其上包裹一個紋理。 例如:

def DrawTexture(texID, x, y, w, h):

    glBindTexture(GL_TEXTURE_2D, texID)

    glEnable(GL_TEXTURE_2D)
    glColor4f(1, 1, 1, 1)

    glBegin(GL_QUADS)
    glTexCoord2f(0, 1); glVertex2f(x, y)
    glTexCoord2f(0, 0); glVertex2f(x, y+h)
    glTexCoord2f(1, 0); glVertex2f(x+w, y+h)
    glTexCoord2f(1, 1); glVertex2f(x+w, y)
    glEnd()

    glDisable(GL_TEXTURE_2D)

請注意,如果啟用了紋理,則默認情況下,紋理像素的顏色將乘以當前顏色,因為默認情況下紋理環境模式( GL_TEXTURE_ENV_MODE )為GL_MODULATE 請參閱glTexEnv

這會導致紋理的紋素顏色與您通過glColor4f設置的最后一種顏色“混合”。

在渲染紋理之前設置“白色”顏色,以解決您的問題:

glColor4f(1, 1, 1, 1)

同樣,您可以將環境模式更改為GL_REPLACE ,而不是:

glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);

此外,如果紋理部分透明,則必須在繪制紋理之前啟用混合

glEnable(GL_BLEND)
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)

DrawTexture(....)

glDisable(GL_BLEND)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM