openGL系列文章目录

文章目录

  • openGL系列文章目录
  • 前言
  • 一、绘制球体
  • 二、关键代码
    • 球体类(Sphere)
    • 主函数
    • 顶点着色器
    • 片元着色器
  • 显示效果
  • 源码下载
  • 参考

前言

openGL绘制带纹理地球,并实现鼠标键盘控制。大致思路:
1.绘制球体
2.加入纹理
3.加入鼠标控制回调函数
4.加入键盘控制回调函数
5.利用glfw、glew、glm库

一、绘制球体

我们可以系统地使用圆的几何知识来通过算法建立球体模型。我们的策略如下。
(1)在整个球体上,选择表示一系列圆形“水平切片”的精度。见图1 的左侧。
(2)将每个圆形切片的圆周细分为若干个点。见图1 的右侧。更多的点和水平切片可
以生成更精确、更平滑的球体模型。在我们的模型中,每个切片将具有相同数量的点。
(3)将顶点分组为三角形。一种方法是逐步遍历顶点,在每一步构建两个三角形。例如,
当我们沿着图2 中球体上5 个彩色顶点这一行移动时,对于这5 个顶点中的每一个,我们构建了以相应颜色显示的两个三角形(见图2,下面将更详细地描述这些步骤)。
(4)根据纹理图像的性质选择纹理坐标。在球体的情况下,存在许多地形纹理图像,假设我们选择这种纹理图像,想象一下,让这个图像围绕球体“包裹”,我们可以根据图像中纹素的最终对应位置为每个顶点指定纹理坐标。
(5)对于每个顶点,通常还希望生成法向量(Normal Vector)——垂直于模型表面的向量。我们将它们用于光照。确定法向量可能很棘手,但是在球体的情况下,从球体中心指向顶点的向量恰好等于该顶
点的法向量!图3 说明了这个特点(球体的中心用“星形”表示)。
一些模型使用索引定义三角形。请注意,在图3 中,每个顶点出现在多个三角形中,
这将导致每个顶点被多次指定。我们不希望这样做,而是会存储每个顶点一次,然后为三角形的每个角指定索引,引用所需的顶点。我们需要存储每个顶点的位置、纹理坐标和法
向量,因此这么做可以为大型模型节省内存。顶点存储在一维数组中,从最下面的水平切片中的顶点开始。使用索引时,关联的索引数组包括每个三角形角的条目。其内容是顶点数组中的整型引用(具体地说,是下标)。假
设每个切片包含n 个顶点,顶点数组以及相应索引数组的示例部分,如图4 所示。
然后,我们可以从球体底部开始,围绕每个水平切片以圆形方式遍历顶点。当我们访问
每个顶点时,我们构建两个三角形,在其右上方形成一个方形区域,如图3 所示。我们将
整个处理过程组织成嵌套循环,如下所示。
对于球体中的每个水平切片i(i 的取值从0 到球体中的所有切片)
{ 对于切片 i 中的每个顶点 j(j 的取值从 0 到切片中的所有顶点)
{ 计算顶点 j 的指向右边相邻顶点、上方顶点,以及右上方顶点的两个三角形的索引
} }
例如,考虑图2中的“红色”顶点(图5中重复出现)。这个顶点位于图5 所示的黄色
三角形的左下方,按照我们刚刚描述的循环,
它的索引序号是in+j,其中i 是当前正在处理
的切片(外循环),j 是当前正在该切片中处理的
顶点(内循环),n 是每个切片的顶点数。图5
显示了这个顶点(红色)以及它的3 个相关的
相邻顶点(见彩插),每个顶点都有公式显示它
们的索引序号。
然后使用这4 个顶点构建为此(红色)顶点生成的两个三角形(以黄色显示)。这两个
三角形的索引表中的6 个条目在图中以数字1~6 的顺序表示。注意,条目3 和6 都指向相
同的顶点,对于条目2 和4 也是如此。当我们到达以红色突出显示的顶点(即vertex[i
n+j])
时由此定义的两个三角形是由这6 个顶点构成的——其中一个三角形的条目标记为1、2、3,
引用的顶点包括vertex[in+j]、vertex[in+j+1]和vertex[(i+1)n+j];另一个三角形的条目标
记为4、5、6,引用的顶点包括vertex[i
n+j+1]、vertex[(i+1)n+j+1]和vertex[(i+1)n+j]。
程序6.1 显示了我们的球体模型的实现,类名为Sphere。生成的球体的中心位于原点。
这里还显示了使用Sphere 的代码。请注意,每个顶点都存储在包含GLM 类vec2 和vec3 实
例的C++向量中(这与之前的示例不同,之前顶点存储在浮点数组中)。vec2 和vec3 包括
了获得所需的x、y 和z 分量浮点值的方法,然后就可以如前所述将它们放入浮点缓冲区。
我们将这些值存储在可变长度C++向量中,因为长度取决于运行时指定的切片数。
请注意Sphere 类中三角形索引的计算,如前面的图5 所述。变量“prec(precision)”指
的是“精度”,在这里它被用来确定球形切片的数量和每个切片中的顶点数量。因为纹理贴
图完全包裹在球体周围,所以在纹理贴图的左右边缘相交的每个点处需要一个额外的重合
顶点。因此,顶点的总数是(prec+1)(prec+1)。由于每个顶点生成6 个三角形索引,因此索
引的总数是precprec*6。

图1.


图2

图3


图4


图5

二、关键代码

球体类(Sphere)

头文件 Sphere.h

#pragma once#include "glm/glm.hpp"
#include <cmath>
#include <vector>using namespace std;class Sphere
{private:int _numVertices;   //顶点总数int _numIndices;    //顶点索引总数vector<int> _indices;vector<glm::vec3> _vertices;  //顶点向量数vector<glm::vec2> _texCoords;vector<glm::vec3> _normals;vector<glm::vec3> _tangents;  //切片数量void init(int prec);float toRadians(float degree);public:Sphere();Sphere(int prec);int getNumVertices();int getNumIndices();vector<int> getIndices();vector<glm::vec3> getVertices();vector<glm::vec2> getTexCoords();vector<glm::vec3> getNormals();vector<glm::vec3> getTangents();};

实现文件
Sphere.cpp

#include "Sphere.h"static const float pai = 3.1415926f;Sphere::Sphere()
{init(48);
}Sphere::Sphere(int prec)
{init(prec);
}void Sphere::init(int prec)  //prec:表示精度,即一个球体被切成prec片
{_numVertices = (prec + 1) * (prec + 1);   //顶点总数_numIndices = prec * prec * 6;  //每个顶点周围被6个三角形包围,索引总数为:prec * prec * 6for (int i=0; i<_numVertices; i++){_vertices.push_back(glm::vec3());_texCoords.push_back(glm::vec2());_normals.push_back(glm::vec3());_tangents.push_back(glm::vec3());}for (int i=0; i<_numIndices; i++){_indices.push_back(0);}// calculate triangle verticesfor (int i=0; i<=prec; i++){for (int j=0; j<=prec; j++){float y = (float)(glm::cos(toRadians(180.f - i * 180.f / prec)));float x = (float)(glm::cos(toRadians(j * 360.f / prec)) * ((float)(glm::abs(glm::cos(glm::asin(y))))));float z = (float)(glm::sin(toRadians(j * 360.f / (float)(prec))) * (float)(glm::abs(glm::cos(glm::asin(y)))));_vertices[i * (prec + 1) + j] = glm::vec3(x, y, z);_texCoords[i * (prec + 1) + j] = glm::vec2(((float)(j) / prec), ((float)(i) / prec));_normals[i * (prec + 1) + j] = glm::vec3(x, y, z);// calculate tangent vectorif (((0 == x) && (1 == y) && (0 == z)) || ((0 == x) && (-1 == y) && (0 == z) )){_tangents[i * (prec + 1) + j] = glm::vec3(0.f, 0.f, -1.f);}else{_tangents[i * (prec + 1) + j] = glm::cross(glm::vec3(0.f, 1.f, 0.f), glm::vec3(x, y, z));}}}// calculate triangle indicesfor (int i=0; i<prec; i++){for (int j=0; j<prec; j++){_indices[6 * (i * prec + j) + 0] = i * (prec + 1) + j;_indices[6 * (i * prec + j) + 1] = i * (prec + 1) + j + 1;_indices[6 * (i * prec + j) + 2] = (i + 1) * (prec + 1) + j;_indices[6 * (i * prec + j) + 3] = i * (prec + 1) + j + 1;_indices[6 * (i * prec + j) + 4] = (i + 1) * (prec + 1) + j + 1;_indices[6 * (i * prec + j) + 5] = (i + 1) * (prec + 1)+ j;}}
}float Sphere::toRadians(float degree)
{return (degree * 2.f * pai) / 360.f;
}int Sphere::getNumVertices()
{return _numVertices;
}int Sphere::getNumIndices()
{return _numIndices;
}std::vector<int> Sphere::getIndices()
{return _indices;
}std::vector<glm::vec3> Sphere::getVertices()
{return _vertices;
}std::vector<glm::vec2> Sphere::getTexCoords()
{return _texCoords;
}std::vector<glm::vec3> Sphere::getNormals()
{return _normals;
}std::vector<glm::vec3> Sphere::getTangents()
{return _tangents;
}

主函数

main.cpp

#include "glew/glew.h"
#include "glfw/glfw3.h"
#include "glm/glm.hpp"
#include "glm/gtc/matrix_transform.hpp"
#include "glm/gtc/type_ptr.hpp"
#include "Sphere.h"
#include "Utils.h"
#include "camera.h"
#include <iostream>
#include <fstream>
#include <string>using namespace std;static const int screen_width = 1920;
static const int screen_height = 1080;int width = 0, height = 0;
float aspect = 0.f;
float cameraX = 0.f, cameraY = 0.f, cameraZ = 0.f;
float sphereLocX = 0.f, spherelocY = 0.f, sphereLocZ = 0.f;GLuint renderingProgram = 0;
static const int numberVAOs = 1;
static const int numberVBOs = 3;
GLuint vao[numberVAOs] = { 0 };
GLuint vbo[numberVBOs] = { 0 };glm::mat4 mMat(1.f), vMat(1.f), mvMat(1.f), pMat(1.f);
int mvLoc = 0;
int projLoc = 0;
float rotAmt = 0.f;GLuint earthTextureId = 0;Sphere earth = Sphere(48);bool firstMouse = GL_TRUE;float lastX = 0.f;
float lastY = 0.f;GLboolean keys[1024];Camera camera(glm::vec3(0.f, 0.f, 6.f));GLfloat lastFrame = 0.0f;
GLfloat deltaTime = 0.0f;void setupVertices()
{vector<int> ind = earth.getIndices();vector<glm::vec3> vert = earth.getVertices();vector<glm::vec2> tex = earth.getTexCoords();vector<glm::vec3> norm = earth.getNormals();vector<glm::vec3> tang = earth.getTangents();vector<float> pValues;vector<float> tValues;vector<float> nValues;int numIndices = earth.getNumIndices();for (int i=0; i<numIndices; i++){pValues.push_back((vert[ind[i]]).x);pValues.push_back((vert[ind[i]]).y);pValues.push_back((vert[ind[i]]).z);tValues.push_back((tex[ind[i]]).s);tValues.push_back((tex[ind[i]]).t);nValues.push_back((norm[ind[i]]).x);nValues.push_back((norm[ind[i]]).y);nValues.push_back((norm[ind[i]]).z);}glGenVertexArrays(numberVAOs, vao);glBindVertexArray(vao[0]);glGenBuffers(numberVBOs, vbo);glBindBuffer(GL_ARRAY_BUFFER, vbo[0]);glBufferData(GL_ARRAY_BUFFER, pValues.size() * 4, &pValues[0], GL_STATIC_DRAW);glBindBuffer(GL_ARRAY_BUFFER, vbo[1]);glBufferData(GL_ARRAY_BUFFER, tValues.size() * 4, &tValues[0], GL_STATIC_DRAW);glBindBuffer(GL_ARRAY_BUFFER, vbo[2]);glBufferData(GL_ARRAY_BUFFER, nValues.size() * 4, &nValues[0], GL_STATIC_DRAW);
}void press_key_callback(GLFWwindow* window, int key, int scancode, int action, int mode)
{if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS){glfwSetWindowShouldClose(window, GL_TRUE);}if (action == GLFW_PRESS){keys[key] = GL_TRUE;}else if (action == GLFW_RELEASE){keys[key] = GL_FALSE;}}void key_movement()
{if (keys[GLFW_KEY_W]){camera.ProcessKeyboard(FORWARD, deltaTime);}if (keys[GLFW_KEY_S]){camera.ProcessKeyboard(BACKWARD, deltaTime);}if (keys[GLFW_KEY_A]){camera.ProcessKeyboard(LEFT, deltaTime);}if (keys[GLFW_KEY_D]){camera.ProcessKeyboard(RIGHT, deltaTime);}}void mouse_move_callback(GLFWwindow* window, double xPos, double yPos)
{if (firstMouse){lastX = xPos;lastY = yPos;firstMouse = GL_FALSE;}GLfloat xOffset = xPos - lastX;GLfloat yOffset = lastY - yPos;lastX = xPos;lastY = yPos;camera.ProcessMouseMovement(xOffset, yOffset);
}void scroll_callback(GLFWwindow* window, double xPos, double yPos)
{camera.ProcessMouseScroll(yPos);
}void init(GLFWwindow* window)
{renderingProgram = Utils::createShaderProgram("vertShader.glsl", "fragShader.glsl");cameraX = 0.f, cameraY = 0.f, cameraZ = 4.f;sphereLocX = 0.f, spherelocY = 0.f, sphereLocZ = 0.f;glfwGetFramebufferSize(window, &width, &height);//屏幕坐标和窗口的帧缓冲/*GLFW在这里和这里解释文档中的两个坐标系。简而言之,窗口坐标是相对于监视器和 / 或窗口的,并且以不一定对应于真实屏幕像素的人造单元给出。 当DPI缩放被激活时(例如,在带有视网膜显示器的Mac上),情况尤其如此。与窗口坐标相比,帧缓冲区的大小与像素相关,以便与glViewport OpenGLs要求相匹配。请注意,在某些系统上,窗口坐标和像素坐标可以相同,但这不一定是正确的。*/aspect = (float)width / (float)height;pMat = glm::perspective(glm::radians(45.f), aspect, 0.01f, 1000.f);setupVertices();earthTextureId = Utils::loadTexture("resource/earth.jpg");
}void display(GLFWwindow* window, float currentTime)
{glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);glClearColor(0.1f, 0.2f, 1.f, 1.f);//必不可少!!!否则窗口是黑的,不会渲染任何东西glUseProgram(renderingProgram);GLfloat currentFrame = glfwGetTime();deltaTime = currentFrame - lastFrame;lastFrame = currentFrame;mvLoc = glGetUniformLocation(renderingProgram, "mv_matrix");projLoc = glGetUniformLocation(renderingProgram, "proj_matrix");//移动相机矩阵:这里必须是-cameraZ,否则相机看不到球体//vMat = glm::translate(glm::mat4(1.f), glm::vec3(cameraX, cameraY, -cameraZ));vMat = camera.GetViewMatrix();mMat = glm::translate(glm::mat4(1.f), glm::vec3(sphereLocX, spherelocY, sphereLocZ));mMat = glm::rotate(glm::mat4(1.f), currentTime * 0.5f, glm::vec3(0.f, 1.f, 0.f));pMat = glm::perspective(camera.Zoom, (GLfloat)screen_width / (GLfloat)screen_height, 0.001f, 1000.f);//右乘规则//mvMat = mMat * vMat;   //金字塔会离开视口mvMat = vMat * mMat;//更改一个uniform矩阵变量或数组的值。要更改的uniform变量的位置由location指定,location的值应该由glGetUniformLocation函数返回// 将透视矩阵和MV 矩阵复制给相应的统一变量/*通过一致变量(uniform修饰的变量)引用将一致变量值传入渲染管线。location : uniform的位置。count : 需要加载数据的数组元素的数量或者需要修改的矩阵的数量。transpose : 指明矩阵是列优先(column major)矩阵(GL_FALSE)还是行优先(row major)矩阵(GL_TRUE)。value : 指向由count个元素的数组的指针。*/glUniformMatrix4fv(mvLoc, 1, GL_FALSE, glm::value_ptr(mvMat));glUniformMatrix4fv(projLoc, 1, GL_FALSE, glm::value_ptr(pMat));//绑定到球体顶点缓冲区glBindBuffer(GL_ARRAY_BUFFER, vbo[0]);//指定了渲染时索引值为 index 的顶点属性数组的数据格式和位置/*Parametersindex指定要修改的顶点属性的索引值size指定每个顶点属性的组件数量。必须为1、2、3或者4。初始值为4。(梦维:如position是由3个(x, y, z)组成,而颜色是4个(r, g, b, a))type指定数组中每个组件的数据类型。可用的符号常量有GL_BYTE, GL_UNSIGNED_BYTE, GL_SHORT, GL_UNSIGNED_SHORT, GL_FIXED, 和 GL_FLOAT,初始值为GL_FLOAT。normalized指定当被访问时,固定点数据值是否应该被归一化(GL_TRUE)或者直接转换为固定点值(GL_FALSE)。stride指定连续顶点属性之间的偏移量。如果为0,那么顶点属性会被理解为:它们是紧密排列在一起的。初始值为0。pointer指定一个指针,指向数组中第一个顶点属性的第一个组件。初始值为0。*/glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);//启用或禁用通用顶点属性数组,参数0索引和着色器中的layout(location = 0)中的0相对应,顶点位置glEnableVertexAttribArray(0);//绑定到纹理坐标glBindBuffer(GL_ARRAY_BUFFER, vbo[1]);glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, 0);glEnableVertexAttribArray(1);//激活纹理坐标 glActiveTexture(GL_TEXTURE0);glBindTexture(GL_TEXTURE_2D, earthTextureId);//背面剔除,默认情况下,背面剔除是关闭的glEnable(GL_CULL_FACE);glFrontFace(GL_CCW);glDrawArrays(GL_TRIANGLES, 0, earth.getNumIndices());//glDrawArrays(GL_TRIANGLES, 0, earth.getNumVertices());
}void window_size_callback(GLFWwindow* window, int newWidth, int newHeight)
{glViewport(0, 0, newWidth, newHeight);aspect = (float)newWidth / (float)newHeight;pMat = glm::perspective(glm::radians(45.f), aspect, 0.01f, 1000.f);
}int main(int argc, char** argv)
{int glfwState = glfwInit();if (GLFW_FALSE == glfwState){cout << "GLFW initialize failed,invoke glfwInit()......Error file:" << __FILE__ << "......Error line:" << __LINE__ << endl;glfwTerminate();exit(EXIT_FAILURE);}glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 6);glfwWindowHint(GLFW_OPENGL_CORE_PROFILE, GLFW_OPENGL_PROFILE);glfwWindowHint(GLFW_RESIZABLE, GL_TRUE);GLFWwindow* window = glfwCreateWindow(screen_width, screen_height, "Sphere Draw", nullptr, nullptr);if (!window){cout << "GLFW create window failed,invoke glfwCreateWindow()......Error file:" << __FILE__ << "......Error line:" << __LINE__ << endl;glfwTerminate();exit(EXIT_FAILURE);}glfwMakeContextCurrent(window);glfwSetCursorPosCallback(window, mouse_move_callback);glfwSetKeyCallback(window, press_key_callback);glfwSetScrollCallback(window, scroll_callback);glfwSetWindowSizeCallback(window, window_size_callback);glfwSwapInterval(1);int glewState = glewInit();if (GLEW_OK != glewState){cout << "GLEW initialize failed,invoke glewInit()......Error file:" << __FILE__ << "......Error line:" << __LINE__ << endl;glfwTerminate();exit(EXIT_FAILURE);}init(window);while (!glfwWindowShouldClose(window)){display(window, glfwGetTime() * 0.3);//key_movement();//glfwPollEvents();key_movement();glfwSwapBuffers(window);glfwPollEvents();}glfwDestroyWindow(window);glfwTerminate();exit(EXIT_SUCCESS);return 0;
}

顶点着色器

vertShader.glsl

#version 460 corelayout(location = 0) in vec3 position;
layout(location = 1) in vec2 texCoords;uniform mat4 mv_matrix;
uniform mat4 proj_matrix;//uniform sampler2D samp;
layout (binding = 0) uniform sampler2D samp;//out vec4 color;
out vec2 tc;void main()
{gl_Position = proj_matrix * mv_matrix * vec4(position, 1.f);tc = texCoords;
}

片元着色器

#version 460 corein vec2 tc;
out vec4 color;uniform mat4 mv_matrix;
uniform mat4 proj_matrix;layout(binding = 0) uniform sampler2D samp;void main()
{color = texture(samp, tc);
}

显示效果

源码下载

源码工程下载地址

参考

《计算机图形学编程(使用OpenGL和C )》

openGL绘制带纹理地球,并实现鼠标键盘控制相关推荐

  1. openGL绘制带纹理太阳、地球、月亮,并且地球自转并且围绕太阳旋转。月亮自转也会围绕地球旋转

    openGL系列文章目录 前言 使用openGL绘制太阳.地球.月亮,太阳自转,地球自转并且围绕太阳旋转.月亮自转也会围绕地球旋转,其实月亮也会围绕太阳旋转的. 一.效果 还是有不满意的地方: 1.没 ...

  2. 一套鼠标键盘控制多台电脑,无界鼠标 (Mouse without Borders)控制界面的功能翻译

    一套鼠标键盘控制多台电脑,无界鼠标 (Mouse without Borders)控制界面的功能翻译: 易君对英语一窍不通 今天在网上找了很久 无界鼠标的使用安装教程倒是很多 但没有完整的功能界面翻译 ...

  3. Mouse Without Borders 一套鼠标键盘控制多台电脑

    当我的桌子上除了笔记本之外还多了一个台式机后,两个鼠标,两个键盘就显得十分多余,于是开始探索怎样用一套鼠标键盘控制多台电脑... 首先搜到的是这玩意--KVM切换器 在他49个赞的诱惑下,我还去淘宝搜 ...

  4. python热键+鼠标键盘控制

    python热键+鼠标键盘控制 应用:ctrl+home自动输入文本:home停止 代码:hotkey 应用:ctrl+home自动输入文本:home停止 代码:hotkey #!/usr/bin/e ...

  5. android 键盘使用教程,用鼠标键盘控制你的Android手机完整图文教程

    网上大都是91助手for android来连接手机和电脑,我自己比较偏向于豌豆荚,都可以的,下面给出我的步骤及过程中遇到的几个小问题,供大家参考! 一.鼠标键盘控制手机教程 1.在PC上,安装豌豆荚, ...

  6. android usb鼠标,用鼠标键盘控制你的Android手机完整图文教程

    网上大都是91助手for android来连接手机和电脑,我自己比较偏向于豌豆荚,都可以的,下面给出我的步骤及过程中遇到的几个小问题,供大家参考! 一.鼠标键盘控制手机教程 1.在PC上,安装豌豆荚, ...

  7. android键盘管理,用鼠标键盘控制你的Android手机完整图文教程

    32路伺服电机控制器V3.0 官方安装版 类型:编程辅助大小:15.0M语言:中文 评分:7.5 标签: 立即下载 网上大都是91助手for android来连接手机和电脑,我自己比较偏向于豌豆荚,都 ...

  8. 一套鼠标键盘控制两台电脑-绝!

    Oliver's R&D Lab C/C++/Linux 一套鼠标键盘控制两台电脑-绝! 这个工具是推荐给双电脑工作人员的,不是的就不用往下看了,嗯. synergy-----按照它主页( h ...

  9. [共享工具] 一套鼠标键盘控制两台不同系统的主机

    问题描述 用一组有线鼠标键盘控制两台主机(不同系统). 解决方法 Barrier,推荐. synergy.

最新文章

  1. IBM X3550 RAID 扩容实例
  2. rust实战入门到进阶(1)
  3. java核心技术----访问权限
  4. iris数据_Kaggle 数据可视化课程5
  5. Android实训案例(四)——关于Game,2048方块的设计,逻辑,实现,编写,加上色彩,分数等深度剖析开发过程!...
  6. mysql随机姓名_mysql 随机生成姓名函数,及模拟大量测试数据
  7. sort()函数之json对象数组排序
  8. 高等计算机教材系列·多媒体技术教程,高等学校计算机规划教材:多媒体技术应用教程...
  9. 海外服务器IP为什么被封以及解决方案
  10. MySQL与Navicat安装及配置
  11. centos7parted分区_怎样用 parted 管理硬盘分区
  12. 计算机类专业毕业论文文献综述,计算机类专业毕业论文参考文献大全
  13. arcgis api 4.X 比例尺的添加
  14. ajax传参中文乱码问题解决
  15. html5银行理财项目实践作业,金融理财产品实践报告.doc
  16. 数据库学习(四)—SQL数据查询01(简单方法条件查询)
  17. 数据挖掘项目(一)Airbnb 新用户的民宿预定结果预测
  18. Ubuntu连接蓝牙鼠标键盘
  19. 九龙证券|券商春季策略扎堆来袭 风格切换成焦点
  20. FilterConfig的用法是什么

热门文章

  1. 宏观分析(美债利率、美元、黄金、石油、标普500的相关性)
  2. python去除图片马赛克_python 检测图片是否有马赛克
  3. 5年400倍增长,Airbnb首位数据科学家揭秘他们到底怎么做的?
  4. 阿弥陀佛和他的极乐世界
  5. ES6——TDZ(暂时性死区)
  6. C. Two Shuffled Sequences
  7. Linux的安装与Linux下PHP开发环境的搭建(LAMP)
  8. OKR如何与绩效考核并行?
  9. 《旅行青蛙》的代码揭秘,攻略,体验
  10. android中的简单animation(三)accelerate(加速),decelerate(减速),anticipate,overshoot,bounce...