【C++ OpenGL ES 2.0编程笔记】8: 使用VBO和IBO绘制立方体 【转】
http://blog.csdn.net/kesalin/article/details/8351935
前言
本文介绍了OpenGL ES 2.0 中的顶点缓冲对象(VBO: Vertex Buffer Object)和索引缓冲对象(IBO: Indice Buffer Object)的用法,
在之前的文章中图元的绘制没用使用VBO, 要绘制的顶点数据是以顶点数组的形式保存在客户端的内存中的,在每次调用glDrawArrays
或者glDrawElements
的时候,顶点数组都会被从客户端内存copy到显卡内存。
VBO的作用,就是把这份每次都要copy的顶点数组缓存在显卡的内存中,VBO使得我可以直接在显卡内存中分配并缓存顶点数据。这样就避免了每次调用draw的时候的copy操作,从而提高了渲染性能同时降低了内存带宽和设备的电量消耗。
IBO跟VBO的原理类似,只不过IBO是缓存了VBO绘制时候的Indices.
根据OpenGL ES 2.0 “Golden Book”的建议,要想获得最好的性能,就要尽可能使用VBO和IBO来完成绘制任务。
下面以一个具体的例子来展示如何使用VBO和IBO完成一个正方体的绘制。
效果图
图中这个绿色的正方体就是绘制的结果
实现
主要分三步:
准备好顶点数据,顶点索引数据;
创建VBO和IBO,分别和顶点数据、顶点索引数据绑定;
使用绑定后的VBO和IBO来完成绘制
1. 准备好顶点数据,顶点索引数据
正方体有八个顶点,定义如下:
绘制正方体主要是确定其八个顶点的坐标,如下图所示:
按编号从0~7以不同的颜色标识,下面定义出这8个顶点
// x,y,z, u,v, r,g,b,a
Vertex cubeVertex[] =
{
{ -1.0f, -1.0f, 1.0f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f }, // 0
{ 1.0f, -1.0f, 1.0f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f }, // 1
{ 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f }, // 2
{ -1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f }, // 3
{ -1.0f, -1.0f, -1.0f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f }, // 4
{ -1.0f, 1.0f, -1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f }, // 5
{ 1.0f, 1.0f, -1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f }, // 6
{ 1.0f, -1.0f, -1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f }, // 7
};
// 根据上面图中的顶点标号,可以很容易确定三角形绘制的顶点索引.
// 正方体共6个面,如下编号为0~5, 每个面是个四边形,由两个三角形组成
// 比如第0个面,由 0,1,2 这个三角形和 0,2,3 这个三角形组成。其他面依次类推
GLubyte cubeIndices[] =
{
0, 1, 2, 0, 2, 3, // Quad 0
4, 5, 6, 4, 6, 7, // Quad 1
5, 3, 2, 5, 2, 6, // Quad 2
4, 7, 1, 4, 1, 0, // Quad 3
7, 6, 2, 7, 2, 1, // Quad 4
4, 0, 3, 4, 3, 5 // Quad 5
};
2. 创建VBO和IBO,分别和顶点数据、顶点索引数据绑定
上面定义好了两个数组,接下来分别把顶点数组和索引数据绑定到VBO和IBO。
unsigned int _vbo;
unsigned int _ibo;
// ======== 创建并绑定VBO
glGenBuffers(1, &_vbo); //创建VBO,1代表创建一个, 传入的_vbo是一个unsigned int,仅保存一个VBO.
glBindBuffer(GL_ARRAY_BUFFER, _vbo); // 绑定新创建的VBO
glBufferData(GL_ARRAY_BUFFER, sizeof(cubeVertex), cubeVertex, GL_STATIC_DRAW); // 传数据, 具体参数说明后面给出
glBindBuffer(GL_ARRAY_BUFFER, 0); // 解除当前绑定的VBO
// ========= 创建并绑定IBO
glGenBuffers(1, &_ibo); // 同VBO
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _ibo); // 同VBO,仅仅是第一个参数不一样
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof( cubeIndices ), cubeIndices, GL_STATIC_DRAW); // 同VBO
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
3. 使用绑定后的VBO和IBO来完成绘制
万事俱备,现在开始绘制:
glBindTexture(GL_TEXTURE_2D, _textureCube); // 立方体的纹理,忽略
glUniformMatrix4fv(_mvp, 1, false, mvpDog.data()); // 投影矩阵,忽略
// ========== 使用 VBO和IBO进行绘制
glBindBuffer(GL_ARRAY_BUFFER, _vbo); // 绑定之前创建好的VBO
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _ibo); // 绑定之前创建好的IBO
int offset = 0; // 从_vbo中取数据要使用偏移量来指定位置
// x,y,z 在数组的开头,因此偏移量是0.
glVertexAttribPointer(_position, 3, GL_FLOAT, false, sizeof(Vertex), reinterpret_cast<void*>(offset));
offset += 3 * sizeof(float); // 纹理坐标在x,y,z之后,因此要加上3个float的偏移量
glVertexAttribPointer(_uv, 2, GL_FLOAT, false, sizeof(Vertex), reinterpret_cast<void*>(offset));
glDrawElements(GL_TRIANGLES, 36, GL_UNSIGNED_BYTE, 0); // 使用_ibo指定的36个索引来绘制。
glBindBuffer(GL_ARRAY_BUFFER, 0); // 使用完要解除VBO绑定
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); // 使用完要解除IBO绑定
完整的源代码和Shader
VboScene.h
#pragma once
#include "gl_include.h"
#include "ELShaderProgram.h"
#include <string>
#include <vector>
NS_BEGIN(elloop);
NS_BEGIN(vbo_scene);
class FirstCamera
{
public:
FirstCamera()
: _moveSpeed(5)
, _eye(0, 10, 0)
, _look(0.5, -0.4, -0.5)
, _up(0, 1, 0)
, _right(1, 0, 0)
{
}
CELL::float3 _eye;
CELL::float3 _look;
CELL::float3 _up;
CELL::float3 _right;
float _moveSpeed;
void updateCamera(float dt)
{
using namespace CELL;
float3 tempLook = _look;
float3 direction = _look - _eye;
direction = normalize(direction);
unsigned char keys[300];
GetKeyboardState(keys);
if (keys[VK_UP] & 0x80)
{
_eye -= direction * (-_moveSpeed) * dt;
_look -= direction * (-_moveSpeed) * dt;
}
if (keys[VK_DOWN] & 0x80)
{
_eye += direction * (-_moveSpeed) * dt;
_look += direction * (-_moveSpeed) * dt;
}
if (keys[VK_LEFT] & 0x80)
{
_eye -= _right * _moveSpeed * dt;
_look -= _right * _moveSpeed * dt;
}
if (keys[VK_RIGHT] & 0x80)
{
_eye += _right * _moveSpeed * dt;
_look += _right * _moveSpeed * dt;
}
}
};
class VboScene : public ShaderProgram
{
public:
struct Vertex
{
float x, y, z;
float u, v;
float r, g, b, a;
};
static VboScene* create();
void begin() override;
void end() override;
void render() override;
uniform _mvp;
uniform _textureBg;
attribute _position;
attribute _uv;
unsigned int _textureBgId;
unsigned int _textureCube;
unsigned int _vbo;
unsigned int _ibo;
FirstCamera _camera;
protected:
bool init();
VboScene()
: _mvp(-1)
, _textureBg(-1)
, _textureBgId(0)
, _position(-1)
, _uv(-1)
, _textureCube(0)
, _vbo(0)
, _ibo(0)
{
_vsFileName = "shaders/3D_projection_vs.glsl";
_fsFileName = "shaders/3D_projection_fs.glsl";
}
~VboScene()
{
glDeleteTextures(1, &_textureBgId);
glDeleteTextures(1, &_textureCube);
}
unsigned int loadMipMap(const std::vector<std::string> &fileNames);
unsigned int loadTexture(const std::string &fileName );
};
NS_END(vbo_scene);
NS_END(elloop);
VboScene.cpp
#include "scenes/VboScene.h"
#include "app_control/ELDirector.h"
#include "math/ELGeometry.h"
#include "include/freeImage/FreeImage.h"
NS_BEGIN(elloop);
NS_BEGIN(vbo_scene);
void VboScene::begin()
{
glUseProgram(_programId);
glEnableVertexAttribArray(_position);
glEnableVertexAttribArray(_uv);
}
void VboScene::end()
{
glDisableVertexAttribArray(_uv);
glDisableVertexAttribArray(_position);
glUseProgram(0);
}
bool VboScene::init()
{
_valid = ShaderProgram::initWithFile(_vsFileName, _fsFileName);
if ( _valid )
{
_position = glGetAttribLocation(_programId, "_position");
_uv = glGetAttribLocation(_programId, "_uv");
_textureBg = glGetUniformLocation(_programId, "_textureBg");
_mvp = glGetUniformLocation(_programId, "_mvp");
}
std::vector<std::string> fileNames =
{
"images/p32x32.bmp",
"images/p16x16.bmp",
"images/p8x8.bmp",
"images/p4x4.bmp",
"images/p2X2.bmp",
"images/p1x1.bmp"
};
_textureBgId = loadMipMap(fileNames);
_textureCube = loadTexture("images/1.jpg");
_camera._eye = CELL::float3(1, 1, 1);
_camera._look = CELL::float3(0.5f, -0.4f, -5.5f);
_camera._up = CELL::float3(0.0f, 1.0f, 0.0f);
_camera._right = CELL::float3(1.0f, 0.0f, 0.0f);
Vertex cubeVertex[] =
{
{ -1.0f, -1.0f, 1.0f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f }, // 0
{ 1.0f, -1.0f, 1.0f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f }, // 1
{ 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f }, // 2
{ -1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f }, // 3
{ -1.0f, -1.0f, -1.0f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f }, // 4
{ -1.0f, 1.0f, -1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f }, // 5
{ 1.0f, 1.0f, -1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f }, // 6
{ 1.0f, -1.0f, -1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f }, // 7
};
GLubyte cubeIndices[] =
{
0, 1, 2, 0, 2, 3, // Quad 0
4, 5, 6, 4, 6, 7, // Quad 1
5, 3, 2, 5, 2, 6, // Quad 2
4, 7, 1, 4, 1, 0, // Quad 3
7, 6, 2, 7, 2, 1, // Quad 4
4, 0, 3, 4, 3, 5 // Quad 5
};
// create vertex buffer object.
glGenBuffers(1, &_vbo);
glBindBuffer(GL_ARRAY_BUFFER, _vbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(cubeVertex), cubeVertex, GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glGenBuffers(1, &_ibo);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _ibo);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof( cubeIndices ), cubeIndices, GL_STATIC_DRAW);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
// or
/* glBufferData(GL_ARRAY_BUFFER, sizeof vertexes, 0, GL_STATIC_DRAW);
glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof vertexes, vertexes);*/
return _valid;
}
VboScene* VboScene::create()
{
auto * self = new VboScene();
if ( self && self->init() )
{
self->autorelease();
return self;
}
return nullptr;
}
unsigned int VboScene::loadMipMap(const std::vector<std::string> &fileNames)
{
unsigned int textureId = 0;
glGenTextures(1, &textureId);
glBindTexture(GL_TEXTURE_2D, textureId);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
size_t nums = fileNames.size();
for (size_t i=0; i<nums; ++i)
{
FREE_IMAGE_FORMAT format = FreeImage_GetFileType(fileNames[i].c_str(), 0);
FIBITMAP *bitmap = FreeImage_Load(format, fileNames[i].c_str(), 0);
bitmap = FreeImage_ConvertTo24Bits(bitmap);
BYTE *pixels = FreeImage_GetBits(bitmap);
int width = FreeImage_GetWidth(bitmap);
int height = FreeImage_GetHeight(bitmap);
for (size_t j = 0; j < width*height * 3; j += 3)
{
BYTE temp = pixels[j];
pixels[j] = pixels[j + 2];
pixels[j + 2] = temp;
}
glTexImage2D(GL_TEXTURE_2D, i, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, pixels);
FreeImage_Unload(bitmap);
}
return textureId;
}
void VboScene::render()
{
using namespace CELL;
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
auto director = Director::getInstance();
Size s = director->getFrameSize();
float width = s._width;
float height = s._height;
glViewport(0, 0, width, height);
_camera.updateCamera(0.016);
float gSize = 100;
float gPos = -5;
float repeat = 100;
Vertex ground[] =
{
{ -gSize, gPos, -gSize, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f },
{ gSize, gPos, -gSize, repeat, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f },
{ gSize, gPos, gSize, repeat, repeat, 1.0f, 1.0f, 1.0f, 1.0f },
{ -gSize, gPos, -gSize, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f },
{ gSize, gPos, gSize, repeat, repeat, 1.0f, 1.0f, 1.0f, 1.0f },
{ -gSize, gPos, gSize, 0.0f, repeat, 1.0f, 1.0f, 1.0f, 1.0f },
};
begin();
matrix4 matWorld(1);
matrix4 matView = lookAt(_camera._eye, _camera._look, _camera._up);
matrix4 matProj = perspective(45.0f, width / height, 0.1f, 100.f);
matrix4 mvp = matProj * matView * matWorld;
// draw ground.
glUniform1i(_textureBg, 0);
glBindTexture(GL_TEXTURE_2D, _textureBgId);
glUniformMatrix4fv(_mvp, 1, false, mvp.data());
glVertexAttribPointer(_position, 3, GL_FLOAT, false, sizeof(Vertex), &ground[0].x);
glVertexAttribPointer(_uv, 2, GL_FLOAT, false, sizeof(Vertex), &ground[0].u);
glDrawArrays(GL_TRIANGLES, 0, sizeof(ground) / sizeof (ground[0]));
// draw cube,
matrix4 matTrans;
matTrans.translate(0, 0, -1);
matrix4 mvpDog = matProj * matView * matTrans;
glBindTexture(GL_TEXTURE_2D, _textureCube);
glUniformMatrix4fv(_mvp, 1, false, mvpDog.data());
glBindBuffer(GL_ARRAY_BUFFER, _vbo);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _ibo);
int offset = 0;
glVertexAttribPointer(_position, 3, GL_FLOAT, false, sizeof(Vertex), reinterpret_cast<void*>(offset));
offset += 3 * sizeof(float); // u,v is after x,y,z three floats.
glVertexAttribPointer(_uv, 2, GL_FLOAT, false, sizeof(Vertex), reinterpret_cast<void*>(offset));
glDrawElements(GL_TRIANGLES, 36, GL_UNSIGNED_BYTE, 0); // use ibo.
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
end();
}
unsigned int VboScene::loadTexture(const std::string &fileName)
{
unsigned int textureId = 0;
FREE_IMAGE_FORMAT format = FreeImage_GetFileType(fileName.c_str(), 0);
FIBITMAP *bitmap = FreeImage_Load(format, fileName.c_str(), 0);
int width = FreeImage_GetWidth(bitmap);
int height = FreeImage_GetHeight(bitmap);
BYTE *pixels = FreeImage_GetBits(bitmap);
int pixelFormat = GL_RGB;
if (FIF_PNG == format)
{
bitmap = FreeImage_ConvertTo32Bits(bitmap);
pixelFormat = GL_RGBA;
for (size_t i = 0; i < width*height * 4; i += 4)
{
BYTE temp = pixels[i];
pixels[i] = pixels[i + 2];
pixels[i + 2] = temp;
}
}
else
{
bitmap = FreeImage_ConvertTo24Bits(bitmap);
for (size_t i = 0; i < width*height * 3; i += 3)
{
BYTE temp = pixels[i];
pixels[i] = pixels[i + 2];
pixels[i + 2] = temp;
}
}
glGenTextures(1, &textureId);
glBindTexture(GL_TEXTURE_2D, textureId);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexImage2D(GL_TEXTURE_2D, 0, pixelFormat, width, height, 0, pixelFormat, GL_UNSIGNED_BYTE, pixels);
FreeImage_Unload(bitmap);
return textureId;
}
NS_END(vbo_scene);
NS_END(elloop);
顶点shader: 3D_projection_vs.glsl
precision lowp float;
attribute vec3 _position;
uniform mat4 _mvp;
attribute vec2 _uv;
varying vec2 _outUv;
void main()
{
vec4 pos = vec4(_position.x, _position.y, _position.z, 1);
gl_Position = _mvp * pos;
_outUv = _uv;
}
片段shader: 3D_projection_fs.glsl
precision lowp float;
varying vec2 _outUv;
uniform sampler2D _textureBg;
void main()
{
vec4 bgColor = texture2D(_textureBg, _outUv);
gl_FragColor = bgColor;
}
源码下载
如果源码对您有帮助,请帮忙在github上给我点个Star, 感谢 :)
参考
本文侧重实战,api的使用细节请参考下面第二个链接,那里有各种OpenGL的API说明。
The OpenGL ES 2.0 programming guide / Aaftab Munshi, Dan Ginsburg, Dave Shreiner (2008)
推荐一个入门的OpenGL ES 视频
【C++ OpenGL ES 2.0编程笔记】8: 使用VBO和IBO绘制立方体 【转】的更多相关文章
- OpenGL ES 2.0 渲染管线 学习笔记
图中展示整个OpenGL ES 2.0可编程管线 图中Vertex Shader和Fragment Shader 是可编程管线: Vertex Array/Buffer objects 顶点数据来源, ...
- 《OpenGL® ES™ 3.0 Programming Guide》读书笔记1 ----总览
OpenGL ES 3.0 Graphics Pipeline OpenGL ES 3.0 Vertex Shader Transform feedback: Additionally, OpenGL ...
- OpenGL ES 3.0 基础知识
首先要了解OpenGL的图形管线有哪些内容,再分别去了解其中的相关的关系: 管线分别包括了顶点缓冲区/数组对象,定点着色器,纹理,片段着色器,变换反馈,图元装配,光栅化,逐片段操作,帧缓冲区.其中顶点 ...
- OpenGL ES 2.0 Shader 调试新思路(二): 做一个可用的原型
OpenGL ES 2.0 Shader 调试新思路(二): 做一个可用的原型 目录 背景介绍 请参考前文OpenGL ES 2.0 Shader 调试新思路(一): 改变提问方式 优化 ledCha ...
- OpenGL ES 3.0顶点着色器(一)
OpenGL ES 3.0流程图 1.Vertex Shader(顶点着色器) 顶点着色实现了一种通用的可编程方法操作顶点. 顶点着色器的输入包括以下几个: • Shader program.程序的顶 ...
- 梳理 Opengl ES 3.0 (二)剖析一个GLSL程序
OpenGL ES shading language 3.0 也被称作 GLSL,是个 C风格的编程语言. Opengl ES 3.0内部有两种可编程处理单元,即Vertex processor和Fr ...
- 梳理 Opengl ES 3.0 (一)宏观着眼
Opengl ES 可以理解为是在嵌入式设备上工作的一层用于处理图形显示的软件,是Opengl 的缩水版本. 下图是它的工作流程示意图: 注意图中手机左边的EGL Layer Opengl ES是跨平 ...
- 【AR实验室】OpenGL ES绘制相机(OpenGL ES 1.0版本)
0x00 - 前言 之前做一些移动端的AR应用以及目前看到的一些AR应用,基本上都是这样一个套路:手机背景显示现实场景,然后在该背景上进行图形学绘制.至于图形学绘制时,相机外参的解算使用的是V-SLA ...
- 在 OpenGL ES 2.0 上实现视差贴图(Parallax Mapping)
在 OpenGL ES 2.0 上实现视差贴图(Parallax Mapping) 视差贴图 最近一直在研究如何在我的 iPad 2(只支持 OpenGL ES 2.0, 不支持 3.0) 上实现 视 ...
随机推荐
- CSS3动画(重要)
CSS3 动画 CSS3,我们可以创建动画,它可以取代许多网页动画图像,Flash动画,和JAVAScripts. CSS3 @keyframes 规则 要创建CSS3动画,你将不得不了解@keyfr ...
- Codeforces Round #475 Div. 2 A B C D
A - Splits 题意 将一个正整数拆分成若干个正整数的和,从大到小排下来,与第一个数字相同的数字的个数为这个拆分的权重. 问\(n\)的所有拆分的不同权重可能个数. 思路 全拆成1,然后每次将2 ...
- django unresolved template
参考:https://stackoverflow.com/questions/8487410/pycharm-django-1-3-static-url-in-templates-unresolved ...
- python--celery
有些时候我们的一些任务比较耗时,比如我们写了一个网站,用户注册的时候需要发送邮件.但是发送邮件的过程比较耗时,用户必须要等到我们将邮件发送成功之后才会得到响应.那么有没有一种办法,当用户点击发送邮件的 ...
- xpath用法(持续更新ing)
article:选取所有article元素的所有子节点 /article:选取根元素article article/a:选取所有属于article的子元素的a元素 //div:选取所有div元素 ar ...
- linux常用命令 ps
linux常用命令 ps Linux中的ps命令是Process Status的缩写.ps命令用来列出系统中当前运行的那些进程.ps命令列出的是当前那些线程的快照,就是执行ps命令的那个时刻的那些进程 ...
- KVM(五)libvirt 介绍
1. Libvirt 是什么 为什么需要Libvirt? Hypervisor 比如 qemu-kvm 的命令行虚拟机管理工具参数众多,难于使用. Hypervisor 种类众多,没有统一的编程接口来 ...
- [转载]如何将OFBIZ(opentaps)默认数据库迁移至mysql((2
原文地址:如何将OFBIZ(opentaps)默认数据库迁移至mysql(利昂原创)作者:利昂 ofbiz自带的数据库是Derby,这是一种小型的适合与测试系统的数据库,但不适合在产品级系统中使用,所 ...
- nginx [error] open() "/usr/local/nginx/logs/nginx.pid" failed的解决
今天关闭nginx后重启不了: nginx -s reload 结果报错: nginx: [error] open() "/usr/local/nginx/logs/nginx.pid&qu ...
- (五)mysql表操作和约束条件
(1)表基本操作 1)创建表 create table 表名(字段 数据类型 约束条件) mysql> create table student1(id int,name varchar(50) ...