Python之OpenGL笔记(30):飘扬的旗帜

一、目的

1、二维纹理映射学习,画一个飘扬的旗帜;
2、向奋战在防疫一线的白衣天使们致敬。

二、程序运行结果

仲宣楼为医生老夫子的作品。

三、基本原理

   吴亚峰《OpenGL ES 3.x游戏开发》(下卷)内容


   1、矩形的旗帜由大量的小三角形组成的。这样只要在绘制一帧画面时由顶点着色器根据一定的规则变换各个顶点的位置,即可得到旗帜迎风飘动的效果。

  2、例子给出的是旗帜面向 z 轴正方向,即顶点沿 z 轴上下振动,形成的波浪沿 x轴传播的情况。同时将坐标系沿x轴的方向旋转一个角度,便于观察z轴的变化。

  3、逐渐增加左侧顶点的对应角度,传入不同的 startAngle 值,使z坐标变化,形成波浪

四、具体实现

  从图 2-2 中可以看出,传入顶点着色器的原始顶点的 z 坐标都是相同的(本案例中为 0),经过顶点着色器变换后顶点的 z 坐标是根据正弦曲线分布的。具体的计算方法如下。

  • 1、 首先计算当前处理顶点的 x 坐标与最左侧顶点 x 坐标的差值,即 X 距离。
  • 2、 然后根据距离与角度的换算率将 x 距离换算为当前顶点与最左侧顶点的角度差
    (tempAngle)。
  • 3、接着将 tempAngle 加上最左侧顶点的对应角度(startAngle)即可得到当前顶点的对应角度(currAngle)。
  • 4、 最后通过求 currAngle 的正弦值即可得到当前顶点变换后的 z 坐标。

五、源代码

"""
程序名称:GL_textures04.py
编程: dalong10
功能: 飘扬的旗帜的实现
参考资料: 《OpenGL ES 3.x游戏开发》(下卷)吴亚峰
"""
import myGL_Funcs    #Common OpenGL utilities,see myGL_Funcs.py,https://www.jianshu.com/p/49dec482a291
import glfw
from OpenGL.GL import *
from OpenGL.GL.shaders import compileProgram, compileShader
import numpy 
import numpy as np
import pyrr
from PIL import Image

SphereVS = """
#version 330 core
layout(location = 0) in vec3 aPosition;
layout(location = 1) in vec2 inTexCoord;
uniform mat4 uMVPMatrix; //总变换矩阵
uniform float uStartAngle;//本帧起始角度(即最左侧顶点的对应角度)
uniform float uWidthSpan;//横向长度总跨度
uniform float a;
uniform float b;
uniform float c;
uniform float scale;

out vec2 passTexCoord;
void main(){
   //计算X向角度                 
   float angleSpanH=4.0*3.14159265;//横向角度总跨度,用于进行X距离与角度的换算
   float startX=-uWidthSpan/2.0;//起始X坐标(即最左侧顶点的X坐标)
   //根据横向角度总跨度、横向长度总跨度及当前点X坐标折算出当前顶点X坐标对应的角度
   float currAngleH=uStartAngle+((aPosition.x-startX)/uWidthSpan)*angleSpanH;
   
   //计算出随Y向发展起始角度的扰动值
   float angleSpanZ=4.0*3.14159265;//纵向角度总跨度,用于进行Y距离与角度的换算 
   float uHeightSpan=0.75*uWidthSpan;//纵向长度总跨度
   float startY=-uHeightSpan/2.0;//起始Y坐标(即最上侧顶点的Y坐标)
   //根据纵向角度总跨度、纵向长度总跨度及当前点Y坐标折算出当前顶点Y坐标对应的角度
   float currAngleZ=((aPosition.y-startY)/uHeightSpan)*angleSpanZ;
      
   //计算斜向波浪
   float tzH=sin(currAngleH-currAngleZ)*0.1;//通过正弦函数求出当前点的Z坐标   
    mat4 rot1=mat4(vec4(scale, 0.0,0.0,0),
                vec4(0.0, scale,0.0,0),
                vec4(0.0,0.0,scale,0.0),
                vec4(a,b,c,1.0));
    gl_Position= uMVPMatrix * rot1  *vec4(aPosition.x, aPosition.y, tzH, 1.0);
    passTexCoord = inTexCoord;
    }
"""

SphereFS = """
#version 330 core
uniform int colorflag;
uniform sampler2D tex;
in vec2 passTexCoord;
out vec4 outColor;
void main(){
    if (colorflag==-1)
    {outColor = texture(tex, passTexCoord);}
    if (colorflag==0)
    {  outColor = vec4(0,1,0,1);}
    if (colorflag==1)
    {  outColor = vec4(1,0,1,1);}
    if (colorflag==2)
    {  outColor = vec4(0,0,1,1);}
    if (colorflag==3)
    {  outColor = vec4(0,0,0,1);}
    if (colorflag>3)
    {  outColor = vec4(0,1,1,1);}
    }
"""

class BoardMode:
    def make_Mode(self):
        if ( self.rows < 1 or self.cols < 1):
            raise Exception("BoardMode can not be initialized!")        
        self.vertexs   = np.array([], np.float32)  # 位置FloatArray(numPoint * 3)
        self.texcoords = np.array([], np.float32)    # 纹理FloatArray(numPoint * 2)
        self.indices =  np.array([], np.int32)       # 索引 ShortArray(numPoint * 6)        
        # 把矩形平铺在一个平面上
        PI = np.pi
        PI_2 =PI/2
        R = 1.0 / self.rows     # 列数
        S = 1.0 / self.cols     # 行数   
        self.vCount=self.cols * self.rows * 6            # 每个格子两个三角形,每个三角形3个顶点
        for j in range(self.rows):
            for i in range(self.cols):
                #计算当前格子左上侧点坐标
                zsx=-S*self.cols/2+i*S   # 
                zsy=(R*self.rows/2-j*R)    #   
                zsz=0
                self.vertexs=np.hstack((self.vertexs, np.array([zsx,zsy,zsz], np.float32) ))   #每个顶点xyz三个坐标,6个顶点
                self.vertexs=np.hstack((self.vertexs, np.array([zsx,zsy-R,zsz], np.float32) ))
                self.vertexs=np.hstack((self.vertexs, np.array([zsx+S,zsy,zsz], np.float32) ))
            
                self.vertexs=np.hstack((self.vertexs, np.array([zsx+S,zsy,zsz], np.float32) ))
                self.vertexs=np.hstack((self.vertexs, np.array([zsx,zsy-R,zsz], np.float32) ))
                self.vertexs=np.hstack((self.vertexs, np.array([zsx+S,zsy-R,zsz], np.float32) ))            
        
        sizew=1.0/self.rows 
        sizeh=1.0/self.cols
        for i in range(self.cols):
            for j in range(self.rows):
            #每行列一个矩形,由两个三角形构成,共六个点,12个纹理坐标
                s=j*sizew
                t=i*sizeh   # 纹理
                self.texcoords=np.hstack((self.texcoords, numpy.array([s,t,s,t+sizeh,s+sizew,t,s+sizew,t,s,t+sizeh,s+sizew,t+sizeh], numpy.float32) ))                           
        #-------------生成旗帜数据库完毕-----------


    def Image_Open(self):       
        img = Image.open(self.imgfilename) 
        imgData = np.array(list(img.getdata()), np.int8)
        texture = glGenTextures(1)
        glPixelStorei(GL_UNPACK_ALIGNMENT,1)
        glBindTexture(GL_TEXTURE_2D, texture)
        glPixelStorei(GL_UNPACK_ALIGNMENT,1)
        glTexParameterfv(GL_TEXTURE_2D,GL_TEXTURE_BORDER_COLOR, 1,0,0)    # 如果用GL_CLAMP_TO_BORDER,还需要指定一个边缘颜色,超出的坐标为用户指定的边缘颜色。
        glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)
        glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)
        glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE)
        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_RGBA, GL_UNSIGNED_BYTE, imgData)
        glBindTexture(GL_TEXTURE_2D, 0)
        self.texid1 = texture   


    def __init__(self,rows,cols,imgfilename,colorflag):
        self.rows=rows
        self.cols=cols
        self.imgfilename = imgfilename  
        self.colorflag=colorflag   
        self.make_Mode()
        # load shaders
        self.program = myGL_Funcs.loadShaders(SphereVS, SphereFS)
        glUseProgram(self.program)
        self.vertIndex = glGetAttribLocation(self.program, b"aPosition")
                    # set up vertex array object (VAO)
        self.vao = glGenVertexArrays(1)
        glBindVertexArray(self.vao)            
                    # set up VBOs
        vertexData = numpy.array(self.vertexs, numpy.float32)
        self.vertexBuffer = glGenBuffers(1)
        glBindBuffer(GL_ARRAY_BUFFER, self.vertexBuffer)
        glBufferData(GL_ARRAY_BUFFER, 4*len(vertexData), vertexData, GL_STATIC_DRAW)                 
                    # enable arrays
        glEnableVertexAttribArray(self.vertIndex)
         # Position attribute
        glBindBuffer(GL_ARRAY_BUFFER, self.vertexBuffer)
        glVertexAttribPointer(self.vertIndex, 3, GL_FLOAT, GL_FALSE, 0,None)               
        # 顶点纹理属性
        if (self.colorflag ==-1):
            texData = numpy.array(self.texcoords, numpy.float32)
            self.texBuffer = glGenBuffers(1)
            glBindBuffer(GL_ARRAY_BUFFER, self.texBuffer)
            glBufferData(GL_ARRAY_BUFFER, 4*len(texData), texData, GL_STATIC_DRAW)           
            glEnableVertexAttribArray(1)
            glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE,  0,None)
            self.Image_Open()
         # unbind VAO
        glBindVertexArray(0)
        glBindBuffer(GL_ARRAY_BUFFER, 0)    
        
        
    def render(self, xx, yy, zz,  scale,pMatrix,theta1,WIDTH_SPAN):  
        # use shader
        glUseProgram(self.program)

        # set modelview matrix
        glUniformMatrix4fv(glGetUniformLocation(self.program, 'uMVPMatrix'), 
                          1, GL_FALSE, mvMatrix)
        glUniform1f(glGetUniformLocation(self.program, "uStartAngle"), theta1)
        glUniform1f(glGetUniformLocation(self.program, "uWidthSpan"), WIDTH_SPAN)
 
        glUniform1f(glGetUniformLocation(self.program, "a"), xx)
        glUniform1f(glGetUniformLocation(self.program, "b"), yy)
        glUniform1f(glGetUniformLocation(self.program, "c"), zz) 
        glUniform1f(glGetUniformLocation(self.program, "scale"), scale)
        glUniform1i(glGetUniformLocation(self.program, "colorflag"), self.colorflag)
        #// 启用多个纹理单元 绑定纹理对象
        if (self.colorflag ==-1):
            glActiveTexture(GL_TEXTURE0);
            glBindTexture(GL_TEXTURE_2D, self.texid1);
            glUniform1i(glGetUniformLocation(self.program, "tex"), 0); #// 设置纹理单元为0号
        # bind VAO
        glBindVertexArray(self.vao)
        # draw
        glDrawArrays(GL_TRIANGLES, 0,self.rows*self.cols*6 )
        # unbind VAO
        glBindVertexArray(0)
        

# glfw callback functions
def window_resize(window, width, height):
    glViewport(0, 0, width, height)

# initializing glfw library
if not glfw.init():
    raise Exception("glfw can not be initialized!")

# creating the window
window = glfw.create_window(640, 480, "My OpenGL window", None, None)

# check if window was created
if not window:
    glfw.terminate()
    raise Exception("glfw window can not be created!")

# set window's position
glfw.set_window_pos(window, 100, 100)

# set the callback function for window resize
glfw.set_window_size_callback(window, window_resize)

# make the context current
glfw.make_context_current(window)

glClearColor(0, 0.1, 0.1, 1)
glEnable(GL_DEPTH_TEST)
glEnable(GL_BLEND)
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)

mvMatrix=np.matrix(np.identity(4))
theta1=0
WIDTH_SPAN=-0.5
board1=BoardMode(40,40,"flag.jpg",-1) 
mvMatrix = pyrr.Matrix44.from_x_rotation(0.1) 

# the main application loop
while not glfw.window_should_close(window):
    currentFrame =  2.0*glfw.get_time()
    glfw.poll_events()   
    glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT)
    
    theta1=currentFrame
    board1.render( 0, 0, 0, 1 ,mvMatrix, theta1,WIDTH_SPAN)

    glfw.swap_buffers(window)

# terminate glfw, free up allocated resources
glfw.terminate()

六、参考资料

1、大龙10的简书:https://www.jianshu.com/p/49dec482a291
2、吴亚峰《OpenGL ES 3.x游戏开发》(下卷)

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,602评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,442评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,878评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,306评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,330评论 5 373
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,071评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,382评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,006评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,512评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,965评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,094评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,732评论 4 323
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,283评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,286评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,512评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,536评论 2 354
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,828评论 2 345

推荐阅读更多精彩内容