current position:Home>Python + OpenGL realizes real-time interactive writing on blocks with B-spline curves

Python + OpenGL realizes real-time interactive writing on blocks with B-spline curves

2022-01-31 18:13:41 Thousand thoughts

 Final effect (P.S. And CSDN Thousands of people want to be the same person )

introduction

The blogger is the computer of Beijing University of technology 2021 Grade one students , This task is the first big assignment in the course of computer graphics . Because I haven't touched it before opengl, So it took some effort to finish this homework . Because I can't find the code that meets the needs of teachers on the Internet , So the code is put together by ourselves , There may still be irregularities . Of course , This blog was posted after the course was over .

rely on

  • python3.9
  • opengl library

It is estimated that python3.7、3.8 Either way , 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
 Copy code 

Draw squares

What we use here is opengl Drawing with its own picture function . First, give the point coordinates of each face :

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]],  #  On 
    [[-0.2, 0.2, 0.2], [-0.2, -0.2, 0.2], [-0.2, -0.2, -0.2], [-0.2, 0.2, -0.2]]  #  Next 
])
 Copy code 

there 1.4 Times 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]
])
 Copy code 

The three numbers here represent RGB Red, green and blue , It can also be adjusted by itself . Finally, show Draw this cube in the 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()
 Copy code 

Convert screen coordinates to world coordinates

Many mistakes have been made in this step , Most of the code on the Internet is based on C++ Of . Complete the conversion mainly through opengl Reflection function in :gluUnProject.

gluUnProject(MOUSE_X, viewport[3] - MOUSE_Y, z, modelview_mat, projection_mat, viewport)
 Copy code 

This function calls the position information of the mouse click (MOUSE_X,MOUSE_Y)、 Picture depth information (z)、 Three transformation matrices (modelview_mat, projection_mat, viewport) Calculate .

B Spline drawing

This step has tried more mistakes . The code part is simple , Is the use gluNurbsCurve Function to do B Drawing of spline curve . among ,NURBS It is non-uniform and reasonable B Spline (Non-Uniform Rational B-Splines) Abbreviation .

gluNurbsCurve(nurb, KNOTS, POINTS, GL_MAP1_VERTEX_3)
 Copy code 

POINTS Namely B Control points in splines , That is, the point coordinates obtained after clicking the mouse . But because before KNOTS It has not been configured correctly , So that the function cannot draw a curve . KNOTS It 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)]
 Copy code 

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]],  #  On 
    [[-0.2, 0.2, 0.2], [-0.2, -0.2, 0.2], [-0.2, -0.2, -0.2], [-0.2, 0.2, -0.2]]  #  Next 
])

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])  #  Visual style left/right/bottom/top/near/far Six sides 
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)  #  Set canvas background color . Be careful : This must be 4 Parameters 
    glEnable(GL_DEPTH_TEST)  #  Open depth test , Realize occlusion relationship 
    glDepthFunc(GL_LEQUAL) #  Set depth test function (GL_LEQUAL It's just 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 up the projection ( Perspective projection )
    glMatrixMode(GL_PROJECTION)
    glLoadIdentity()

    if IS_PERSPECTIVE:
        glFrustum(VIEW[0], VIEW[1], VIEW[2], VIEW[3], VIEW[4], VIEW[5])

    #  Set 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 line segments are connected 
    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: # The dots outside the square don't trace 
            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()
 Copy code 

copyright notice
author[Thousand thoughts],Please bring the original link to reprint, thank you.
https://en.pythonmana.com/2022/01/202201311813388201.html

Random recommended