OpenGL yes Open Graphics Library Abbreviation , Meaning for “ Open graphics library ”, It's for rendering 2D、3D Cross language of vector graphics 、 Cross platform application programming interface （API）.OpenGL It's not a stand-alone platform , therefore , It needs a programming language to be used .C / C++ / python / java Can be very good support OpengGL, Of course, I'm used to choosing python Language .
If the reader is python The programmer , And understand numpy, The next reading should be free from any obstacles ; otherwise , I suggest you spend half an hour studying python Language . About numpy, You can refer to another blog post of mine 《 Mathematical modeling three swordsmen MSN》. in fact , I think python Language is close to natural language , As long as the reader is a programmer , Even if not familiar with python, It won't be too much of a problem to read .
in addition , And readers don't have to worry about math . Use OpenGL You don't need to have such a high level of Mathematics , As long as the students in junior high school can tutor their homework , It's enough .
stay OpenGL In the world of , There are all kinds of coordinate systems . As for OpenGL Conceptual understanding , We're going to be exposed to at least six coordinate systems , It's enough to know three of them initially （ The first time I read this passage , You just need to know the world coordinate system ）.
World coordinate system （World Coordinates）
The world coordinate system is the right-handed coordinate system , Take the center of the screen as the origin (0, 0, 0), And it's always the same .
The viewpoint coordinate system （Eye or Camera Coordinates）
The viewpoint coordinates take the viewpoint as the origin , In the direction of the line of sight Z+ The coordinate system in the positive direction of the axis .OpenGL The pipeline will first transform the world coordinates to the viewpoint coordinates , And then cut it , Only in line of sight （ The vistas ） The scene within will enter the next stage of calculation .
Screen coordinate system （Window or Screen Coordinates）
OpenGL One of its important functions is to transform the three-dimensional world coordinates 、 Projection, etc , Finally, calculate its corresponding position on the display device , This location is called the device coordinates . On screen 、 The coordinates on printers and other devices are two-dimensional coordinates . It is worth mentioning that ,OpenGL You can draw with just a part of the device , This part is called the viewing area or the view （viewport）. The projection results in the coordinates in the viewing area ( The projection coordinate ), The calculation process from projection coordinates to equipment coordinates is the transformation of equipment .
Objects in a 3D scene will eventually be displayed on a two-dimensional observation plane like a screen . The transformation from a three-dimensional object to a two-dimensional figure becomes a projection transformation . There are two most commonly used projections ： Parallel projection and perspective projection . As shown in the figure below ,F It's the projection plane ,p1p2 It's a straight line in three-dimensional space ,p’1 and p’2 Namely p1 and p2 stay F The projection on the , The dotted line represents the projection line ,O It's the center of projection .
The parallel projection here , Orthogonal parallel projection —— The projection line is perpendicular to the projection plane . Put a three-dimensional point (x,y,z) Orthogonal parallel projection to xoy In the plane , Then the coordinates of the projection point are (x,y,0). Because the parallel projection discards the depth information , So there's no sense of reality , But it can keep the relative size relationship between objects unchanged .
Perspective projection places the projection plane between the observation point and the projected object , The farther away from the observer , The smaller the projection size , The projection effect is realistic , It is often used in games and simulation .
Whether it's a parallel projection or a perspective projection , Projection imaging is all on the projection plane —— We can think of the projection plane as a display screen . The three-dimensional space described by the world coordinate system is infinite , The projection plane is infinite , but （ What we can see ） The screen area is always limited , So in the projection transformation , Usually only deal with the part of 3D space that can be displayed on the screen . The part of 3D space that can be displayed on the screen by cutting out the infinite three-dimensional space , We call it the visual object . The vistas have six sides , It's left, right, up and down, front and back .
For parallel projections , The viewcube is a rectangular parallelepiped ; For perspective projection , The visual object is a prism . It's not hard to understand that ： Because the farther the object is, the smaller the perspective projection will be in the projection window , This means that it takes more volume to fill the projection window , The visual body naturally becomes a prism .
For parallel projections , A viewport is a rectangle surrounded by four sides of the scene , For perspective projection , The viewpoint is the perspective projection of the front section of the scene on the projection window .
The viewport is OpenGL The more important concept in , At this stage, it can be simply understood as a screen （ Or other output devices ）. in fact , The view and the screen are related but not the same , The screen has a fixed aspect ratio , The size of the view can be defined by the user . Usually , In order to adapt to different aspect ratio of the screen , When setting up the viewport , It will adjust the volume of the scene according to the aspect ratio of the screen （ To increase the width or height of ）.
in real life , What people see in three-dimensional space depends on what angle the observer is looking at . There are three concepts of bread ：
Corresponding to OpenGL in , There's the same concept , The position of the viewpoint 、 Reference point in the direction of aim , as well as （ Up ） Direction .
The following figure shows the flow of 3D graphics . Three dimensional objects in the world coordinate system undergo viewpoint transformation and a series of geometric transformations （ translation 、 rotate 、 The zoom ） after , Coordinate system is transformed into viewpoint coordinate system ; After projection and clipping , Coordinate system is transformed into normalized equipment coordinate system ; Finally, it is displayed on the screen through the transformation of the view , Accordingly , The coordinate system becomes the window coordinate system .
If you take it for granted pip Install as follows , There may be some trouble .
pip install pyopengl
When I install it like this , function OpenGL Code , Got the wrong message ：
NullFunctionError: Attempt to call an undefined function glutInit, check for bool(glutInit) before calling
original ,pip The default installation is 32 A version of the pyopengl, And my operating system is 64 Bit . Suggest clicking here Download the version that suits you , Direct installation .whl file . I installed it like this ：
pip install PyOpenGL-3.1.3b2-cp37-cp37m-win_amd64.whl
My first contact with OpenGL Of GL / GLU / GLUT When , All of a sudden, I was confused by the names of Ku who looked like twin brothers , If it wasn't for the inner strength , Maybe with OpenGL Say goodbye . It took a long time to discover ,OpenGL The naming rules of Library and function are very reasonable , Easy to find 、 memory .
OpenGL The function is named in the following format ：
< Library prefix >< Root command >< The number of optional parameters >< Optional parameter types >
Common library prefixes are gl、glu、glut、aux、wgl、glx、agl etc. . The library prefix indicates that the function belongs to OpenGL Which development library . You can also see from the function name how many parameters are needed and the type of the parameter .I representative int type ,f representative float type ,d representative double type ,u For an unsigned integer . for example glColor3f() Indicates that the function belongs to gl library , Parameters are three floating-point numbers .
OpenGL Library related API There's a core library (gl)、 Utility library (glu)、 Utility library (glut)、 Auxiliary Library (aux)、 Window Library (glx、agl、wgl) And extension function library .gl Is the core ,glu It's right gl Part of the package .glut It's for cross platform OpenGL The toolkit for the program , Than aux Powerful .glx、agl、wgl Is a function for different window systems . Extended function library is used by hardware manufacturers for hardware update OpenGL Functions developed by the extension mechanism of . This paper only makes a brief introduction to the four commonly used libraries .
The core library contains 115 A function , Function names are prefixed with gl. This part of the function is used for the regular 、 Core graphics processing . This function consists of gl.dll To be responsible for the interpretation and implementation of . Because many functions can accept different numbers of the following types . According to the parameters of the type , As a result, the derived function prototype as many as 300 Multiple . The functions in the core library can be divided into the following types of functions ：
The function of drawing basic geometry ：
Matrix operation 、 Functions of geometric transformation and projection transformation ：
Such as matrix stack function glPushMatrix(), Matrix stack function glPopMatrix(), Loading matrix functions glLoadMatrix(), Matrix multiplication function glMultMatrix(), The current matrix function glMatrixMode() And matrix normalization functions glLoadIdentity(), Geometric transformation functions glTranslate*()、glRotate*() and glScale*(), Projection transform function glOrtho()、glFrustum() And the VP transform function glViewport()
Color 、 A function of light and material ：
For example, set the color mode function glColor*()、glIndex*(), Set the function of lighting effect glLight*() 、glLightModel*() And set the material effect function glMaterial()
Show list function ：
It's mainly about creating 、 end 、 Generate 、 The list of functions to delete and delete glNewList()、glEndList()、glGenLists()、glCallList() and glDeleteLists()
Texture mapping function ：
There are mainly one-dimensional texture functions glTexImage1D()、 Two dimensional texture function glTexImage2D()、 Set texture parameters 、 Function of texture environment and texture coordinates glTexParameter*()、glTexEnv*() and glTetCoord*()
Special functions ：
Fusion function glBlendFunc()、 Anti aliasing function glHint() And the atomization effect glFog*()
Rasterize 、 Pixel operation function ：
Such as the position of an image element glRasterPos*()、 Line width glLineWidth()、 Polygon drawing mode glPolygonMode(), Reading pixels glReadPixel()、 Copy pixels glCopyPixel()
Selection and feedback functions ：
There are mainly rendering modes glRenderMode()、 Buffer selection glSelectBuffer() And the feedback buffer glFeedbackBuffer()
Drawing functions of curves and surfaces ：
A function that generates a curve or surface glMap*()、glMapGrid*(), The function of the evaluator glEvalCoord*() glEvalMesh*()
State setting and query function ：
contains 43 A function , Function names are prefixed with glu.OpenGL Provides powerful but few drawing commands , All more complex drawings have to start from the point 、 Line 、 Face to face .Glu In order to reduce the heavy programming work , Encapsulates the OpenGL function ,Glu Function by calling the core library function , Provides relatively simple usage for developers , Implement some more complex operations . This function consists of glu.dll To be responsible for the interpretation and implementation of .OpenGL The core library and utility library in can be found in all OpenGL Run on the platform . It mainly includes the following ：
Auxiliary texture mapping function ：
Coordinate transformation and projection transformation functions ：
Define projection mode function gluPerspective()、gluOrtho2D() 、gluLookAt(), Pick up projection scene volume function gluPickMatrix(), Projection matrix calculation gluProject() and gluUnProject()
Polygon tessellation tool ：
Quadric surface drawing tool ：
It's mainly about drawing spheres 、 cone 、 cylinder 、 Torus gluNewQuadric()、gluSphere()、gluCylinder()、gluDisk()、gluPartialDisk()、gluDeleteQuadric()
Non uniform rational B Spline drawing tools ：
It is mainly used to define and draw Nurbs Curves and surfaces , Include gluNewNurbsRenderer()、gluNurbsCurve()、gluBeginSurface()、gluEndSurface()、gluBeginCurve()、gluNurbsProperty()
Error feedback tools ：
Get error information string gluErrorString()
Contains about 30 Multiple functions , The function name is prefixed with glut.glut It doesn't depend on the window platform OpenGL tool kit , from Mark KLilgrad stay SGI To write （ Now in Nvidia）, The goal is to hide different window platforms API Complexity . Function to glut start , They act as aux A more powerful alternative to the library , Provides more complex rendering capabilities , This function consists of glut.dll To be responsible for the interpretation and implementation of . because glut The window management function in is independent of the running environment , therefore OpenGL Tool libraries in can be found in X-Window, Windows NT, OS/2 Wait for the system to run , It is especially suitable for the development of the complex interface OpenGL The sample program . For experienced programmers , Usually use first glut Straighten out 3D Graphic code , And then it's integrated into a complete application . This part of the function mainly includes ：
Window operation function ：
Window initialization 、 Window size 、 Window position function, etc glutInit()、glutInitDisplayMode()、glutInitWindowSize()、glutInitWindowPosition()
Callback function ：
Response refresh message 、 Keyboard messages 、 Mouse message 、 Timer Functions GlutDisplayFunc()、glutPostRedisplay()、glutReshapeFunc()、glutTimerFunc()、glutKeyboardFunc()、glutMouseFunc()
Create complex 3D objects ：
These and aux The functions of the library are the same
Menu functions ：
Create a function to add a menu GlutCreateMenu()、glutSetMenu()、glutAddMenuEntry()、glutAddSubMenu() and glutAttachMenu()
Program running function ：
in the light of windows Platform expansion , contains 16 A function , The function name is prefixed with wgl. This part of the function is mainly used to connect OpenGL and Windows , To make up for OpenGL The lack of text . Windows A dedicated library can only be used for Windows Environment . This kind of function mainly includes the following types ：
Drawing context sensitive functions ：
Text and text processing functions ：
overburden 、 Stratum and main plane layer processing function ：
Other functions ：
There are dozens of color setting functions , It's all about glColor start , Followed by the number of parameters and parameter type . Parameters can be 0 To 255 Unsigned integer between , It can also be 0 To 1 The floating point number between . Three parameters are respectively expressed RGB component , The fourth parameter is transparency （ In fact, opacity is more appropriate ）. The following two most commonly used methods of setting color ：
glColor3f(1.0,0.0,0.0) # Set the current color to red glColor4f(0.0,1.0,1.0,1.0) # Set the current color to cyan , The opacity glColor3ub(0, 0, 255) # Set the current color to blue
glColor It also supports passing three or four parameters in vector form , for example ：
glColor3fv([0.0,1.0,0.0]) # Set the current color to green
hot tip ：OpenGL Is using state machine mode , Color is a state variable , To set the color is to change the state variable and keep it in effect , Until you call the color setting function again . Except for color ,OpenGL There are many more state variables or patterns . At any time , You can query the current value of each state variable , You can also use glPushAttrib() or glPushClientAttrib() Save the set of state variables , When necessary , Reuse glPopAttrib() or glPopClientAttrib() Restore state variables .
The vertices （vertex） yes OpengGL A very important concept in , Describe line segments 、 Polygons are inseparable from vertices . Similar to setting colors , There are also dozens of functions for setting vertices , It's all about glVertex start , Followed by the number of parameters and parameter type , It also supports the transfer of multiple vectors . Two parameters , respectively xy coordinate , The three parameters represent xyz coordinate . If there is a fourth parameter , Then it represents the homogeneous coordinates of the point w; otherwise , Default w=1. As for what are homogeneous coordinates , Obviously beyond the scope of junior high school mathematics , I won't discuss it here .
glVertex2f(1.0,0.5) # xoy Points on the plane ,z=0 glVertex3f(0.5,1.0,0.0) # Three dimensional points in space
Just set colors and vertices , It doesn't draw anything . We can change the color at any time , But all the vertex settings , Must be included in glBegin() and glEnd() Between , and glBegin() The parameter specifies what the vertices should be drawn as . Here are glBegin() Possible parameter options ：
|GL_POINTS||Draw one or more vertices|
|GL_LINES||Draw line segments|
|GL_LINE_STRIP||Draw continuous line segments|
|GL_LINE_LOOP||Draw closed line segments|
|GL_TRIANGLES||Draw one or more triangles|
|GL_TRIANGLE_STRIP||Draw a continuous triangle|
|GL_TRIANGLE_FAN||Draw a fan of triangles|
|GL_QUADS||Draw one or more quadrangles|
|GL_QUAD_STRIP||Draw continuous quadrilateral|
Usually , We use the tool library （GLUT） establish OpenGL Applications . Why don't GL perhaps GLU The library? ？ There must be a canvas before painting , You can't just pick up the brush and start painting . remember , The tool library mainly provides window related functions , With the window , It's like having a canvas , And the core library and the utility library , It's like all kinds of brushes 、 Pigment . Using the tool library （GLUT） establish OpenGL The application only takes four steps （ Of course , The premise is that you need to have the drawing function ready , And give it a proper name ）：
OK, After so much bedding , We finally started the first OpenGL The app ： Drawing the world coordinate system in three dimensions , After the origin of the coordinates （z The negative half of the axis ） Draw a triangle . The code is as follows ：
# -*- coding: utf-8 -*- # ------------------------------------------- # quidam_01.py The world coordinate system and triangle in three dimensional space # ------------------------------------------- from OpenGL.GL import * from OpenGL.GLUT import * def draw(): # --------------------------------------------------------------- glBegin(GL_LINES) # Start drawing line segments （ World coordinate system ） # Draw in red x Axis glColor4f(1.0, 0.0, 0.0, 1.0) # Set the current color to red opaque glVertex3f(-0.8, 0.0, 0.0) # Set up x Axis vertex （x Axis negative direction ） glVertex3f(0.8, 0.0, 0.0) # Set up x Axis vertex （x Affirmative direction ） # Draw in green y Axis glColor4f(0.0, 1.0, 0.0, 1.0) # Set the current color to green opaque glVertex3f(0.0, -0.8, 0.0) # Set up y Axis vertex （y Axis negative direction ） glVertex3f(0.0, 0.8, 0.0) # Set up y Axis vertex （y Affirmative direction ） # Draw in blue z Axis glColor4f(0.0, 0.0, 1.0, 1.0) # Set the current color to blue opaque glVertex3f(0.0, 0.0, -0.8) # Set up z Axis vertex （z Axis negative direction ） glVertex3f(0.0, 0.0, 0.8) # Set up z Axis vertex （z Affirmative direction ） glEnd() # End drawing line segment # --------------------------------------------------------------- glBegin(GL_TRIANGLES) # Start drawing triangles （z The negative half of the axis ） glColor4f(1.0, 0.0, 0.0, 1.0) # Set the current color to red opaque glVertex3f(-0.5, -0.366, -0.5) # Set triangle vertex glColor4f(0.0, 1.0, 0.0, 1.0) # Set the current color to green opaque glVertex3f(0.5, -0.366, -0.5) # Set triangle vertex glColor4f(0.0, 0.0, 1.0, 1.0) # Set the current color to blue opaque glVertex3f(0.0, 0.5, -0.5) # Set triangle vertex glEnd() # Finish drawing the triangle # --------------------------------------------------------------- glFlush() # Empty buffer , Send instructions to hardware for immediate execution if __name__ == "__main__": glutInit() # 1. initialization glut library glutCreateWindow('Quidam Of OpenGL') # 2. establish glut window glutDisplayFunc(draw) # 3. Register callback function draw() glutMainLoop() # 4. Get into glut Main circulation
Run code , I show the results here, as shown in the figure on the left below . If you try to run this code in error , I guess so pyopengl There's an installation problem , It is suggested to go back to the front and reread pyopengl Installation .
After a brief excitement , You may try to draw some other lines , Change color or transparency , Even drawing polygons . Soon you will find out , Our first program had a lot of problems , such as ：
No problem , Except for 1 I don't know how to solve this problem （ I don't know ）, The other problems are nothing . Compared to our code , A really practical one OpenGL Program , There is still a lot of work to do ：
initialization glut When I was in the library , We usually use glutInitDisplayMode() To set the initial display mode , Its parameters can be a combination of the parameters in the following table .
|GLUT_RGB||Appoint RGB Color mode window|
|GLUT_RGBA||Appoint RGBA Color mode window|
|GLUT_INDEX||The window that specifies the color index mode|
|GLUT_SINGLE||Specify a single cache window|
|GLUT_DOUBLE||Specify double cache window|
|GLUT_ACCUM||Windows use accumulated caching|
|GLUT_ALPHA||The color component of the window contains alpha value|
|GLUT_DEPTH||Windows use deep caching|
|GLUT_STENCIL||Windows use template caching|
|GLUT_MULTISAMPLE||Specify the window that supports the multi sample function|
|GLUT_STEREO||Specify stereo window|
|GLUT_LUMINANCE||Windows use the brightness color model|
Using double cache windows , It can avoid the feeling of shaking when redrawing . I usually choose GLUT_DOUBLE | GLUT_ALPHA | GLUT_DEPTH Set the initial display mode as a parameter .
Before you start drawing , You need to do some initialization of the canvas , These jobs only need to be done once . such as ：
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 ）
If necessary, , You can also turn on distortion correction （ Anti aliasing ）、 Open surface culling, etc .
Before each redraw , You need to clear the screen and depth cache first . This operation is usually placed at the beginning of the drawing function .
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
Projection setup is also one of the steps required for each redraw .glOrtho() Used to set the parallel projection ,glFrustum() Used to set the perspective projection . These two functions have the same parameters , It's all visual left / right / bottom / top / near / far Six sides .
Visual style left / right / bottom / top A rectangle surrounded by four faces , It's the view .near It's the projection plane , Its value is the distance between the projection plane and the viewpoint ,far It's the back section of the scene , Its value is the distance from the back section to the viewpoint .far and near The difference between the , It's the depth of the scene . The relative position relationship between viewpoint and scene body is fixed , When the viewpoint moves , The scene moves with it .
I personally think , The visual aspect is OpengGL above all 、 The core concept , It's with the viewport 、 viewpoint 、 The projection plane 、 The zoom 、 Roaming and other concepts are closely related . Only when we have a correct understanding of the visual body , In order to correctly set its six parameters , In order to show the desired effect .
In order to change the window aspect ratio , The drawn objects still maintain a fixed aspect ratio , In general, when doing projection transformation , It is necessary to adjust the view volume according to the aspect ratio of the window left / right perhaps bottom / top Parameters .
hypothesis view It's visual ,width and height It's the width and height of the window , Before the projection transformation , It needs to be declared that it is an operation on the projection matrix , And unifying the projection matrix ：
glMatrixMode(GL_PROJECTION) glLoadIdentity() if width > height: k = width / height glFrustum(view *k, view *k, view , view , view , view ) else: k = height / width glFrustum(view , view , view *k, view *k, view , view )
Viewpoint is a concept associated with the visual object . Setting the viewpoint needs to consider where the eyes are 、 Look where 、 Where is the top of your head , Corresponding to eye, lookat and eye_up Three vectors .
gluLookAt( eye, eye, eye, look_at, look_at, look_at, eye_up, eye_up, eye_up )
The concept of a viewport is also associated with a scene volume , It's relatively simple .
glViewport(0, 0, width, height)
Model translation 、 rotate 、 Geometric transformations such as scaling , You need to switch to the model matrix ：
glMatrixMode(GL_MODELVIEW) glLoadIdentity() glScale(1.0, 1.0, 1.0)
GLUT The library provides several functions to help us capture mouse events 、 Keyboard events and window events ：
This function captures mouse clicks and wheel operations , return 4 Parameters to the bound event function ： key （ left-click / Right click / In the key / On the roller / Under the wheel ）、 state （1/0）、x coordinate 、y coordinate
This function captures the mouse movement when a mouse button is pressed to the bound event function , return 2 Parameters ：x coordinate 、y coordinate
This function captures mouse movement , return 2 Parameters to the bound event function ：x coordinate 、y coordinate
This function captures the mouse leaving or entering the window area , return 1 Parameters to the bound event function ：GLUT_LEFT perhaps GLUT_ENTERED
This function captures the keyboard key being pressed , return 3 Parameters to the bound event function ： The key pressed ,x coordinate 、y coordinate
The capture window is resized , return 2 Parameters to the bound event function ： Window width 、 Window height
If we need to capture these events , Just define the event function , Just register the corresponding function ：
def reshape(width, height): pass def mouseclick(button, state, x, y): pass def mousemotion(x, y): pass def keydown(key, x, y): pass glutReshapeFunc(reshape) # Register functions that respond to window changes reshape() glutMouseFunc(mouseclick) # Register functions that respond to mouse clicks mouseclick() glutMotionFunc(mousemotion) # Register functions that respond to mouse dragging mousemotion() glutKeyboardFunc(keydown) # Register keyboard input functions keydown()
It's time to give a complete demonstration of the above mentioned things . The following code still draws the world coordinate system , And draw a triangle before and after the origin . The mouse can drag the viewpoint to rotate around the reference point （ The distance between them remains the same ）, The scroll wheel can scale the model . Hit backspace or enter to move the viewpoint away from or near the reference point . Knock x/y/z It can reduce the coordinate value corresponding to the reference point , Knock X/Y/Z The coordinate value corresponding to the reference point can be increased . Click the space bar to switch the projection mode .
On the left of the above image is the display effect of parallel projection mode , On the right of the above picture is the display effect of perspective projection mode . The code is as follows ：
# -*- coding: utf-8 -*- # ------------------------------------------- # quidam_02.py rotate 、 The zoom 、 Change viewpoint and reference point # ------------------------------------------- from OpenGL.GL import * from OpenGL.GLU import * from OpenGL.GLUT import * import numpy as np IS_PERSPECTIVE = True # Perspective projection VIEW = np.array([-0.8, 0.8, -0.8, 0.8, 1.0, 20.0]) # Visual style left/right/bottom/top/near/far Six sides SCALE_K = np.array([1.0, 1.0, 1.0]) # Model scaling EYE = np.array([0.0, 0.0, 2.0]) # Eye position （ Default z The positive direction of the axis ） LOOK_AT = np.array([0.0, 0.0, 0.0]) # Reference point in the direction of aim （ The default is at the coordinate origin ） EYE_UP = np.array([0.0, 1.0, 0.0]) # Define the top for the observer （ Default y The positive direction of the axis ） WIN_W, WIN_H = 640, 480 # Save variables for window width and height LEFT_IS_DOWNED = False # Left mouse button pressed MOUSE_X, MOUSE_Y = 0, 0 # The starting position to be saved when examining the mouse displacement def getposture(): global EYE, LOOK_AT dist = np.sqrt(np.power((EYE-LOOK_AT), 2).sum()) if dist > 0: phi = np.arcsin((EYE-LOOK_AT)/dist) theta = np.arcsin((EYE-LOOK_AT)/(dist*np.cos(phi))) else: phi = 0.0 theta = 0.0 return dist, phi, theta DIST, PHI, THETA = getposture() # The distance between the eye and the object of observation 、 Elevation 、 azimuth 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 ） def draw(): global IS_PERSPECTIVE, VIEW global EYE, LOOK_AT, EYE_UP global SCALE_K global WIN_W, WIN_H # Clear screen and deep cache glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) # Set up the projection （ Perspective projection ） glMatrixMode(GL_PROJECTION) glLoadIdentity() if WIN_W > WIN_H: if IS_PERSPECTIVE: glFrustum(VIEW*WIN_W/WIN_H, VIEW*WIN_W/WIN_H, VIEW, VIEW, VIEW, VIEW) else: glOrtho(VIEW*WIN_W/WIN_H, VIEW*WIN_W/WIN_H, VIEW, VIEW, VIEW, VIEW) else: if IS_PERSPECTIVE: glFrustum(VIEW, VIEW, VIEW*WIN_H/WIN_W, VIEW*WIN_H/WIN_W, VIEW, VIEW) else: glOrtho(VIEW, VIEW, VIEW*WIN_H/WIN_W, VIEW*WIN_H/WIN_W, VIEW, VIEW) # Set model view glMatrixMode(GL_MODELVIEW) glLoadIdentity() # Geometric transformation glScale(SCALE_K, SCALE_K, SCALE_K) # Set viewpoint gluLookAt( EYE, EYE, EYE, LOOK_AT, LOOK_AT, LOOK_AT, EYE_UP, EYE_UP, EYE_UP ) # Set up the viewport glViewport(0, 0, WIN_W, WIN_H) # --------------------------------------------------------------- glBegin(GL_LINES) # Start drawing line segments （ World coordinate system ） # Draw in red x Axis glColor4f(1.0, 0.0, 0.0, 1.0) # Set the current color to red opaque glVertex3f(-0.8, 0.0, 0.0) # Set up x Axis vertex （x Axis negative direction ） glVertex3f(0.8, 0.0, 0.0) # Set up x Axis vertex （x Affirmative direction ） # Draw in green y Axis glColor4f(0.0, 1.0, 0.0, 1.0) # Set the current color to green opaque glVertex3f(0.0, -0.8, 0.0) # Set up y Axis vertex （y Axis negative direction ） glVertex3f(0.0, 0.8, 0.0) # Set up y Axis vertex （y Affirmative direction ） # Draw in blue z Axis glColor4f(0.0, 0.0, 1.0, 1.0) # Set the current color to blue opaque glVertex3f(0.0, 0.0, -0.8) # Set up z Axis vertex （z Axis negative direction ） glVertex3f(0.0, 0.0, 0.8) # Set up z Axis vertex （z Affirmative direction ） glEnd() # End drawing line segment # --------------------------------------------------------------- glBegin(GL_TRIANGLES) # Start drawing triangles （z The negative half of the axis ） glColor4f(1.0, 0.0, 0.0, 1.0) # Set the current color to red opaque glVertex3f(-0.5, -0.366, -0.5) # Set triangle vertex glColor4f(0.0, 1.0, 0.0, 1.0) # Set the current color to green opaque glVertex3f(0.5, -0.366, -0.5) # Set triangle vertex glColor4f(0.0, 0.0, 1.0, 1.0) # Set the current color to blue opaque glVertex3f(0.0, 0.5, -0.5) # Set triangle vertex glEnd() # Finish drawing the triangle # --------------------------------------------------------------- glBegin(GL_TRIANGLES) # Start drawing triangles （z The positive half of the axis ） glColor4f(1.0, 0.0, 0.0, 1.0) # Set the current color to red opaque glVertex3f(-0.5, 0.5, 0.5) # Set triangle vertex glColor4f(0.0, 1.0, 0.0, 1.0) # Set the current color to green opaque glVertex3f(0.5, 0.5, 0.5) # Set triangle vertex glColor4f(0.0, 0.0, 1.0, 1.0) # Set the current color to blue opaque glVertex3f(0.0, -0.366, 0.5) # Set triangle vertex glEnd() # Finish drawing the triangle # --------------------------------------------------------------- glutSwapBuffers() # Switch buffer , To show the drawing content def reshape(width, height): global WIN_W, WIN_H WIN_W, WIN_H = width, height glutPostRedisplay() def mouseclick(button, state, x, y): global SCALE_K global LEFT_IS_DOWNED global MOUSE_X, MOUSE_Y MOUSE_X, MOUSE_Y = x, y if button == GLUT_LEFT_BUTTON: LEFT_IS_DOWNED = state==GLUT_DOWN elif button == 3: SCALE_K *= 1.05 glutPostRedisplay() elif button == 4: SCALE_K *= 0.95 glutPostRedisplay() def mousemotion(x, y): global LEFT_IS_DOWNED global EYE, EYE_UP global MOUSE_X, MOUSE_Y global DIST, PHI, THETA global WIN_W, WIN_H if LEFT_IS_DOWNED: dx = MOUSE_X - x dy = y - MOUSE_Y MOUSE_X, MOUSE_Y = x, y PHI += 2*np.pi*dy/WIN_H PHI %= 2*np.pi THETA += 2*np.pi*dx/WIN_W THETA %= 2*np.pi r = DIST*np.cos(PHI) EYE = DIST*np.sin(PHI) EYE = r*np.sin(THETA) EYE = r*np.cos(THETA) if 0.5*np.pi < PHI < 1.5*np.pi: EYE_UP = -1.0 else: EYE_UP = 1.0 glutPostRedisplay() def keydown(key, x, y): global DIST, PHI, THETA global EYE, LOOK_AT, EYE_UP global IS_PERSPECTIVE, VIEW if key in [b'x', b'X', b'y', b'Y', b'z', b'Z']: if key == b'x': # Aim at the reference point x Reduce LOOK_AT -= 0.01 elif key == b'X': # Aim for reference x increase LOOK_AT += 0.01 elif key == b'y': # Aim at the reference point y Reduce LOOK_AT -= 0.01 elif key == b'Y': # Aim at the reference point y increase LOOK_AT += 0.01 elif key == b'z': # Aim at the reference point z Reduce LOOK_AT -= 0.01 elif key == b'Z': # Aim at the reference point z increase LOOK_AT += 0.01 DIST, PHI, THETA = getposture() glutPostRedisplay() elif key == b'\r': # Enter key , Point forward EYE = LOOK_AT + (EYE - LOOK_AT) * 0.9 DIST, PHI, THETA = getposture() glutPostRedisplay() elif key == b'\x08': # backspace , Point of view back EYE = LOOK_AT + (EYE - LOOK_AT) * 1.1 DIST, PHI, THETA = getposture() glutPostRedisplay() elif key == b' ': # Space bar , Switch projection mode IS_PERSPECTIVE = not IS_PERSPECTIVE glutPostRedisplay() if __name__ == "__main__": glutInit() displayMode = GLUT_DOUBLE | GLUT_ALPHA | GLUT_DEPTH glutInitDisplayMode(displayMode) glutInitWindowSize(WIN_W, WIN_H) glutInitWindowPosition(300, 200) glutCreateWindow('Quidam Of OpenGL') init() # Initialize canvas glutDisplayFunc(draw) # Register callback function draw() glutReshapeFunc(reshape) # Register functions that respond to window changes reshape() glutMouseFunc(mouseclick) # Register functions that respond to mouse clicks mouseclick() glutMotionFunc(mousemotion) # Register functions that respond to mouse dragging mousemotion() glutKeyboardFunc(keydown) # Register keyboard input functions keydown() glutMainLoop() # Get into glut Main circulation
Although there are still many areas that we need to continue to explore , Like lights 、 texture of material 、 atomization 、 Pick up, etc , But that's not the goal of the fantasy tour . The fantasy tour is just to help readers build OpenGL Basic concepts of . thus , We have basically finished our task .
The practical application OpenGL When drawing three-dimensional images , Tens of thousands of vertices need to be processed , Sometimes even a million 、 Tens of millions . We don't usually pass this data in a drawing function , But before drawing , Send the data ahead of time to GPU. Every time the drawing function is drawn , Just from GPU Take the data out of the cache , Greatly improved efficiency . This mechanism realizes , Vertex buffer object dependent （Vertex Buffer Object）, abbreviation VBO.
Even though VBO It's an extension of the graphics card , Actually, it doesn't use GPU operation , in other words VBO No coloring language , Direct use opengl Function can call , The main purpose is to speed up rendering .
VBO Put the vertex information into GPU in ,GPU In the rendering time to cache data , The bridge between the two is GL-Context.GL-Context The whole program usually has only one , So if there are two different rendering codes in a rendering process ,GL-context It's responsible for switching between them . That's why in the rendering process , In every drawing code there will be glBindbuffer、glEnableVertexAttribArray、glVertexAttribPointer. If you put it all in initialization time , Use a structure to record all that is needed for this drawing VBO Information needed , Save it to VBO Specific location , When drawing, you can get information directly from this position to draw , Will simplify the rendering process 、 Improve rendering speed . This is it. VAO The original intention of the concept .
VAO The full name is Vertex Array Object, First , It is not Buffer-Object, So it doesn't have to be used to store data ; secondly , It's for “ The vertices ” for , That is to say, it follows “ Vertex drawing ” Is closely linked .VAO It records the information needed in a drawing , This includes “ Where is the data glBindBuffer”、“ What is the format of the data glVertexAttribPointer”、shader-attribute Of location To enable glEnableVertexAttribArray.
According to my information , Almost all graphics cards support VBO, But not all graphics cards support VAO, and VAO It's just optimized VBO How to use , There's no real impact on acceleration , Therefore, this paper only discusses VBO technology .
Suppose you draw a hexahedron , The top is like this ：
# Hexahedral data # ------------------------------------------------------ # v4----- v5 # /| /| # v0------v1| # | | | | # | v7----|-v6 # |/ |/ # v3------v2 # Vertex set vertices = np.array([ -0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, -0.5, 0.5, -0.5, -0.5, 0.5, # v0-v1-v2-v3 -0.5, 0.5, -0.5, 0.5, 0.5, -0.5, 0.5, -0.5, -0.5, -0.5, -0.5, -0.5 # v4-v5-v6-v7 ], dtype=np.float32) # Index set indices = np.array([ 0, 1, 2, 3, # v0-v1-v2-v3 (front) 4, 5, 1, 0, # v4-v5-v1-v0 (top) 3, 2, 6, 7, # v3-v2-v6-v7 (bottom) 5, 4, 7, 6, # v5-v4-v7-v6 (back) 1, 5, 6, 2, # v1-v5-v6-v2 (right) 4, 0, 3, 7 # v4-v0-v3-v7 (left) ], dtype=np.int)
stay GPU To create a VBO as follows ：
from OpenGL.arrays import vbo vbo_vertices = vbo.VBO(vertices) vbo_indices = vbo.VBO(indices, target=GL_ELEMENT_ARRAY_BUFFER)
establish The vertices VBO when , Default target=GL_ARRAY_BUFFER, And creating an index VBO when ,target=GL_ELEMENT_ARRAY_BUFFER, Because the data type of the vertex is np.float32, The data type of the index is np.int.
stay VBO Saved vertex dataset , Besides vertex information , You can also include colors 、 normal 、 Texture data , This is the concept of vertex blending arrays . Suppose we add the color of each vertex to the set of vertices above , It can be written like this ：
vertices = np.array([ 0.3, 0.6, 0.9, -0.35, 0.35, 0.35, # c0-v0 0.6, 0.9, 0.3, 0.35, 0.35, 0.35, # c1-v1 0.9, 0.3, 0.6, 0.35, -0.35, 0.35, # c2-v2 0.3, 0.9, 0.6, -0.35, -0.35, 0.35, # c3-v3 0.6, 0.3, 0.9, -0.35, 0.35, -0.35, # c4-v4 0.9, 0.6, 0.3, 0.35, 0.35, -0.35, # c5-v5 0.3, 0.9, 0.9, 0.35, -0.35, -0.35, # c6-v6 0.9, 0.9, 0.3, -0.35, -0.35, -0.35 # c7-v7 ], dtype=np.float32)
Use glInterleavedArrays() Function to separate vertices from a vertex blend array 、 Color 、 Normals and textures . such as , For vertex blending arrays containing only vertex information ：
vbo_indices.bind() glInterleavedArrays(GL_V3F, 0, None)
If the vertex blending array contains color and vertex information ：
vbo_indices.bind() glInterleavedArrays(GL_C3F_V3F, 0, None)
glInterleavedArrays() The first parameter of the function has a total of 14 An option , Namely ：
Use glDrawElements() Before drawing the function , You need to bind the vertex dataset and index dataset first , And then use glInterleavedArrays() Sort out the top point 、 Color 、 Normal and other data .
vbo_indices.bind() glInterleavedArrays(GL_V3F, 0, None) vbo_indices.bind() glDrawElements(GL_QUADS, int(vbo_indices .size/4), GL_UNSIGNED_INT, None) vbo_indices.unbind() vbo_indices.unbind()
In the process of writing , I refer to a lot of materials , Including paper books and web pages , The column is written here , Thank you ！
The writing process lasted two or three weeks , During this period, it can be described as painstaking efforts . At the time of publication , feel a sense of relief , Fill in the words to remember it .
Sad clouds, light wind , It's evening and evening . Don't come. It should be , Weight loss , Bone stud . I think about it every day OpenGL, The mood is so boring . A blog post , A computer , A mouse . —— Send in 《 Mei mouth 》
Recently, a lot of friends have consulted through private letters about Python Learning problems . To facilitate communication , I am here CSDN Of app Created on “Python Homework guidance ” stronghold , oriented Python beginner , Provide consulting services for you 、 Coach Python Homework . Welcome interested students to use wechat code scanning to join .
From blogs to official account , Every article 、 Every question 、 Every sentence 、 Every line of code , All insist on originality , Never copy , This is my principle . If you like , Please pay attention to my WeChat official account “Python Homework counselor ”.