Welcome to follow the public account: Sumsmile/focus on image processing mobile development veterans
This is my graphics notes. The cases are from 8 assignments of yan Lingqi – Modern Computer Graphics [1]. Homework to address: http://games-cn.org/forums/topic/allhw/
The first part of the implementation of drawing a triangle, sparrow although small five viscera complete, do not use any graphics API, simulation opengL principle to complete the simple graphics razzle, the whole implementation of many details need to be carefully reviewed.
Directory:
- 1. Achieve results
- 2. Preparation
- 3. Core concepts
- 3.1 What is Rasterization
- 3.1 Implementation Process
- 4. Core code description
- 4.1 implement the MVP
- 4.2 Rasterizer implementation
- 4.3 Organize the main program
- 4.4 Compile and run
- conclusion
1. Achieve results
In addition to displaying the triangle, it can also respond to the button left and right (A/D) rotation, Esc exit.
2. Preparation
Minimal development environment:
- Develop IDE: Visual Studio Code, or VS for Windows or Xcode for Mac
- Dependency libraries: Eigen(Linear algebra operations), OpencV (rendering, image processing)
- Development language: C++
- Cmake compilation tool
3. Core concepts
3.1 What is Rasterization
“Raster” is German for “screen,” and to rasterize means to draw graphics on a screen.
Rasterization takes many forms and does not necessarily mean display on the screen.
3.1 Implementation Process
- Prepare triangle points
- MVP transform (Model, View/Camera, projection)
- Restore the normalized coordinates to the coordinates of the real scene and draw the three sides of the triangle
- Listen for buttons and process different key messages in the loop logic
Highlight the MVP transformation,
Model transform: Like when you’re taking a picture and you put an object into the scene and Pose it. A Pose is a model transform
View /camera transform: move the camera and the object at the same time so that the camera is at (0,0,0) in the global coordinate system, i.e. the origin. The view transformation is just for the convenience of calculation.
Projection: The final image of the object is projected onto the camera’s sensitive equipment. This process is projection, and the scene is scaled to unit cubes, i.e. 1x1x1, for calculation purposes.
Note: Projection can be divided into orthogonal projection and perspective projection. Orthogonal projection means that objects are projected parallel to the screen. Perspective projection simulates the characteristics of human eyes and scales according to the near large, far small
4. Core code description
4.1 implement the MVP
The model transformation
The model transformation mainly involves rotation
// Model transform, which only rotates around the z axis
Eigen::Matrix4f get_model_matrix(float rotation_angle)
{
Eigen::Matrix4f model = Eigen::Matrix4f::Identity();
// TODO: Implement this function
// Create the model matrix for rotating the triangle around the Z axis.
// Then return it.
float angle = rotation_angle * MY_PI / 180;
model(0.0) = cos(angle);
model(0.1) = -sin(angle);
model(1.0) = sin(angle);
model(1.1) = cos(angle);
return model;
}
Copy the code
The view transformation
The view transformation involves translation and rotation, and the rotation is a little more complicated, so you need the inverse of the matrix. This example is simple and deals only with translation
Eigen::Matrix4f get_view_matrix(Eigen::Vector3f eye_pos)
{
Eigen::Matrix4f view = Eigen::Matrix4f::Identity();
Eigen::Matrix4f translate;
translate << 1, 0, 0, -eye_pos[0], 0, 1, 0, -eye_pos[1], 0, 0, 1,
-eye_pos[2], 0, 0, 0, 1;
view = translate * view;
return view;
}
Copy the code
Projection transform, where perspective projection is implemented
Slightly more complicated, in several steps:
- Extrude the perspective projection cone into a cube of orthogonal projection
- Move the window to the origin and scale to 1 * 1 * 1
Note: Windows can be interpreted as display Windows, where you select which part of the scene to display on the screen
The code implementation can be calculated in 3 steps, or it can be combined into a matrix by three matrix multiplications:
-
Step 1: Perspective –> orthogonal
-
The second and third steps: orthogonal projection transformation
Matrix merging:
Eigen::Matrix4f get_projection_matrix(float eye_fov, float aspect_ratio,
float zNear, float zFar)
{
// Generate the identity matrix
Eigen::Matrix4f projection = Eigen::Matrix4f::Identity();
// Turn the Angle to radians
float eye_angle = eye_fov * MY_PI / 180;
float t,b,l,r;
// Calculate the top right left bottom coordinates
t = zNear * tan(eye_angle/2);
r = t * aspect_ratio;
l = -r;
b = -t;
Eigen::Matrix4f m1;
Eigen::Matrix4f m2;
Eigen::Matrix4f m3;
// 1. The perspective projection is compressed into an orthogonal projection
m1 << zNear,0.0.0.0,zNear,0.0.0.0,zNear + zFar, -zNear*zFar, 0.0.1.0;
// 2. The center is moved to the origin to facilitate calculation. The projection is equivalent to an observation window. The center of the window moves to the origin as a whole, without changing the relative position of the Model and view.
m2 <<1.0.0.0.0.1.0.0.0.0.1,-(zNear + zFar)/2.0.0.0.1;
// 3. Scaling into a unit cube is also easy to calculate
// Note that the scale operation is not linear and cannot be restored. The Z coordinate is lost, but it can still be used to indicate depth
m3 << 2/(r-l),0.0.0.0.2/(t-b),0.0.0.0.2/(zNear-zFar),0.0.0.0.1;
projection = m3 * m2 * m1 * projection;
return projection;
}
Copy the code
4.2 Rasterizer implementation
Rasterizer.cpp implements the core function of drawing. Take drawing a line segment as an example:
- Give the starting point and ending point of the line segment: P0 and P1
- In fact, there is no color difference in the code. Finally, the color is uniformly white (255,255,255). Opencv’s color channel is ABGR, that is, the rightmost channel is R channel
- Rasterized traversal starts at the lower left corner, while image array buffering starts at the upper left corner. Recall that in OpengL, when sampling an image texture, we have to flip it up and down. That is, the y coordinates of the image store and the render sample are oppositePerspective projection analysis note at the end of the paper[2]
Code is longer, not one by one analysis, may refer to: https://github.com/kingiluob/Games101/blob/master/Assignment1/rasterizer.cpp
Note:
- Bresenham line drawing algorithm
- Normalized coordinates are restored to screen coordinates
Code Breshnham linear interpolation algorithm is not very elegant, reference is an implementation of stack overflow: https://stackoverflow.com/a/16405254
Refer to the following blog Breshnham code, to achieve better: https://www.cnblogs.com/1Kasshole/p/14038274.html. Who is the Bresenham [3]
4.3 Organize the main program
nt main(int argc, const char** argv)
{
float angle = 0;
// Control whether the command line call, and output pictures, here I simplify the code to remove the relevant logic
bool command_line = false;
std: :string filename = "output.png";
// Set rasterization size, width * height
rst::rasterizer r(700.700);
// The position of the glasses (or camera)
Eigen::Vector3f eye_pos = {0.0.5};
// Initialize the three points of the triangle
std: :vector<Eigen::Vector3f> pos{{2.0.2 -}, {0.2.2 -}, {2 -.0.2 -}};
// Select the above three points, corner index 0, 1, 2, same as opengL based on corner point selection
std: :vector<Eigen::Vector3i> ind{{0.1.2}};
// Load the index corner of the coordinate points and, as with opengL, the same point can be used multiple times
auto pos_id = r.load_positions(pos);
auto ind_id = r.load_indices(ind);
// Keylogger
int key = 0;
int frame_count = 0;
while(key ! =27) {
// Clear buffer: color buffer and depth buffer
r.clear(rst::Buffers::Color | rst::Buffers::Depth);
/ / set the MVP
r.set_model(get_model_matrix(angle));
r.set_view(get_view_matrix(eye_pos));
r.set_projection(get_projection_matrix(45.1.0.1.50));
/ / to draw
r.draw(pos_id, ind_id, rst::Primitive::Triangle);
// CV_32FC3 is understood below
// Calculate the image memory can be larger, generate the image memory can be smaller
cv::Mat image(700.700, CV_32FC3, r.frame_buffer().data());
// 1.0f is an alpha channel, that is, completely opaque
image.convertTo(image, CV_8UC3, 1.0 f);
cv::imshow("image", image);
// Wait 10ms for keyboard input
key = cv::waitKey(10);
std: :cout << "frame count: " << frame_count++ << '\n';
if (key == 'a') {
angle += 10;
}
else if (key == 'd') {
angle -= 10;
}
}
return 0;
}
Copy the code
CV_8UC1 and CV_32FC3 indicate the meanings of these parameters
bit_depth
8 bits - on behalf of the bite, 16 bites, 32 bites, 64 bites - for example, say,
If you now create a Mat object that stores a grayscale image, the image size is 100 wide and 100 high, then the grayscale image now has 10,000 pixels, and the memory size of each pixel is 8bite,8 bits -- so it corresponds to CV_8;
S|U|F
S-- for --signed int-- signed integer
U-- for --unsigned int-- unsigned integer
F - -float--------- Single-precision floating point
C<number_of_channels>
Represents the number of channels in an image, such as:
1-- Grayscale image --grayImg-- yes -- single channel image
2--RGB color images --------- is --3 channel images
3-- RGB image with Alph channel -- is --4 channel image
Copy the code
4.4 Compile and run
Project based on cmake organization code, do not know cmake can simply refer to the basic operations of cmake and cmakelist.txt compilation.
The basic cmake command: go to the build folder and generate the cmake related file cmake..
Compile, j4 means make -j4 with 4 threads
Run. / Rasterizer
Cmakelist.txt: find_package(OpenCV REQUIRED) : run findOpencv in the root directory of Cmake. OpenCV is usually found here, but other libraries may not work, depending on how you install them. If find fails, include, such as
include_directories(/usr/local/ Cellar/eigen / 3.3.9 / include)
Copy the code
conclusion
You should be able to easily implement the results and run the demo by following the instructions in the article, which gives me a reference code at the end of the article. Thanks to Teacher Yan Lingqi for the graphics course, I have gained a lot.
Ms. Yan lingqi was born in the 1990s and won the best doctoral thesis in the same year. Now she is a doctoral supervisor and assistant professor at the University of California, Santa Barbara.
“Lingqi Yan, first of his name, the unrejected, author of seven papers, breaker of the record, and the chicken eater.” — Born to be Legendary, by Lifan Wu, UCSD
Some life and extraordinary!
Other reference
GAMES101 Implementation — 1[4] GAMES101 Implementation — 2[5] GAMES101 Experiment Notes [6]
The resources
[1]
GAMES101: Introduction to modern computer Graphics: http://games-cn.org/intro-graphics/
[2]
Perspective projection resolution’s note: https://www.cnblogs.com/1Kasshole/p/13993749.html
[3]
Bresenham introduced: en.wikipedia.org/wiki/Jack_Elton_Bresenham
[4]
Making implementation – 1: https://github.com/kingiluob/Games101/blob/master/Assignment1/rasterizer.cpp
[5]
Games101 implementation – 2: https://github.com/Quanwei1992/GAMES101/blob/master/01/rasterizer.cpp
[6]
GAMES101 lab notes: https://zhuanlan.zhihu.com/p/355170943
Welcome to follow the public account: Sumsmile/focus on image processing mobile development veterans