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游戏开发》(下卷)

你可能感兴趣的:(Python之OpenGL笔记(30):飘扬的旗帜)