目录
引言
博主是北京理工大学计算机2021级研一的学生,这项任务是计算机图形学这门课程的第一个大作业。由于之前并未接触过opengl,所以在完成这个作业过程中费了一些劲。因为网上找不到符合老师需求的代码,所以代码都是自己拼拼凑凑整的,可能仍有不规范之处。当然,这篇博客是在这门课全结束之后才发布的。
依赖
- python3.9
- opengl库
估计python3.7、3.8也都行,但没试过。具体导入库的代码如下:
from OpenGL.GL import *
from OpenGL.GLUT import *
from OpenGL.GLU import *
import numpy as np
画方块
这里使用的是opengl自带的画面函数进行绘制的。
首先给出每个面的点坐标:
vertices2 = 1.4*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]] # 下
])
这里的1.4倍是为了调整大小关系,可根据实际情况调整这个系数。
再给出每个面的颜色:
colours = np.array([
[0, 1, 1], [1, 0.5, 0.5],
[1, 1, 0], [0.1, 0.1, 1],
[0, 1, 0.2], [0.6, 0.6, 0.6]
])
这里的三个数就分别代表RGB的红绿蓝,也可自行调整。
最后再show函数里画出这个立方体。
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[2])
glVertex3f(point[0], point[1], point[2])
glEnd()
屏幕坐标转换为世界坐标
这一步试过很多错,网上大部分代码都是基于C++的。
完成转换主要通过opengl里的反映射函数:gluUnProject.
gluUnProject(MOUSE_X, viewport[3] - MOUSE_Y, z, modelview_mat, projection_mat, viewport)
该函数通过调用鼠标点击的位置信息(MOUSE_X,MOUSE_Y)、图片深度信息(z)、三个转换矩阵(modelview_mat, projection_mat, viewport)进行计算。
B样条曲线绘制
这一步试过更多的错。
代码部分很简单,就是利用gluNurbsCurve函数来进行B样条曲线的绘制。其中,NURBS是非均匀有理B样条(Non-Uniform Rational B-Splines)的缩写。
gluNurbsCurve(nurb, KNOTS, POINTS, GL_MAP1_VERTEX_3)
POINTS就是B样条中的控制点,也就是鼠标点击后得到的点坐标。但由于之前KNOTS一直没有配置正确,导致函数无法画出曲线。
KNOTS可直接根据控制点的数量进行生成。
degree = 3
knotNum = len(POINTS) + degree
KNOTS = [float(i)/(knotNum-1) for i in range(knotNum)]
完整代码
from OpenGL.GL import *
from OpenGL.GLUT import *
from OpenGL.GLU import *
import numpy as np
vertices2 = 1.4*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([
[0, 1, 1], [1, 0.5, 0.5],
[1, 1, 0], [0.1, 0.1, 1],
[0, 1, 0.2], [0.6, 0.6, 0.6]
])
IS_PERSPECTIVE = True # 透视投影
VIEW = np.array([-0.5, 0.5, -0.5, 0.5, 0.5, 20.0]) # 视景体的left/right/bottom/top/near/far六个面
RIGHT_IS_DOWNED = False
CameraPos = np.array([0.0, 0.0, 1])
CameraFront = np.array([0, 0, 0])
CameraUp = np.array([0, 1, 0])
SCALE_K = np.array([1.0, 1.0, 1.0])
yaw = 0
pitch = 0
MOUSE_X, MOUSE_Y = 0, 0
WIN_W = 480
WIN_H = 480
POINTS = np.array([[0, 0, 0]])
def init():
glClearColor(0.0, 0.0, 0.0, 1.0) # 设置画布背景色。注意:这里必须是4个参数
glEnable(GL_DEPTH_TEST) # 开启深度测试,实现遮挡关系
glDepthFunc(GL_LEQUAL) # 设置深度测试函数(GL_LEQUAL只是选项之一)
global nurb
nurb = gluNewNurbsRenderer()
global samplingTolerance
gluNurbsProperty(nurb, GLU_SAMPLING_TOLERANCE, samplingTolerance)
nurb=None
samplingTolerance=1.0
def show():
global IS_PERSPECTIVE, VIEW
global CameraPos, CameraFront, CameraUp
global SCALE_K
global WIN_W, WIN_H
global vertices2
global POINTS
global KNOTS
global nurb
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)
n = POINTS.shape[0]
for i in range(n):
glPointSize(3.0)
glColor3f(1.0, 0.0, 0.0)
glBegin(GL_POINTS)
glVertex3f(POINTS[i, 0], POINTS[i, 1], POINTS[i, 2])
glEnd()
#连续线段连接
glLineWidth(0.2)
glColor3f(1.0, 1.0, 1.0)
glBegin(GL_LINE_STRIP)
for i in range(n):
glVertex3f(POINTS[i, 0], POINTS[i, 1], POINTS[i, 2])
glEnd()
if n > 2:
degree = 3
knotNum = len(POINTS) + degree
KNOTS = [float(i)/(knotNum-1) for i in range(knotNum)]
gluBeginCurve(nurb)
glColor3f(0, 0, 0)
glLineWidth(7.0)
gluNurbsCurve(nurb, KNOTS, POINTS, GL_MAP1_VERTEX_3)
gluEndCurve(nurb)
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[2])
glVertex3f(point[0], point[1], point[2])
glEnd()
glutSwapBuffers()
def Mouse_click(button, state, x, y):
global RIGHT_IS_DOWNED
global MOUSE_X, MOUSE_Y
global SCALE_K
global WIN_W
global WIN_H
global POINTS
MOUSE_X = x
MOUSE_Y = y
if button == GLUT_LEFT_BUTTON and state == 0:
modelview_mat = OpenGL.GL.glGetDoublev(OpenGL.GL.GL_MODELVIEW_MATRIX)
projection_mat = OpenGL.GL.glGetDoublev(OpenGL.GL.GL_PROJECTION_MATRIX)
viewport = OpenGL.GL.glGetIntegerv(OpenGL.GL.GL_VIEWPORT)
z = OpenGL.GL.glReadPixels(MOUSE_X, viewport[3] - MOUSE_Y, 1, 1, OpenGL.GL.GL_DEPTH_COMPONENT, OpenGL.GL.GL_FLOAT)
ret = gluUnProject(MOUSE_X, viewport[3] - MOUSE_Y, z, modelview_mat, projection_mat, viewport)
ret_paint = [[ret[0], ret[1], ret[2]]]
if abs(ret[0]) < 0.3 and abs(ret[1]) < 0.3 and abs(ret[2]) < 0.3: #方块之外的点不描出
POINTS = np.append(POINTS, ret_paint, axis=0)
if button == GLUT_RIGHT_BUTTON:
RIGHT_IS_DOWNED = state == GLUT_DOWN
def Mouse_motion(x, y):
global RIGHT_IS_DOWNED
global MOUSE_X, MOUSE_Y
global yaw, pitch
global CameraPos
if RIGHT_IS_DOWNED:
dx = x - MOUSE_X
dy = y - MOUSE_Y
MOUSE_X = x
MOUSE_Y = y
sensitivity = 0.4
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("CUBE")
init()
glutDisplayFunc(show)
glutMouseFunc(Mouse_click)
glutMotionFunc(Mouse_motion)
glutMainLoop()