Learn OpenGL
acs opened this issue · 28 comments
It is time to start learning OpenGL. Needed for #122 and in general, to understand the details of computer 3D graphics.
The two main references are:
- The Red Book: http://www.opengl-redbook.com/
- NeHe tutorials: http://nehe.gamedev.net/ and https://github.com/gamedev-net/nehe-opengl OpenGL 1.1
- http://www.opengl-tutorial.org (3.3 and later)
- http://ogldev.atspace.co.uk/index.html
My idea is to start using the NeHe Python tutorials. Let's see.
“Modern” OpenGL (version 3.x and higher, latest is 4.5) which uses lower level API’s to give you more flexibility. But we are using the older versions during the learning process. From this cool intro: http://15462.courses.cs.cmu.edu/spring2018content/lectures/00_opengl/00_opengl_slides.pdf
Great book with lots of references to free resources: http://www.realtimerendering.com/
https://learnopengl.com/book/book_pdf.pdf
«OpenGL is an API for accessing a hardware-based rasterizer. As such, it conforms to the model for rasterization-based 3D renderers. A rasterizer receives a sequence of triangles from the user, performs operations on them, and writes pixels based on this triangle data. This is a simplification of how rasterization works in OpenGL, but it is useful for our purposes.»
https://paroj.github.io/gltut/Basics/Intro%20Graphics%20and%20Rendering.html
GLUT is the windowing library used instead of pygame of pyglet. It seems to be more low level. Let's see. Because it is unmaintained for more then 20 years: https://www.opengl.org/resources/libraries/glut/ But probably FreeGLUT is what we are using inside PyOpenGL: http://freeglut.sourceforge.net/ (An alternative: https://www.glfw.org/ with python support https://github.com/pyglfw/pyglfw)
The idea is to use https://wiki.python.org/moin/PyOpenGL and in the Python examples is used: https://github.com/gamedev-net/nehe-opengl/blob/master/python/lesson01/ztvE1/lesson1.py
The contents from this lesso1 is in NeHe tutorials at: http://nehe.gamedev.net/tutorial/creating_an_opengl_window_(win32)/13001/
Let's check if it useful this combination to learn OpenGL. The first thing is to try to run the code. And as expected, there are issue, Some functions don't exist anymore:
glutSetIdleFuncCallback
glutSetReshapeFuncCallback
glutSetKeyboardFuncCallback
And of course, it is python2 code which needs some love to work on python3. Once all is fixed following lesson02 a nice blank sreen is show:
It seems that lesson2 has updated code: https://github.com/gamedev-net/nehe-opengl/blob/master/python/lesson02/ztvDB/Lesson%2002/lesson02.py
This one works like a charm (just fix the print so it works in python3):
Let's comparte and try to fix also lesson1.
http://www.wag.caltech.edu/home/rpm/python_course/Lecture_7.pdf A nice intro to OpenGL from 2000 by Richard P. Muller
This code is pretty similar to the one the the tutorials, so it is great to confirm things.
Lesson 1: "This program will create a blank OpenGL window"
Original my own fixed working with PyOpenGL and Python3.
The contents of the tutorial (1-5): lesson1
Is it possible that a tutorial for C++ in Windows from 2000 is useful for learning OpenGL in Python in 2020? Let's see.
- OpenGL Rendering Context: every OpenGL program has one
- Device Context
- Graphis Device Interface: Window
The Rendering Context is linked to the Device Context which is linked to the GDI to show OpenGL results in a window.
The GDI I remember is something specific for Windows.
The creation and destroy oof the window in our case is done by GLUT. So we can forget about this part of the tutorial. But it is important to know that there is a OpenGL Rendering Context. In our case is the window created and destoryed by GLUT:
window = glutCreateWindow("Jeff Molofee's GL Code Tutorial ... NeHe '99")
...
glutDestroyWindow(window)
There are a lot of things at the start of lesson1 that are handled by the GLUT library in our case. But the concepts are the same, so it is not a waste of time reading them. And once we are totally OpenGL, the window is juts where our work will be shown, the code will be the same in the tutorial and in Python.
We have some important matrices:
- Projection Matrix: responsible for adding perspective to our scene
- Model Matrix: where our object information is stored
and critical concepts:
- The depth buffer keeps track of how deep objects are into the screen and it is a very important part of OpenGL
This is not in Python: glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST)
which tell OpenGL we want the best perspective correction to be done.
- InitGL() where we can set up lighting, textures, and anything else that needs to be setup
- By using double buffering we get smooth flicker free animation
Ok, after reading this first lesson, yes, we can learn OpenGL from this tutorials. There are tons of Windows specific stuff that is not needed because of GLUT, but the OpenGL code it the same except the glHint line. So great!
Common to all lessons
- GLUT window creation, positioning and registering of draw functions and key control
- DrawGLScene: method to draw the OpenGL Scene
- ReSizeGLScene method to resize the OpenGL Scene
- keyPressed method to control key interaction
- InitGL: method to initialize OpenGL
- glutMainLoop: method for processing events in the app (main loop)
So it seems that all the code can be reused and just change the DrawGLScene from lesson to lesson.
After reviewing all lessons, probably we should focus in the first 10 lessons. After them, the rest are pretty specialized. At this point we should move to other goals.
Lesson 2
Drawing 3D models it is no that hard for basic shapes:
- Triangle:
# Draw a triangle
glBegin(GL_POLYGON) # Start drawing a polygon
glVertex3f(0.0, 1.0, 0.0) # Top
glVertex3f(1.0, -1.0, 0.0) # Bottom Right
glVertex3f(-1.0, -1.0, 0.0) # Bottom Left
glEnd() # We are done with the polygon
- Square:
glBegin(GL_QUADS) # Start drawing a 4 sided polygon
glVertex3f(-1.0, 1.0, 0.0) # Top Left
glVertex3f(1.0, 1.0, 0.0) # Top Right
glVertex3f(1.0, -1.0, 0.0) # Bottom Right
glVertex3f(-1.0, -1.0, 0.0) # Bottom Left
glEnd() # We are done with the polygon
To show the results when you are working with Double Buffering:
glutSwapBuffers()
Units
It is important to understand for example in the glVertex3 how to define the coordinates. We are working in 2D: the z coordinate is always 0. But, whats is the value for 1.0?
The dimensions of the GLUT Window are:
glutInitWindowSize(640, 480)
So, howto translate then top of the triangle 'glVertex3f(0.0, 1.0, 0.0)' to a pixel coordinates?
Probably this is done with the perspective:
gluPerspective(45.0, float(Width) / float(Height), 0.1, 100.0)
with the aspect ratio: float(Width) / float(Height). But I am not totally sure about that.
The coordinates are relatives to the block glBegin(GL_POLYGON) ... glEnd()
(0, 0, 0) is the center of the glBegin block. The top y-axis of the window seems to be a 2.5. So 480/2 = 240 = 2.5
But doing this exercise is wrong. The size of the shapes depends on the perspective, the position of the viewer ... so it is all relative. By default 1 is related the max size with respect to the aspect ratio and the dimensions of the screen.
And the drawing begins in tthe center of the glBegin block. Ok, let's continue with this intuitive idea.
Lesson3
This is a pretty easy lesson un which I directly modify the lesson2 sample. The Python code from the tutorial is not always updated with the tutorial contents (probably the content was updated), so it is better to follow this approach.
You set a color before adding a vertex. When the figure is closed, for example the triangle, if all the vertexes are from the same color, the triangle color will be the same. If not, the inside color will change from the color of a vertex to the color of the other vertex. The smoothing of colors is because of glShadeModel(GL_SMOOTH).
To set a vertex color:
glColor3f(1.0, 0.0, 0.0)
Refactoring
Ok, after 3 tutorials, is clear that the code to modify between tutorials is going to be small. So let's create a basic OpenGLApp class with all the shared code and inherit from it in each sample. And we will just redefined the needed methods.
Also, I do a heavy refactoring of the code to be more pythonic.
Lesson4
Completed also lesson4 with rotations pretty fast. i have created a gif animation with peek
- Triangle rotating based on y
- Square rotating based on x
The rotation speed is x1, x10 (x1: 1 degree per refresh)
Lesson 6
Ok, let's continue with textures. This sample seems to be more tricky because it has two versions, one with glaux (for loading images) and the other one with SOIL. There are bindings for python: https://pypi.org/project/pysoil/ (but they are unmaintained, the project does not exists anymore and there are no samples).
Let's try to find some other tutorials that we can use as the base for loading the texture. The python version for this lesson could help: https://github.com/gamedev-net/nehe-opengl/blob/master/python/lesson6/lesson6.py
There are other samples like: http://www.magikcode.com/?p=122
And pretty goood: http://www.opengl-tutorial.org/beginners-tutorials/tutorial-5-a-textured-cube/ But using C.
The translation details to Python to NeHe lesson 6: http://pyopengl.sourceforge.net/context/tutorials/nehe6.html
Ok, I have my first texture applied to a square:
Next step is to play with real textures and apply them to 3D objects!
So what's next? I don't pretend to learn all OpenGL with deep this time, but there are three more topics I would like to cover:
- Lights: lesson7
- Movements: lesson7
- Physics
- Perspective management (Lesson 42 about viewports could help)
Taking a look to the tutorials, 7-10 must be covered also.
And then I will try to jump to:
- Collision Detection (30)
- Introduction to Physical Simulations (39)
- Rope Physics (40)
Let's see how far I reach. During the trip I am refactoring the Python code so probably at the end, a mini framework for OpenGL with Python will emerge.
Lesson 7: Texture Filters, Lighting & Keyboard Control
Topics: use three different texture filters, move an object using keys on the keyboard, apply simple lighting to your OpenGL scene
All done: https://twitter.com/acstw/status/1286713147831844864
Lesson 8: Blending
What is so important about blending? "Blending is used to combine the color of a given pixel that is about to be drawn with the pixel that is already on the screen." It materials are opaque there is no blending, just draw the pixel of the outer object. But if they are transparent in some grade, the blending magic starts. Ok, time to explore this world of blending.
All done:
Ok, I would like to focus now in physics. But I am not totally sure where is the best place to learn about physics in 3D. ¿OpenGL? ¿Vulkan? Is it the same?
Maybe it is time to learn a bit Vulkan API. I will stop here my OpenGL trip until I decide where to continue.
After taking a quick look, OpenGL has more resources about physics than Vulkan, so I will continue with OpenGL. Next step is to find then best way to learn howto apply physics to 3D models with OpenGL.
https://github.com/bulletphysics/bullet3 Is this what we are searching for? Playing with it we will learn more about collision detection and physics engines which is a good thing. And probably we will know better the way to follow.
Ok, after researching about physics engines, it is a huge topic, so let's try to get some basics concepts from the tutorial:
http://nehe.gamedev.net/tutorial/introduction_to_physical_simulations/18005/
also, reading and reading it seems that this chapter is more valuable:
https://sites.google.com/site/letsmakeavoxelengine/home/physics
Lesson 9: Moving Bitmaps In 3D Space
http://nehe.gamedev.net/tutorial/moving_bitmaps_in_3d_space/17001/
It is the same that what I have already done for cubes.py sample. The logic is more complex so I have decided not to invest time in this tutorial.
Probably we must also learn from more updated resources like:
https://learnopengl.com/Getting-started/Hello-Triangle
in order to use modern OpenGL and windowing systems, like GLFW: https://learnopengl.com/Getting-started/Creating-a-window
Matrices
In the code of OpenGL all are matrices. And the different operations are done with them. Let's review why matrices are used and the basic math around them.
http://www.opengl-tutorial.org/beginners-tutorials/tutorial-3-matrices/
https://open.gl/transformations
After reading both articles, in OpenGL we work normally with vectors of 4d and convert them using different transformations. A key concept in transformations is the order in which they have to be applied:
- Scaling
- Rotation
- Translation
The order it not commutative and if we don't follow it, we will have strange results.
«The job of transforming 3D points into 2D coordinates on your screen is also accomplished through matrix transformations»
3D to 2D pipeline:
OpenGL in Linux
The OpenGL implementation in Linux is done by Mesa: https://en.wikipedia.org/wiki/Mesa_(computer_graphics) It offers the API for OpenGL, Vulkan and others APis. And it implements them using the video driver specific for the available graphics card. It seems that Mesa used DRM API which is the one implemented by the graphics drivers.
In my case it is:
[~]$ glxinfo | grep -i opengl
OpenGL vendor string: Intel
OpenGL renderer string: Mesa Intel(R) UHD Graphics 620 (KBL GT2)
OpenGL core profile version string: 4.6 (Core Profile) Mesa 20.1.3
OpenGL core profile shading language version string: 4.60
OpenGL core profile context flags: (none)
OpenGL core profile profile mask: core profile
OpenGL core profile extensions:
OpenGL version string: 4.6 (Compatibility Profile) Mesa 20.1.3
OpenGL shading language version string: 4.60
OpenGL context flags: (none)
OpenGL profile mask: compatibility profile
OpenGL extensions:
OpenGL ES profile version string: OpenGL ES 3.2 Mesa 20.1.3
OpenGL ES profile shading language version string: OpenGL ES GLSL ES 3.20
OpenGL ES profile extensions:
Then intel drivers are described in: https://01.org/linuxgraphics: «The Intel Linux Graphics project is composed by different components, such as Kernel, Mesa, VAAPI, xf86-video-intel, and LibDRM. »
The name of the driver is i915:
[~]$ lsmod | grep i915
i915 2600960 39
i2c_algo_bit 16384 1 i915
cec 61440 1 i915
drm_kms_helper 249856 1 i915
drm 618496 12 drm_kms_helper,i915
video 53248 2 thinkpad_acpi,i915
For testing OpenGL in Linux:
- glxgears (original pretty basic)
- glmark2: 2728, 2769 in two consecutives iterations
Ok, time to return to the NeHe tutorials, the 10 about creating a simple 3D World.
Loading And Moving Through A 3D World
I have decided to stop my research in OpenGl, which is for rendering rasterized graphics, in order to put our focus in ray tracing. if needed, I will continue with OpenGL in the future. It has been a great time. And we have the python samples ready to be used in the future.
Great tutorials using C: https://antongerdelan.net/opengl/hellotriangle.html
https://www.bassi.io/articles/2015/02/17/using-opengl-with-gtk/ OpenGL and GTK+