Python之OpenGL笔记(31):扭动的软糖

38 篇文章 66 订阅

一、目的

1、顶点着色器的妙用,画一个扭动的软糖;

二、程序运行结果

三、基本原理

   吴亚峰《OpenGL ES 3.x游戏开发》(下卷)内容
   从图 2-4 中可以看出,软糖模型实际上是由很多层小矩形叠加而成。在同一帧中,随着 y 坐标的不断升高,此层的顶点绕中心轴扭曲的角度越大。因此,实现扭动软糖的效果只要将代表软糖的长方体中各层顶点的 x、z 坐标按照一定的规则根据顶点的 y 坐标以及当前帧的控制参数进行变换即可,具体的计算思路如图 2-5、图 2-6 与图 2-7 所示。
   具体的计算步骤如下:
   1、首先如图 2-5 所示,需要计算出当前顶点 y 坐标与最下层顶点 y 坐标的差值。

   2、接着根据 y 坐标的差值、角度换算比例以及本帧的总扭曲角度换算出当前顶点的扭曲角度,计算公式为: currAngle =(currY-yStart)/ySpan×angleSpan。
   3、最后根据当前顶点的 x、 z 坐标以及扭曲的角度计算出变换后顶点的 x、 z 坐标,此步的计算思路如图 2-6 与图 2-7 所示。

   4、从图 2-6 与图 2-7 中可以看出,将顶点绕中心轴扭曲(旋转)实际上可以看成是将从中心点出发到变换前顶点的向量旋转指定的角度。旋转后得到的新向量的终点位置即所求的变换后顶点的位置,因此具体的计算公式如下。
   x’=xcosα-zsinα
   z’=xsinα+zcosα
   上述公式中的α为需要旋转(扭曲)的角度,实际计算时采用前面步骤计算出来的变量 currAngle 的值即可。

四、源代码

"""
程序名称:GL_textures05.py
编程: dalong10
功能: 扭动的软糖的实现
参考资料: 《OpenGL ES 3.x游戏开发》(下卷)吴亚峰
"""
import myGL_Funcs    #Common OpenGL utilities,see myGL_Funcs.py
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 angleSpan;//本帧扭曲总角度
uniform float yStart;//Y坐标起始点
uniform float ySpan;//Y坐标总跨度

out vec2 passTexCoord;
void main(){
   float tempAS= angleSpan*(aPosition.y-yStart)/ySpan;//计算当前顶点扭动(绕中心点选择)的角度
   vec3 tPosition=aPosition;
  
   if(aPosition.y>yStart)
   {//若不是最下面一层的顶点则计算扭动后的X、Z坐标
     tPosition.x=(cos(tempAS)*aPosition.x-sin(tempAS)*aPosition.z);
     tPosition.z=(sin(tempAS)*aPosition.x+cos(tempAS)*aPosition.z);
   }
   gl_Position = uMVPMatrix * vec4(tPosition,1); //根据总变换矩阵计算此次绘制此顶点的位置
   passTexCoord = inTexCoord; //将接收的纹理坐标传递给片元着色器
	}
"""

SphereFS = """
#version 330 core
precision mediump float;
uniform sampler2D tex; //纹理内容数据
in vec2 passTexCoord; //接收从顶点着色器过来的参数
out vec4 outColor;    //输出到的片元颜色
void main(){
   //给此片元从纹理中采样出颜色值            
   outColor = texture(tex, passTexCoord);
    	}
"""

class CuboMode:
    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
        Y_MAX=1.5*0.5     #  
        Y_MIN=-1.5 *0.5    #   
        FD=6 
        hw=0.5 *0.5
        self.vCount=FD*4*6            # 每个格子两个三角形,每个三角形3个顶点
        yStart=Y_MIN
        ySpan=(Y_MAX-Y_MIN)/FD
        count=0
        tCount=0
        for i in range(FD):
            x1=-hw
            y1=yStart
            z1=hw
            x2=hw
            y2=yStart
            z2=hw
            x3=hw
            y3=yStart
            z3=-hw
            x4=-hw
            y4=yStart
            z4=-hw
             
            x5=-hw
            y5=yStart+ySpan
            z5=hw
            x6=hw
            y6=yStart+ySpan
            z6=hw
            x7=hw
            y7=yStart+ySpan
            z7=-hw
            x8=-hw
            y8=yStart+ySpan
            z8=-hw
            self.vertexs=np.hstack((self.vertexs, np.array([x5,y5,z5], np.float32) ))   #每个顶点xyz三个坐标,6个顶点//512
            self.vertexs=np.hstack((self.vertexs, np.array([x1,y1,z1], np.float32) ))    
            self.vertexs=np.hstack((self.vertexs, np.array([x2,y2,z2], np.float32) ))    
            
            self.vertexs=np.hstack((self.vertexs, np.array([x5,y5,z5], np.float32) ))   #每个顶点xyz三个坐标,6个顶点//526
            self.vertexs=np.hstack((self.vertexs, np.array([x2,y2,z2], np.float32) ))    
            self.vertexs=np.hstack((self.vertexs, np.array([x6,y6,z6], np.float32) ))    
        
            self.vertexs=np.hstack((self.vertexs, np.array([x6,y6,z6], np.float32) ))   #每个顶点xyz三个坐标,6个顶点//623
            self.vertexs=np.hstack((self.vertexs, np.array([x2,y2,z2], np.float32) ))    
            self.vertexs=np.hstack((self.vertexs, np.array([x3,y3,z3], np.float32) ))    

            self.vertexs=np.hstack((self.vertexs, np.array([x6,y6,z6], np.float32) ))   #每个顶点xyz三个坐标,6个顶点//637
            self.vertexs=np.hstack((self.vertexs, np.array([x3,y3,z3], np.float32) ))    
            self.vertexs=np.hstack((self.vertexs, np.array([x7,y7,z7], np.float32) ))    

            self.vertexs=np.hstack((self.vertexs, np.array([x7,y7,z7], np.float32) ))    
            self.vertexs=np.hstack((self.vertexs, np.array([x3,y3,z3], np.float32) ))    
            self.vertexs=np.hstack((self.vertexs, np.array([x4,y4,z4], np.float32) ))    

            self.vertexs=np.hstack((self.vertexs, np.array([x7,y7,z7], np.float32) ))    
            self.vertexs=np.hstack((self.vertexs, np.array([x4,y4,z4], np.float32) ))    
            self.vertexs=np.hstack((self.vertexs, np.array([x8,y8,z8], np.float32) ))    

            self.vertexs=np.hstack((self.vertexs, np.array([x8,y8,z8], np.float32) ))    
            self.vertexs=np.hstack((self.vertexs, np.array([x4,y4,z4], np.float32) ))    
            self.vertexs=np.hstack((self.vertexs, np.array([x1,y1,z1], np.float32) ))    

            self.vertexs=np.hstack((self.vertexs, np.array([x8,y8,z8], np.float32) ))    
            self.vertexs=np.hstack((self.vertexs, np.array([x1,y1,z1], np.float32) ))    
            self.vertexs=np.hstack((self.vertexs, np.array([x5,y5,z5], np.float32) ))    

            yStart=yStart+ySpan
            self.texcoords=np.hstack((self.texcoords, numpy.array([0,0,0,1,1,1,0,0,1,1,1,0], numpy.float32) ))                           
            self.texcoords=np.hstack((self.texcoords, numpy.array([0,0,0,1,1,1,0,0,1,1,1,0], numpy.float32) ))                           
            self.texcoords=np.hstack((self.texcoords, numpy.array([0,0,0,1,1,1,0,0,1,1,1,0], numpy.float32) ))                           
            self.texcoords=np.hstack((self.texcoords, numpy.array([0,0,0,1,1,1,0,0,1,1,1,0], 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):
        self.rows=rows
        self.cols=cols
        self.imgfilename = imgfilename  
 
        self.make_Mode()
        # load shaders
        self.program = myGL_Funcs.loadShaders(SphereVS, SphereFS)
        #print('ok1')
        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)               
        # 顶点纹理属性
        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, pMatrix,angleSpan,yStart,ySpan):  
        # use shader
        glUseProgram(self.program)

        # set modelview matrix
        glUniformMatrix4fv(glGetUniformLocation(self.program, 'uMVPMatrix'), 
                          1, GL_FALSE, mvMatrix)
        glUniform1f(glGetUniformLocation(self.program, "angleSpan"), angleSpan)
        glUniform1f(glGetUniformLocation(self.program, "yStart"), yStart)
        glUniform1f(glGetUniformLocation(self.program, "ySpan"), ySpan)

        #// 启用多个纹理单元 绑定纹理对象        
        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)


if __name__ == '__main__':
    import sys
    import glfw
    import OpenGL.GL as gl
    cameraPos=np.array([0.0, 0.0, 4])  # 眼睛的位置(默认z轴的正方向)
    cameraFront=np.array([0.0, 0.0, 0.0])  # 瞄准方向的参考点(默认在坐标原点)
    cameraUp=np.array([0.0, 1.0, 0.0])  # 定义对观察者而言的上方(默认y轴的正方向)

    # Initialize the library
    if not glfw.init():
        sys.exit()
    # Create a windowed mode window and its OpenGL context
    window = glfw.create_window(320, 240, "My OpenGL window", None, None)
    if not window:
        glfw.terminate()
        sys.exit()
    # 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=6
    board1=CuboMode(40,40,"wall.png",) 
    mvMatrix = pyrr.Matrix44.from_x_rotation(0.5)   

    # the main application loop
    while not glfw.window_should_close(window):
        width, height = glfw.get_framebuffer_size(window)
        ratio = width / float(height)
        currentFrame =  1.0*glfw.get_time()
        glfw.poll_events()           
        gl.glViewport(0, 0, width, height)       
        gl.glClear(gl.GL_COLOR_BUFFER_BIT | gl.GL_DEPTH_BUFFER_BIT)
        gl.glMatrixMode(gl.GL_PROJECTION)
        gl.glLoadIdentity()
        gl.glOrtho(-ratio, ratio, -1, 1, 1, -1)
        gl.glMatrixMode(gl.GL_MODELVIEW)
        gl.glLoadIdentity()
        gl.glClearColor(0.0,0.0,4.0,0.0)  
  
        theta1=currentFrame
        board1.render( mvMatrix, theta1,-0.6, WIDTH_SPAN)  #从y=-0.6以上开始扭

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值