Python之OpenGL笔记(17):键盘鼠标控制摄像机移动旋转

一、目的

1、键盘控制摄像机自由移动;
2、鼠标控制摄像机旋转;
3、鼠标滚轮放大缩小视野。

二、程序运行结果

在这里插入图片描述

三、自由移动

view = lookAt(cameraPos, cameraPos + cameraFront, cameraUp)
cameraPos为摄像机位置
cameraPos + cameraFront为物体位置
  当我们按下WASD键,摄像机的位置都会相应更新。如果我们希望向前或向后移动,我们就把位置向量加上或减去方向向量。如果我们希望向旁边移动,我们做一个叉乘来创建一个右向量,沿着它移动就可以了。这样就创建了类似使用摄像机横向、前后移动的效果。
  注意,我们对右向量进行了标准化。如果我们没对这个向量进行标准化,最后的叉乘结果会根据cameraFront变量的大小返回不同的大小。如果我们不对向量进行标准化,我们就得根据摄像机的方位加速或减速移动了,但假如进行了标准化移动就是匀速的。
  一个技巧是只在回调函数中跟踪哪个键被按下/释放。在游戏循环中我们读取这些值,检查那个按键被激活了,然后做出相应反应。我们只储存哪个键被按下/释放的状态信息,在游戏循环中对状态做出反应,我们来创建一个布尔数组代表按下/释放的键:

四、视角移动

  欧拉角(Euler Angle)是表示3D空间中可以表示任何旋转的三个值,由莱昂哈德·欧拉在18世纪提出。有三种欧拉角:俯仰角(Pitch)、偏航角(Yaw)和滚转角(Roll),
  俯仰角是描述我们如何往上和往下看的角(X轴,Pitch)。
  偏航角表示我们往左和往右看的大小(Y轴,Yaw)。
  滚转角代表我们如何翻滚摄像机(Z轴,Roll)。
  用一个给定的俯仰角和偏航角,我们可以把它们转换为一个代表新的方向向量的3D向量。
  front.x = cos(glm::radians(pitch)) * cos(glm::radians(yaw))
  // 注意我们先把角度转为弧度,direction代表摄像机的前轴

五、鼠标输入

  偏航角和俯仰角是从鼠标移动获得的,鼠标水平移动影响偏航角,鼠标垂直移动影响俯仰角。它的思想是储存上一帧鼠标的位置,在当前帧中我们当前计算鼠标位置和上一帧的位置相差多少。如果差别越大那么俯仰角或偏航角就改变越大。
  在处理FPS风格的摄像机鼠标输入的时候,我们必须在获取最终的方向向量之前做下面这几步:
1、计算鼠标和上一帧的偏移量。
2、把偏移量添加到摄像机和俯仰角和偏航角中。
3、对偏航角和俯仰角进行最大和最小值的限制。
4、计算方向向量。

六、缩放

  我们还要往摄像机系统里加点东西,实现一个缩放接口。前面教程中我们说视野(Field of View或fov)定义了我们可以看到场景中多大的范围。当视野变小时可视区域就会减小,产生放大了的感觉。我们用鼠标滚轮来放大。和鼠标移动、键盘输入一样我们需要一个鼠标滚轮的回调函数。
void scroll_callback(GLFWwindow* window, double xoffset, double yoffset)
  yoffset值代表我们滚动的大小。当scroll_callback函数调用后,我们改变全局aspect变量的内容。因为45.0f是默认的fov,我们将会把缩放级别限制在1.0f到45.0f。
  我们现在在每一帧都必须把透视投影矩阵上传到GPU,但这一次使aspect变量作为它的fov:
projection = glm::perspective(aspect, (GLfloat)WIDTH/(GLfloat)HEIGHT, 0.1f, 100.0f);

七、源代码

"""
glfw_cube06.py
Author: dalong10
Description: Draw 4 Cube, learning OPENGL 
"""
import glutils    #Common OpenGL utilities,see glutils.py
import sys, random, math
import OpenGL
from OpenGL.GL import *
from OpenGL.GL.shaders import *
import numpy 
import numpy as np
import glfw

strVS = """
#version 330 core
layout(location = 0) in vec3 position;
layout (location = 1) in vec2 inTexcoord;
out vec2 outTexcoord;
uniform mat4 uMVMatrix;
uniform mat4 uPMatrix;
uniform float a;
uniform float b;
uniform float c;
uniform float scale;
uniform float theta;

void main(){
	mat4 rot1=mat4(vec4(1.0, 0.0,0.0,0),
				vec4(0.0, 1.0,0.0,0),
				vec4(0.0,0.0,1.0,0.0),
				vec4(a,b,c,1.0));
	mat4 rot2=mat4(vec4(scale, 0.0,0.0,0.0),
					vec4(0.0, scale,0.0,0.0),
					vec4(0.0,0.0,scale,0.0),
					vec4(0.0,0.0,0.0,1.0));
	mat4 rot3=mat4( vec4(0.5+0.5*cos(theta),  0.5-0.5*cos(theta), -0.707106781*sin(theta), 0),
				   vec4(0.5-0.5*cos(theta),0.5+0.5*cos(theta), 0.707106781*sin(theta),0),
				vec4(0.707106781*sin(theta), -0.707106781*sin(theta),cos(theta), 0.0),
				vec4(0.0,         0.0,0.0, 1.0));
	gl_Position=uPMatrix * uMVMatrix * rot2 *rot1 *rot3 * vec4(position.x, position.y, position.z, 1.0);
    outTexcoord = inTexcoord;
	}
"""

strFS = """
#version 330 core
out vec4 FragColor;
in vec2 outTexcoord;
uniform sampler2D texture1;
void main(){
    FragColor = texture(texture1, outTexcoord);
	}
"""

class FirstCube:
    def __init__(self, side):
        self.side = side

        # load shaders
        self.program = glutils.loadShaders(strVS, strFS)
        glUseProgram(self.program)
        # attributes
        self.vertIndex = glGetAttribLocation(self.program, b"position")
        self.texIndex = glGetAttribLocation(self.program, b"inTexcoord")
        
        s = side/2.0
        cube_vertices = [
            -s, -s, -s, 
             s, -s, -s,
             s, s, -s,
             s, s, -s,
             -s, s, -s,
             -s, -s, -s,
             
             -s, -s, s, 
             s, -s, s,
             s, s, s,
             s, s, s,
             -s, s, s,
             -s, -s, s,

             -s, s, s, 
             -s, s, -s,
             -s, -s, -s,
             -s, -s, -s,
             -s, -s, s,
             -s, s, s,

             s, s, s, 
             s, s, -s,
             s, -s, -s,
             s, -s, -s,
             s, -s, s,
             s, s, s,

             -s, -s, -s, 
             s, -s, -s,
             s, -s, s,
             s, -s, s,
             -s, -s, s,
             -s, -s, -s,

             -s, s, -s, 
             s, s,-s,
             s, s, s,
             s, s, s,
             -s, s, s,
             -s, s,-s
             ]
        # texture coords
        t=1.0
        quadT = [
            0,0, t,0, t,t, t,t, 0,t, 0,0, 
            0,0, t,0, t,t, t,t, 0,t, 0,0, 
            t,0, t,t, 0,t, 0,t, 0,0, t,0, 
            t,0, t,t, 0,t, 0,t, 0,0, t,0,  
            0,t, t,t, t,0, t,0, 0,0, 0,t, 
            0,t, t,t, t,0, t,0, 0,0, 0,t
            ]               
        # set up vertex array object (VAO)
        self.vao = glGenVertexArrays(1)
        glBindVertexArray(self.vao)
            
        # set up VBOs
        vertexData = numpy.array(cube_vertices, numpy.float32)
        self.vertexBuffer = glGenBuffers(1)
        glBindBuffer(GL_ARRAY_BUFFER, self.vertexBuffer)
        glBufferData(GL_ARRAY_BUFFER, 4*len(vertexData), vertexData, GL_STATIC_DRAW)
        
        tcData = numpy.array(quadT, numpy.float32)
        self.tcBuffer = glGenBuffers(1)
        glBindBuffer(GL_ARRAY_BUFFER, self.tcBuffer)
        glBufferData(GL_ARRAY_BUFFER, 4*len(tcData), tcData,GL_STATIC_DRAW)
        # enable arrays
        glEnableVertexAttribArray(self.vertIndex)
        glEnableVertexAttribArray(self.texIndex)
        # Position attribute
        glBindBuffer(GL_ARRAY_BUFFER, self.vertexBuffer)
        glVertexAttribPointer(self.vertIndex, 3, GL_FLOAT, GL_FALSE, 0,None)
        
        # TexCoord attribute
        glBindBuffer(GL_ARRAY_BUFFER, self.tcBuffer)        
        glVertexAttribPointer(self.texIndex, 2, GL_FLOAT, GL_FALSE, 0,None)
        
        # unbind VAO
        glBindVertexArray(0)
        glBindBuffer(GL_ARRAY_BUFFER, 0)    

    def render(self,pMatrix,mvMatrix,texid,a,b,c,scale,r):       
        self.texid = texid
        # enable texture
        glActiveTexture(GL_TEXTURE0)
        glBindTexture(GL_TEXTURE_2D, self.texid)
        # use shader
        # set proj matrix
        glUniformMatrix4fv(glGetUniformLocation(self.program, 'uPMatrix'), 
                          1, GL_FALSE, pMatrix)       
        # set modelview matrix
        glUniformMatrix4fv(glGetUniformLocation(self.program, 'uMVMatrix'), 
                          1, GL_FALSE, mvMatrix)
        glUseProgram(self.program)
        glUniform1f(glGetUniformLocation(self.program, "a"), a)
        glUniform1f(glGetUniformLocation(self.program, "b"), b)
        glUniform1f(glGetUniformLocation(self.program, "c"), c)
        glUniform1f(glGetUniformLocation(self.program, "scale"), scale)
        theta = r*PI/180.0
        glUniform1f(glGetUniformLocation(self.program, "theta"), theta)
        # bind VAO
        glBindVertexArray(self.vao)
        glEnable(GL_DEPTH_TEST)
        # draw
        glDrawArrays(GL_TRIANGLES, 0, 36)
        # unbind VAO
        glBindVertexArray(0)

#Is called whenever a key is pressed/released via GLFW
def on_key(window, key, scancode, action, mods):
    if key == glfw.KEY_ESCAPE and action == glfw.PRESS:
        glfw.set_window_should_close(window,1)
    elif key >0  and key<1024 :
        if action == glfw.PRESS:
            keys[key]=True
        elif (action == glfw.RELEASE):
            keys[key]=False 

def do_movement():
    global cameraPos
    global cameraFront
    global cameraUp
    global deltaTime
    
    #Camera controls
    cameraSpeed = 5.0 * deltaTime
    if (keys[glfw.KEY_W]):
        cameraPos += cameraSpeed * cameraFront
        print(cameraPos)
    if (keys[glfw.KEY_S]):
        cameraPos -= cameraSpeed * cameraFront
    if (keys[glfw.KEY_A]):
        # normalize up vector
        norm = numpy.linalg.norm(cameraFront)
        cameraFront /= norm
        norm = np.linalg.norm(cameraUp)
        cameraUp /= norm
        # Side = forward x up 
        side = np.cross(cameraFront, cameraUp)
        cameraPos -= cameraSpeed * side
    if (keys[glfw.KEY_D]):
        # normalize up vector
        norm = numpy.linalg.norm(cameraFront)
        cameraFront /= norm
        norm = np.linalg.norm(cameraUp)
        cameraUp /= norm
        # Side = forward x up 
        side = np.cross(cameraFront, cameraUp)
        cameraPos += cameraSpeed * side
        
def mouse_callback(window, xpos, ypos):
    global cameraFront
    global firstMouse
    global lastX
    global lastY
    global yaw
    global pitch
    #print('mouse button: ', window, xpos, ypos)
    if (firstMouse==True):
        lastX = xpos
        lastY = ypos
        firstMouse = False
    xoffset = xpos - lastX
    yoffset = lastY - ypos
    lastX = xpos
    lastY = ypos
    #camera.ProcessMouseMovement(xoffset, yoffset)
    sensitivity = 0.05
    xoffset *= sensitivity
    yoffset *= sensitivity
    yaw   += xoffset
    pitch += yoffset
    if(pitch > 89.0):
        pitch = 89.0
    if(pitch < -89.0):
        pitch = -89.0
    cameraFront=[math.cos(math.radians(yaw)) * math.cos(math.radians(pitch)),
                math.sin(math.radians(pitch)),
                math.sin(math.radians(yaw))* math.cos(math.radians(pitch)) ]    
    norm = numpy.linalg.norm(cameraFront)
    cameraFront /= norm
    
def scroll_callback(window, xoffset, yoffset):
    global aspect
    if(aspect >= 1.0 and aspect <= 45.0):
        aspect -= yoffset
    if(aspect <= 1.0):
        aspect=1.0
    if(aspect >= 45.0):
        aspect=45.0
    print('aspect=',aspect)

if __name__ == '__main__':
    import sys
    import glfw
    import OpenGL.GL as gl
    global cameraPos
    global cameraFront
    global cameraUp
    global deltaTime
    global firstMouse
    global lastX
    global lastY
    global yaw
    global pitch
    global aspect
    
    keys=numpy.zeros(1024)
    deltaTime = 0.0
    lastFrame = 0.0   # Time of last frame
    firstMouse = True
    lastX= 400
    lastY = 300
    yaw   = -90.0
    pitch =   0.0
    aspect=45.0
    
    camera = glutils.Camera([0.0, 0.0, 5.0],
                             [0.0, 0.0, 0.0],
                             [0.0, 1.0, 0.0])
    cameraPos  =numpy.array([0,0,3.0], numpy.float32)                        
    cameraFront=numpy.array([0,0,-1.0], numpy.float32)        
    cameraUp   =numpy.array([0,1.0,0], numpy.float32)        
            

    # Initialize the library
    if not glfw.init():
        sys.exit()

    # Create a windowed mode window and its OpenGL context
    window = glfw.create_window(800, 600, "draw Cube ", None, None)
    if not window:
        glfw.terminate()
        sys.exit()

    # Make the window's context current
    glfw.make_context_current(window)
    # Install a key handler
    glfw.set_key_callback(window, on_key)
    # set window  mouse callbacks
    glfw.set_cursor_pos_callback(window,  mouse_callback)
    glfw.set_scroll_callback(window,  scroll_callback)
    
    PI = 3.14159265358979323846264
    texid = glutils.loadTexture("container2.png")
    # Loop until the user closes the window
    a=0
    firstCube0 = FirstCube(1.0)
    while not glfw.window_should_close(window):
        currentFrame = glfw.get_time()
        deltaTime = currentFrame - lastFrame       
        lastFrame = currentFrame
        glfw.poll_events()
        do_movement()
        # Render here
        width, height = glfw.get_framebuffer_size(window)
        ratio = width / float(height)
        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)                
        i=a 
        camera.eye=[5*math.sin(glfw.get_time()), 0 , 5* math.cos(glfw.get_time()) ]
        # modelview matrix
        mvMatrix = glutils.lookAt(cameraPos, cameraPos+cameraFront, cameraUp)
        pMatrix = glutils.perspective(aspect, ratio, 0.1, 100.0)
        glBindTexture(GL_TEXTURE_2D, texid)              
        firstCube0.render(pMatrix, mvMatrix,texid,0.0,1,0,0.4,i)
        firstCube0.render(pMatrix, mvMatrix,texid,1.0,0,0.4,0.5,i+30)
        firstCube0.render(pMatrix, mvMatrix,texid,0.0,-1,-0.5,0.3,i+60)
        firstCube0.render(pMatrix, mvMatrix,texid,-1.0,0,0.2,0.2,i+120)
                 
        # Swap front and back buffers
        glfw.swap_buffers(window)       
        # Poll for and process events
        glfw.poll_events()

    glfw.terminate()

八、参考文献

1、learnopengl教程 https://learnopengl-cn.readthedocs.io/zh/latest/01%20Getting%20started/09%20Camera/
2、大龙10简书 https://www.jianshu.com/p/4382b25ad797

  • 1
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值