唠嗑:

做前端有小两年了,对自己使用的渲染技术却还是门外汉,这怎么说得过去,毕竟自己当初还是因为这神奇的技术才入了这个坑的呢。

关于OpenGL

我竟然都懒得去百度百科复制粘贴了。OpenGL其实是一套通用的API标准,它准确地指定了每个函数的结果/输出是什么以及应该如何执行。但其本身不是库也没有实现,具体实现是各大显卡厂商的显卡驱动程序。

环境搭建

首先推荐个学习网站:LearnOpenGL
虽然是英文版,英语不好的通过网页的翻译工具并不影响阅读

你完全可以跟着网上的教程去学习包括环境的搭建,值得一提的是,你要区分这几个关键词:glad、glew、glfw、freeGLUT。因为OpenGL的实现都是驱动程序,对于开发者而言,我们需要获取OpenGL的一些可操作对象,然而这些不同平台会有差异,因此就有了glew/glad,它们是OpenGL的库,在封装的同时并且跨平台的,使开发者不用担心平台的差异。glad和glew并没有太大差异,可把glad当作是glew的升级版,在一些教程里的接口基本没什么差异。

我们最终是希望所有的图形操作是展示在屏幕上的,这就需要OpenGL的上下文了。这就有了glfw/freeGLUT, 可把glfw看作是freeGLUT的升级版,其实相当于是OpenGL渲染的载体,并且也是跨平台的。不得不感叹一句,我们都是站在巨人的肩上的。

进入正题,前面推荐的网站上使用OpenGL库是glad,这里我使用的是glew(主要是因为cocos底层用的是glew),这两个没什么太大区别,对于常规操作基本没什么差异。

这里我使用的是mac,所以环境搭建会有点区别,不是mac的可以跳过。首先,我需要下载并编译glew+glfw这两个库,对于这个我就遇到了点麻烦。mac其实有个叫Homebrew的工具,使用很方便,直接brew+install+libName 即可安装想装的库。但是前提得安装了Homebrew,安装命令:
ruby -e “$(curl -fsSL https://raw.github.com/Homebrew/homebrew/go/install)”
但如果你也出现访问禁止的错误,你需要把这个域名加到你本地hosts里,像这样:
199.232.68.133 raw.githubusercontent.com
其实就是github的域名,然后终端执行:
brew install glfw
brew install glew
它们安装在你本地的/usr/local/Cellar目录

然后就是xcode(vs的配置也差不多)环境配置了,检查/usr/local/include 和 /usr/local/lib 这两个目录有没有你安装的库头文件和库,默认是会引用你Cellar目录你安装的内容,如果没有需要手动复制过去。

然后在xcode的search path 添加搜索路径即可,像这样:

但这里,环境配置部分就结束了,可以愉快的撸代码了。

OpenGL版HelloWorld

这并不是一件容易的事,你需要创建窗口、知道图形渲染过程和着色器使用,这些你都可以到前面的网站上找到,我并不想缀述。如果你通过学习能看懂以下代码,说明你已经入门了。

// Include standard headers
#include<iostream>
// Include GLEW
#include <GL/glew.h>
//glm库
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
// Include GLFW
#include <GLFW/glfw3.h>
GLFWwindow* window;
#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"
#define SCR_WIDTH 800
#define SCR_HEIGHT 600glm::vec3 cubePositions[] = {glm::vec3( 0.0f,  0.0f,  0.0f),glm::vec3( 2.0f,  5.0f, -15.0f),glm::vec3(-1.5f, -2.2f, -2.5f),glm::vec3(-3.8f, -2.0f, -12.3f),glm::vec3 (2.4f, -0.4f, -3.5f),glm::vec3(-1.7f,  3.0f, -7.5f),glm::vec3( 1.3f, -2.0f, -2.5f),glm::vec3( 1.5f,  2.0f, -2.5f),glm::vec3( 1.5f,  0.2f, -1.5f),glm::vec3(-1.3f,  1.0f, -1.5f)
};// camera
glm::vec3 cameraPos   = glm::vec3(0.0f, 0.0f,  3.0f);
glm::vec3 cameraFront = glm::vec3(0.0f, 0.0f, -1.0f);
glm::vec3 cameraUp    = glm::vec3(0.0f, 1.0f,  0.0f);bool firstMouse = true;
float yaw   = -90.0f;    // yaw is initialized to -90.0 degrees since a yaw of 0.0 results in a direction vector pointing to the right so we initially rotate a bit to the left.
float pitch =  0.0f;
float lastX =  800.0f / 2.0;
float lastY =  600.0 / 2.0;
float fov   =  45.0f;// timing
float deltaTime = 0.0f;    // time between current frame and last frame
float lastFrame = 0.0f;GLfloat vertices3d[] = {//x      y      z      r    g     b     tx    ty-0.5f, -0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f,0.5f, -0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f,0.5f,  0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f,// 0.5f,  0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f,-0.5f,  0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f,//-0.5f, -0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f,-0.5f, -0.5f,  0.5f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f,0.5f, -0.5f,  0.5f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f,0.5f,  0.5f,  0.5f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f,//0.5f,  0.5f,  0.5f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f,-0.5f,  0.5f,  0.5f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f,// -0.5f, -0.5f,  0.5f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f,-0.5f,  0.5f,  0.5f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f,-0.5f,  0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f,-0.5f, -0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f,//-0.5f, -0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f,-0.5f, -0.5f,  0.5f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f,//-0.5f,  0.5f,  0.5f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f,0.5f,  0.5f,  0.5f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f,0.5f,  0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f,0.5f, -0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f,//0.5f, -0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f,0.5f, -0.5f,  0.5f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f,//0.5f,  0.5f,  0.5f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f,-0.5f, -0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f,0.5f, -0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f,0.5f, -0.5f,  0.5f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f,// 0.5f, -0.5f,  0.5f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f,-0.5f, -0.5f,  0.5f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f,//-0.5f, -0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f,-0.5f,  0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f,0.5f,  0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f,0.5f,  0.5f,  0.5f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f,//0.5f,  0.5f,  0.5f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f,-0.5f,  0.5f,  0.5f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f,// -0.5f,  0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f
};//每两个三角形组成一个面
const int indices3d[] = {  // note that we start from 0!0, 1, 3,   // first triangle1, 2, 3,    // second triangle4, 5, 7,   // first triangle5, 6, 7,    // second triangle8, 9, 11,   // first triangle9, 10, 11,    // second triangle12, 13, 15,   // first triangle13, 14, 15,    // second triangle16, 17, 19,   // first triangle17, 18, 19,    // second triangle20, 21, 23,   // first triangle21, 22, 23,    // second triangle
};const char *vertexShaderSource = "#version 330 core\n"
"layout (location = 0) in vec3 aPos;\n"
"layout (location = 1) in vec3 aColor;\n"
"layout (location = 2) in vec2 aTexCoord;\n"
"out vec3 ourColor;\n"
"out vec2 TexCoord;\n"
//"uniform mat4 transform;\n"
"uniform mat4 model;\n"
"uniform mat4 view;\n"
"uniform mat4 projection;\n"
"void main()\n"
"{\n"
"   gl_Position = projection * view * model * vec4(aPos, 1.0);\n"
"   ourColor = aColor;\n"
"   TexCoord = vec2(aTexCoord.x, aTexCoord.y);\n"
"}\n\0";const char *fragmentShaderSource = "#version 330 core\n"
"out vec4 FragColor;\n"
"in vec3 ourColor;\n"
"in vec2 TexCoord;\n"
"uniform sampler2D texture1;\n"
"void main()\n"
"{\n"
"    FragColor = texture(texture1,TexCoord);\n"
"}\n\0";// process all input: query GLFW whether relevant keys are pressed/released this frame and react accordingly
// ---------------------------------------------------------------------------------------------------------
void processInput(GLFWwindow *window)
{if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)glfwSetWindowShouldClose(window, true);float cameraSpeed = 2.5 * deltaTime;if (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS)cameraPos -= cameraSpeed * cameraFront;if (glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS)cameraPos += cameraSpeed * cameraFront;if (glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS)cameraPos += glm::normalize(glm::cross(cameraFront, cameraUp)) * cameraSpeed;if (glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS)cameraPos -= glm::normalize(glm::cross(cameraFront, cameraUp)) * cameraSpeed;
}bool bInWinRect(double xpos,double ypos)
{if(xpos < 0 || xpos > SCR_WIDTH ){return false;}if(ypos < 0 || ypos > SCR_HEIGHT){return false;}return true;
}void mouse_callback(GLFWwindow* window, double xpos, double ypos)
{if(!bInWinRect(xpos,ypos)){return;}if(firstMouse){lastX = xpos;lastY = ypos;firstMouse = false;}
//    std::cout<<"xpos->"<<xpos<<"ypos->"<<ypos<<std::endl;float xoffset = xpos - lastX;float yoffset = lastY - ypos;lastX = xpos;lastY = ypos;float sensitivity = 0.05;xoffset *= sensitivity;yoffset *= sensitivity;yaw   += xoffset;pitch += yoffset;if(pitch > 89.0f)pitch = 89.0f;if(pitch < -89.0f)pitch = -89.0f;glm::vec3 direction;direction.x = cos(glm::radians(yaw)) * cos(glm::radians(pitch));direction.y = sin(glm::radians(pitch));direction.z = sin(glm::radians(yaw)) * cos(glm::radians(pitch));cameraFront = glm::normalize(direction);
}void framebuffer_size_callback(GLFWwindow* window, int width, int height)
{// make sure the viewport matches the new window dimensions; note that width and// height will be significantly larger than specified on retina displays.glViewport(0, 0, width, height);
}void scroll_callback(GLFWwindow* window, double xoffset, double yoffset)
{if(fov >= 1.0f && fov <= 90.0f)fov -= yoffset;else if(fov <= 1.0f)fov = 1.0f;else if(fov > 90.0f)fov = 90.0f;
}int main( void )
{// Initialise GLFWif( !glfwInit() ){fprintf( stderr, "Failed to initialize GLFW\n" );getchar();return -1;}glfwWindowHint(GLFW_SAMPLES, 4);glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // To make MacOS happy; should not be neededglfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);// Open a window and create its OpenGL contextwindow = glfwCreateWindow( SCR_WIDTH, SCR_HEIGHT, "LEARN GL", NULL, NULL);if( window == NULL ){fprintf( stderr, "Failed to open GLFW window. If you have an Intel GPU, they are not 3.3 compatible. Try the 2.1 version of the tutorials.\n" );getchar();glfwTerminate();return -1;}glfwMakeContextCurrent(window);glfwSetCursorPosCallback(window, mouse_callback);glfwSetScrollCallback(window, scroll_callback);
//    glfwSetWindowSize(window,SCR_WIDTH,SCR_HEIGHT);glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_HIDDEN);//glfwSetInputMode(window, GLFW_STICKY_KEYS, GL_TRUE);// Initialize GLEWif (glewInit() != GLEW_OK) {fprintf(stderr, "Failed to initialize GLEW\n");getchar();glfwTerminate();return -1;}glEnable(GL_DEPTH_TEST);// Ensure we can capture the escape key being pressed below// Dark blue backgroundglClearColor(0.1f, 0.1f, 0.5f, 0.0f);//创建着色器//vertex shaderint vertexShader = glCreateShader(GL_VERTEX_SHADER);glShaderSource(vertexShader,1,&vertexShaderSource,NULL);glCompileShader(vertexShader);int success;char infoLog[512];glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success);if (!success){glGetShaderInfoLog(vertexShader, 512, NULL, infoLog);std::cout << "ERROR::SHADER::VERTEX::COMPILATION_FAILED\n" << infoLog << std::endl;}// fragment shaderint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL);glCompileShader(fragmentShader);// check for shader compile errorsglGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &success);if (!success){glGetShaderInfoLog(fragmentShader, 512, NULL, infoLog);std::cout << "ERROR::SHADER::FRAGMENT::COMPILATION_FAILED\n" << infoLog << std::endl;}// link shadersint shaderProgram = glCreateProgram();glAttachShader(shaderProgram,vertexShader);glAttachShader(shaderProgram,fragmentShader);glLinkProgram(shaderProgram);//创建顶点属性及顶点缓冲区GLuint vertexArrayID;glGenVertexArrays(1, &vertexArrayID);glBindVertexArray(vertexArrayID);// This will identify our vertex bufferGLuint vertexBuffer;// Generate 1 buffer, put the resulting identifier in vertexbufferglGenBuffers(1, &vertexBuffer);// The following commands will talk about our 'vertexbuffer' bufferglBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);// Give our vertices to OpenGL.glBufferData(GL_ARRAY_BUFFER, sizeof(vertices3d), vertices3d, GL_STATIC_DRAW);//元素缓冲区对象GLuint EBO;glGenBuffers(1, &EBO);glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices3d), indices3d, GL_STATIC_DRAW);// position attributeglVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)0);glEnableVertexAttribArray(0);// color attributeglVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(3 * sizeof(float)));glEnableVertexAttribArray(1);// texture coord attributeglVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(6 * sizeof(float)));glEnableVertexAttribArray(2);//解除绑定glBindBuffer(GL_ARRAY_BUFFER, 0);glBindVertexArray(0);//加载图片int width, height, nrChannels;stbi_set_flip_vertically_on_load(true);unsigned char *data = stbi_load("/Users/hfy/Dev/Test/ShaderTest/ShaderTest/test.jpg", &width, &height, &nrChannels, 0);//纹理设置unsigned int texture1;glGenTextures(1, &texture1);glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);if (data){glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data);glGenerateMipmap(GL_TEXTURE_2D);}else{std::cout << "Failed to load texture" << std::endl;}stbi_image_free(data);//激活纹理单元,不同系统默认纹理单元可能有差异,本测试系统默认是关闭状态glUseProgram(shaderProgram);glUniform1i(glGetUniformLocation(shaderProgram, "texture1"), 1);while(!glfwWindowShouldClose(window)){float currentFrame = glfwGetTime();deltaTime = currentFrame - lastFrame;lastFrame = currentFrame;processInput(window);// Clear the screen. It's not mentioned before Tutorial 02, but it can cause flickering, so it's there nonetheless.glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // also clear the depth buffer now!// Draw nothing, see you in tutorial 2 !glActiveTexture(GL_TEXTURE0);//GL_TEXTURE0本测试系统并不对应0,而是1glBindTexture(GL_TEXTURE_2D, texture1);// get matrix's uniform location and set matrixglUseProgram(shaderProgram);//视锥glm::mat4 projection = glm::perspective(glm::radians(fov), (float)SCR_WIDTH / (float)SCR_HEIGHT, 0.1f, 100.0f);unsigned int projectionLoc = glGetUniformLocation(shaderProgram, "projection");glUniformMatrix4fv(projectionLoc, 1, GL_FALSE, glm::value_ptr(projection));//glm::mat4 view = glm::mat4(1.0f);//视图glm::mat4 view = glm::lookAt(cameraPos, cameraPos + cameraFront, cameraUp);unsigned int viewLoc  = glGetUniformLocation(shaderProgram, "view");glUniformMatrix4fv(viewLoc, 1, GL_FALSE, glm::value_ptr(view));// note: currently we set the projection matrix each frame, but since the projection matrix rarely changes it's often best practice to set it outside the main loop only once.glBindVertexArray(vertexArrayID);for (unsigned int i = 0; i < 10; i++){// calculate the model matrix for each object and pass it to shader before drawingglm::mat4 model = glm::mat4(1.0f);model = glm::translate(model, cubePositions[i]);float angle = 20.0f * (i + 1) * (float)glfwGetTime();model = glm::rotate(model, glm::radians(angle), glm::vec3(1.0f, 0.3f, 0.5f));unsigned int modelLoc = glGetUniformLocation(shaderProgram, "model");glUniformMatrix4fv(modelLoc, 1, GL_FALSE, glm::value_ptr(model));
//              glDrawArrays(GL_TRIANGLES, 0, 36);glDrawElements(GL_TRIANGLES, 36, GL_UNSIGNED_INT, 0);}// Swap buffersglfwSwapBuffers(window);glfwPollEvents();}glDeleteVertexArrays(1, &vertexArrayID);glDeleteBuffers(1, &vertexBuffer);glDeleteBuffers(1, &EBO);// Close OpenGL window and terminate GLFWglfwTerminate();return 0;
}

运行效果类似这样:

这几乎涵盖了入门篇的所有内容,代码虽然有点长,但比较直观,像着色器和摄像机等等是可以封装成类的,这里方便学习全都放一个文件里了。值得一提的是,这里需要另外附加两个库,一个是图片加载器的库,一个是glm,即:gl的数学库,里面大部分是一些矩阵转换运算的函数,你都可以去前面的推荐网站找到下载地址以及使用方式。

我对OpenGL教程的学习(入门篇)相关推荐

  1. 主动学习入门篇:什么是主动学习?有哪些具体应用

    文 | 淘系技术部 初类 来源 | 知乎 在大数据和算力的助力下,深度学习掀起了一波浪潮,在许多领域取得了显著的成绩.以监督学习为主的深度学习方法,往往期望能够拥有大量的标注样本进行训练,模型能够学到 ...

  2. OpenGL学习——入门篇 第三章 四个变换及模拟地球公转

    一.四个变换 1.1 视图变换:不同位置观察它: 涉及函数: glMatrixMode(GL_MODELVIEW);//设置当前操作的矩阵为"模型视图矩阵" glLoadIdent ...

  3. C++ OpenGL学习——入门篇

    本文参考自 opengl学习网站 opengl学习网站中文版 一.什么是OpenGL OpenGL主流上被认为是一个API(一个应用程序程序接口),提供了大量的函数,使用这些函数可以操作图形和图像.但 ...

  4. Docker超详细版教程通俗易懂 -之- 入门篇

    前言 学习Docker,你可以熟练的操作命令,能够把你的项目构建成Docker镜像! 是后端开发人员必备的技能!下面是自己的学习笔记,希望能帮助到需要的你! 特别感谢哔哩哔哩狂神:[狂神说Java]D ...

  5. 【Python3.6+Django2.0+Xadmin2.0系列教程之一(入门篇-上)】环境搭建及项目创建

    由于工作需要,接触了大半年时间的Django+xadmin框架,一直没空对这块对进行相关的梳理.最近在同事的怂恿下,就在这分享下笔者的学习及工作经验吧. 好了,话不多说,下面开始进入正题: 转载请注明 ...

  6. android studio 学习入门篇

    开发第一应用 可以开发属于自己的应用,是否有点小激动?好吧!让我们开始,首先点击Start a new Android Studio Project创建工程: 接下来需要输入应用名称(第一个字母要大写 ...

  7. 深度学习入门篇--手把手教你用 TensorFlow 训练模型

    欢迎大家前往腾讯云技术社区,获取更多腾讯海量技术实践干货哦~ 作者:付越 导语 Tensorflow在更新1.0版本之后多了很多新功能,其中放出了很多用tf框架写的深度网络结构(https://git ...

  8. TensorFlow学习——入门篇

    本文主要通过一个简单的 Demo 介绍 TensorFlow 初级 API 的使用方法,因为自己也是初学者,因此本文的目的主要是引导刚接触 TensorFlow 或者 机器学习的同学,能够从第一步开始 ...

  9. 【安卓开发】android studio 学习入门篇

    以下内容转载自:https://blog.csdn.net/myosotis5/article/details/79208707 (为防止链接失效,特备份文章内容,侵删) 开发第一应用 可以开发属于自 ...

最新文章

  1. 杜克大学和Facebook联手开发更好的光通信
  2. Ubuntu10.10更新源
  3. 251f与ips屏显示器对比_不闪屏,HDR,带鱼屏全都有,LG 29WK600宽屏显示器测评
  4. PHP经常使用正則表達式汇总
  5. 别不承认!搞懂那些数理原理,才发现它们和枯燥根本不沾边!
  6. ios 代码设置控件宽高比_iOS--利用比例纯代码适配屏幕大小
  7. 腾讯视频真实下载地址_腾讯视频如何多倍速播放视频
  8. git add后取消_满满干货!——Git知识总结
  9. ubuntu12.04 php环境搭建,Ubuntu12.04筹建php开发环境
  10. lambda表达式不使用委托(delegate) 用FUNC
  11. (JS)蔚蓝网上书店(课本案例)
  12. 问题 N: [入门OJ]车辆统计(NHOI2011XX1)
  13. 计算机excel表格公式教程,职称计算机Excel教程:显示公式的方法
  14. 《碎玉投珠》的读后感想法心得范文3800字
  15. [2015国家集训队互测]口胡
  16. Excel表格中多个文本内容快速合并到一个单元格内
  17. 波导Z769手机java下载_手机指令秘籍传授
  18. SEO算法深度分析之倒排索引,来解释SEO排名的问题
  19. photoshop的应用领域
  20. c#中的命名空间、类

热门文章

  1. jre-8u20-linux网盘,JDK1.8和JRE1.8所有版本国内网盘下载,省得大家还要去官网注册...
  2. Out-Of-Vocabulary(OOV)的理解
  3. 写毕业论文期间的一些收获和感想
  4. winwebmail的设置方法
  5. 关于电脑CPU双核四线程
  6. 四级资料免费分享 【写作万能模板 + 听力高频词 + 核心500词 + 翻译必备句型 + 作文对策】 点个关注即可全部拿走!!!
  7. 关于Android Studio中点9图的编译错误问题:Some file crunching failed
  8. vue uniapp 折叠功能实现
  9. 计算机平面设计必学英语,40个Fireworks学习英文教程
  10. 【工具使用】Fireworks基本使用