introduction
The blogger is a student of grade 2021 Graduate School of computer science at Beijing University of technology. This task is the first major assignment of the course of computer graphics. Because I haven't contacted opengl before, I spent some effort in completing this operation. Because we can't find the code that meets the needs of teachers on the Internet, the code is patched up by ourselves, and there may still be irregularities. Of course, this blog was released after the end of the course.
rely on
- python3.9
- opengl library
It is estimated that Python 3.7 and 3.8 are OK, but I haven't tried. The specific import library code is as follows:
from OpenGL.GL import * from OpenGL.GLUT import * from OpenGL.GLU import * import numpy as np
Draw a square
Here, the picture function provided by opengl is used for drawing.
Firstly, the point coordinates of each face are given:
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]], # front [[0.2, 0.2, -0.2], [0.2, -0.2, -0.2], [-0.2, -0.2, -0.2], [-0.2, 0.2, -0.2]], # after [[0.2, 0.2, 0.2], [0.2, 0.2, -0.2], [-0.2, 0.2, -0.2], [-0.2, 0.2, 0.2]], # Left [[0.2, -0.2, 0.2], [0.2, -0.2, -0.2], [-0.2, -0.2, -0.2], [-0.2, -0.2, 0.2]], # right [[0.2, 0.2, 0.2], [0.2, -0.2, 0.2], [0.2, -0.2, -0.2], [0.2, 0.2, -0.2]], # upper [[-0.2, 0.2, 0.2], [-0.2, -0.2, 0.2], [-0.2, -0.2, -0.2], [-0.2, 0.2, -0.2]] # lower ])
The 1.4 times here is to adjust the size relationship. This coefficient can be adjusted according to the actual situation.
Then give the color of each face:
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] ])
The three numbers here represent RGB red, green and blue respectively, and can also be adjusted by themselves.
Finally, draw the cube in the show function.
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()
Convert screen coordinates to world coordinates
Many mistakes have been made in this step. Most of the online code is based on C + +.
The conversion is mainly completed through the reflection function in opengl: gluUnProject
gluUnProject(MOUSE_X, viewport[3] - MOUSE_Y, z, modelview_mat, projection_mat, viewport)
This function calculates by calling the mouse click position information (MOUSE_X, MOUSE_Y), picture depth information (z) and three conversion matrices (modelview_mat, projection_mat, viewport).
B-spline curve drawing
This step has tried more mistakes.
The code part is very simple. It uses the gluNurbsCurve function to draw B-spline curves. NURBS is the abbreviation of Non-Uniform Rational B-Splines.
gluNurbsCurve(nurb, KNOTS, POINTS, GL_MAP1_VERTEX_3)
POINTS is the control point in B-spline, that is, the point coordinates obtained after clicking the mouse. However, because KNOTS has not been configured correctly before, the function cannot draw a curve.
KNOTS can be generated directly according to the number of control points.
degree = 3 knotNum = len(POINTS) + degree KNOTS = [float(i)/(knotNum-1) for i in range(knotNum)]
Complete code
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]], # front [[0.2, 0.2, -0.2], [0.2, -0.2, -0.2], [-0.2, -0.2, -0.2], [-0.2, 0.2, -0.2]], # after [[0.2, 0.2, 0.2], [0.2, 0.2, -0.2], [-0.2, 0.2, -0.2], [-0.2, 0.2, 0.2]], # Left [[0.2, -0.2, 0.2], [0.2, -0.2, -0.2], [-0.2, -0.2, -0.2], [-0.2, -0.2, 0.2]], # right [[0.2, 0.2, 0.2], [0.2, -0.2, 0.2], [0.2, -0.2, -0.2], [0.2, 0.2, -0.2]], # upper [[-0.2, 0.2, 0.2], [-0.2, -0.2, 0.2], [-0.2, -0.2, -0.2], [-0.2, 0.2, -0.2]] # lower ]) 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 # perspective projection VIEW = np.array([-0.5, 0.5, -0.5, 0.5, 0.5, 20.0]) # Six left/right/bottom/top/near/far faces of the vista 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) # Sets the canvas background color. Note: there must be 4 parameters here glEnable(GL_DEPTH_TEST) # Open the depth test to realize the occlusion relationship glDepthFunc(GL_LEQUAL) # Set the depth test function (GL_LEQUAL is only one of the options) 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) # Set projection (perspective projection) glMatrixMode(GL_PROJECTION) glLoadIdentity() if IS_PERSPECTIVE: glFrustum(VIEW[0], VIEW[1], VIEW[2], VIEW[3], VIEW[4], VIEW[5]) # Set up model view glMatrixMode(GL_MODELVIEW) glLoadIdentity() # Geometric transformation glScale(SCALE_K[0], SCALE_K[1], SCALE_K[2]) # viewpoint 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() #Continuous segment connection 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 outside the box are not drawn 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()