Python OpenGL 使物体旋转起来

本文
引入
创建物体
三维坐标系
四边形
四边形满足不了
旋转
鼠标响应
鼠标拖拽
完整代码
友情提示
点的坐标
投影
视点
最后
引入
简单的实现一下在OpenGL中对物体从各个角度进行观察,这里不讲述具体的理论,如果对投影,坐标系不太了解的,可以看一下点击此处,这篇文章对Python下的OpenGL有详细的描述。在这里我们实现旋转的方法主要用的是LearnOpenGL CN中,通过鼠标移动实现物体的旋转。

创建物体
我们要有个物体去进行观察,这里我们创建一个四边形和一个三维坐标系。

三维坐标系
 

glBegin(GL_LINES)

# 以红色绘制x轴
glColor3f(1.0, 0.0, 0.0)
glVertex3f(-0.3, 0.0, 0.0)
glVertex3f(0.3, 0.0, 0.0)

# 以绿色绘制y轴
glColor3f(0.0, 1.0, 0.0)
glVertex3f(0.0, -0.3, 0.0)
glVertex3f(0.0, 0.3, 0.0)

# 以蓝色绘制z轴
glColor3f(0.0, 0.0, 1.0)
glVertex3f(0.0, 0.0, -0.3)
glVertex3f(0.0, 0.0, 0.3)
glEnd()

简单理解就是将坐标轴正方向和反方向的俩个点进行连线,形成了需要的三维坐标系。
在这里插入图片描述

四边形

vertices是四边形的顶点坐标。四边形有四个顶点,分别有三个坐标。

import numpy as np

vertices = np.array([
    [0.2, 0.2, 0.2],
    [-0.2, 0.2, 0.2],
    [-0.2, -0.2, 0.2],
    [0.2, -0.2, 0.2]
])
glBegin(GL_QUADS)
for point in vertices:
    glColor3f(1, 1, 0)
    glVertex3f(point[0], point[1], point[2])
glEnd()

四边形满足不了

如果四边形已经满足不了了,你想要一个立方体,那么你需要6个四边形的面去组成这个立方体,当然也可以用GL_LINESGL_TRIANGLES。不过要想要需要几个点,我们这里用四边形去组成立方体,那么我们需要8个点的坐标。

import numpy as np

vertices2 = np.array([
    [[0.2, 0.2, 0.2], [-0.2, 0.2, 0.2], [-0.2, -0.2, 0.2], [0.2, -0.2, 0.2]],  # 前
    [[0.2, 0.2, -0.2], [0.2, -0.2, -0.2], [-0.2, -0.2, -0.2], [-0.2, 0.2, -0.2]],  # 后
    [[0.2, 0.2, 0.2], [0.2, 0.2, -0.2], [-0.2, 0.2, -0.2], [-0.2, 0.2, 0.2]],  # 左
    [[0.2, -0.2, 0.2], [0.2, -0.2, -0.2], [-0.2, -0.2, -0.2], [-0.2, -0.2, 0.2]],  # 右
    [[0.2, 0.2, 0.2], [0.2, -0.2, 0.2], [0.2, -0.2, -0.2], [0.2, 0.2, -0.2]],  # 上
    [[-0.2, 0.2, 0.2], [-0.2, -0.2, 0.2], [-0.2, -0.2, -0.2], [-0.2, 0.2, -0.2]]  # 下
])

colours = np.array([
    [1, 0, 1], [1, 0, 1],
    [1, 1, 1], [1, 1, 1],
    [0, 1, 1], [0, 1, 1]
])

再加上代码:

 for i in range(vertices2.shape[0]):
     glBegin(GL_QUADS)
     points = vertices2[i, :]
     color = colours[i, :]
     for point in points:
         glColor3f(color[0], color[1], color[0])
         glVertex3f(point[0], point[1], point[2])
     glEnd()

这样我们就能实现在窗口中绘制立方体,但是只能看到平面和两条坐标轴,下面我们来实现鼠标拖动旋转,看到三维图形。

旋转
鼠标响应
我们想实现的就是当鼠标左键按下的时候,拖动鼠标,实现物体的旋转,能从各个角度观察到物体(忽略90度)。
首先我们定义以下鼠标按下的函数:
LEFT_IS_DOWNED定义鼠标状态,一开始设置为Fasle,当鼠标按下的时候改为True, x,y为鼠标当前的坐标。

LEFT_IS_DOWNED = False  
MOUSE_X, MOUSE_Y = 0, 0

def Mouse_click(button, state, x, y):
    global LEFT_IS_DOWNED
    global MOUSE_X, MOUSE_Y

    MOUSE_X = x
    MOUSE_Y = y
    if button == GLUT_LEFT_BUTTON:
        LEFT_IS_DOWNED = state == GLUT_DOWN

鼠标拖拽

我们要实现拖拽要实现下面这几步:

  1. 计算鼠标距上一帧的偏移量。
  2. 把偏移量添加到摄像机的俯仰角和偏航角中。
  3. 对偏航角和俯仰角进行最大和最小值的限制。
  4. 计算方向向量。

让我们来看代码:

# 导入库
from OpenGL.GL import *
from OpenGL.GLUT import *
from OpenGL.GLU import *
import numpy as np

LEFT_IS_DOWNED = False
CameraPos = np.array([0.0, 0.0, 2])
yaw = 0
pitch = 0
# 上面的是全局变量

# 鼠标拖拽函数
def Mouse_motion(x, y):
    global LEFT_IS_DOWNED
    global MOUSE_X, MOUSE_Y
    global yaw, pitch
    global CameraPos

    if LEFT_IS_DOWNED:  # 如果鼠标按下
    
    #  计算鼠标距上一帧的偏移量。
        dx = x - MOUSE_X
        dy = MOUSE_Y - y
        MOUSE_X = x
        MOUSE_Y = y
	
	# 这里设置以下灵敏度,防止鼠标一拖,物体转的太快
        sensitivity = 0.2
        dx = dx * sensitivity
        dy = dy * sensitivity

	# 把偏移量添加到摄像机的俯仰角和偏航角中。
        yaw = yaw + dx
        pitch = pitch + dy
	
	# 对偏航角和俯仰角进行最大和最小值的限制。
        if pitch > 89:
            pitch = 89
        if pitch < -89:
            pitch = -89
	# 计算方向向量。
        CameraPos[0] = np.cos(np.radians(yaw)) * np.cos(np.radians(pitch))
        CameraPos[1] = np.sin(np.radians(pitch))
        CameraPos[2] = np.sin(np.radians(yaw)) * np.cos(np.radians(pitch))
        
	# 在OpenGL中进行显示
        glutPostRedisplay()

鼠标拖拽和点击函数定义好之后,我们要进行调用。

完整代码

from OpenGL.GL import *
from OpenGL.GLUT import *
from OpenGL.GLU import *
import numpy as np

vertices2 = np.array([
    [[0.2, 0.2, 0.2], [-0.2, 0.2, 0.2], [-0.2, -0.2, 0.2], [0.2, -0.2, 0.2]],  # 前
    [[0.2, 0.2, -0.2], [0.2, -0.2, -0.2], [-0.2, -0.2, -0.2], [-0.2, 0.2, -0.2]],  # 后
    [[0.2, 0.2, 0.2], [0.2, 0.2, -0.2], [-0.2, 0.2, -0.2], [-0.2, 0.2, 0.2]],  # 左
    [[0.2, -0.2, 0.2], [0.2, -0.2, -0.2], [-0.2, -0.2, -0.2], [-0.2, -0.2, 0.2]],  # 右
    [[0.2, 0.2, 0.2], [0.2, -0.2, 0.2], [0.2, -0.2, -0.2], [0.2, 0.2, -0.2]],  # 上
    [[-0.2, 0.2, 0.2], [-0.2, -0.2, 0.2], [-0.2, -0.2, -0.2], [-0.2, 0.2, -0.2]]  # 下
])

colours = np.array([
    [1, 0, 1], [1, 0, 1],
    [1, 1, 1], [1, 1, 1],
    [0, 1, 1], [0, 1, 1]
])
IS_PERSPECTIVE = True  # 透视投影
VIEW = np.array([-0.8, 0.8, -0.8, 0.8, 0.5, 20.0])  # 视景体的left/right/bottom/top/near/far六个面
LEFT_IS_DOWNED = False
CameraPos = np.array([0.0, 0.0, 2])
CameraFront = np.array([0, 0, 0])
CameraUp = np.array([0, 1, 0])
SCALE_K = np.array([1, 1, 1])
yaw = 0
pitch = 0
MOUSE_X, MOUSE_Y = 0, 0
WIN_W = 640
WIN_H = 480

def init():
    glClearColor(0.0, 0.0, 0.0, 1.0)  # 设置画布背景色。注意:这里必须是4个参数
    glEnable(GL_DEPTH_TEST)  # 开启深度测试,实现遮挡关系
    glDepthFunc(GL_LEQUAL)  # 设置深度测试函数(GL_LEQUAL只是选项之一)


def show():
    global IS_PERSPECTIVE, VIEW
    global CameraPos, CameraFront, CameraUp
    global SCALE_K
    global WIN_W, WIN_H
    global vertices2

    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)

    # 设置投影(透视投影)
    glMatrixMode(GL_PROJECTION)
    glLoadIdentity()
    
    if IS_PERSPECTIVE:
        glFrustum(VIEW[0], VIEW[1], VIEW[2], VIEW[3], VIEW[4], VIEW[5])
        
    # 设置模型视图
    glMatrixMode(GL_MODELVIEW)
    glLoadIdentity()

    # 几何变换
    glScale(SCALE_K[0], SCALE_K[1], SCALE_K[2])
	
	# 视点
    gluLookAt(
        CameraPos[0], CameraPos[1], CameraPos[2],
        CameraFront[0], CameraFront[1], CameraFront[2],
        CameraUp[0], CameraUp[1], CameraUp[2]
    )

    glViewport(0, 0, WIN_W, WIN_H)

    glBegin(GL_LINES)

    # 以红色绘制x轴
    glColor3f(1.0, 0.0, 0.0)  # 设置当前颜色为红色不透明
    glVertex3f(-0.5, 0.0, 0.0)  # 设置x轴顶点(x轴负方向)
    glVertex3f(0.5, 0.0, 0.0)  # 设置x轴顶点(x轴正方向)

    # 以绿色绘制y轴
    glColor3f(0.0, 1.0, 0.0)  # 设置当前颜色为绿色不透明
    glVertex3f(0.0, -0.5, 0.0)  # 设置y轴顶点(y轴负方向)
    glVertex3f(0.0, 0.5, 0.0)  # 设置y轴顶点(y轴正方向)

    # 以蓝色绘制z轴
    glColor3f(0.0, 0.0, 1.0)  # 设置当前颜色为蓝色不透明
    glVertex3f(0.0, 0.0, -0.5)  # 设置z轴顶点(z轴负方向)
    glVertex3f(0.0, 0.0, 0.5)  # 设置z轴顶点(z轴正方向)

    glEnd()

    for i in range(vertices2.shape[0]):
        glBegin(GL_QUADS)
        points = vertices2[i, :]
        color = colours[i, :]
        for point in points:
            glColor3f(color[0], color[1], color[0])
            glVertex3f(point[0], point[1], point[2])
        glEnd()

    glutSwapBuffers()

def Mouse_click(button, state, x, y):
    global LEFT_IS_DOWNED
    global MOUSE_X, MOUSE_Y
    global SCALE_K

    MOUSE_X = x
    MOUSE_Y = y
    if button == GLUT_LEFT_BUTTON:
        LEFT_IS_DOWNED = state == GLUT_DOWN

def Mouse_motion(x, y):
    global LEFT_IS_DOWNED
    global MOUSE_X, MOUSE_Y
    global yaw, pitch
    global CameraPos

    if LEFT_IS_DOWNED:
        dx = x - MOUSE_X
        dy = MOUSE_Y - y
        MOUSE_X = x
        MOUSE_Y = y

        sensitivity = 0.2
        dx = dx * sensitivity
        dy = dy * sensitivity

        yaw = yaw + dx
        pitch = pitch + dy

        if pitch > 89:
            pitch = 89
        if pitch < -89:
            pitch = -89

        CameraPos[0] = np.cos(np.radians(yaw)) * np.cos(np.radians(pitch))
        CameraPos[1] = np.sin(np.radians(pitch))
        CameraPos[2] = np.sin(np.radians(yaw)) * np.cos(np.radians(pitch))
        
        glutPostRedisplay()

if __name__ == '__main__':
    glutInit()
    displayMode = GLUT_DOUBLE | GLUT_ALPHA | GLUT_DEPTH
    glutInitDisplayMode(displayMode)
    glutInitWindowSize(WIN_W, WIN_H)
    glutInitWindowPosition(300, 200)
    glutCreateWindow("OpenGL")

    init()
    glutDisplayFunc(show)
    glutMouseFunc(Mouse_click)
    glutMotionFunc(Mouse_motion)
    glutMainLoop()


到这里,如果运行上面的代码,点击鼠标进行拖拽之后,会看到下面这个图像:


友情提示
点的坐标
点的坐标也可以自由发挥一下,不过要想好点的顺序,要不然出来的形状可能会是下面这个样子:


投影
想要看到完整的立方体,投影是必不可少的,否则会看到奇怪的图像,例如:


视点
视点的参数设置非常重要,设置合适的参数,能更好的对物体进行观察,可以自行更改一下参数,看一下变动后的效果。

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值