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完成一个正方体的绘制。

效果图

图中这个绿色的正方体就是绘制的结果

实现

主要分三步:

  1. 准备好顶点数据,顶点索引数据;

  2. 创建VBO和IBO,分别和顶点数据、顶点索引数据绑定;

  3. 使用绑定后的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说明。

【C++ OpenGL ES 2.0编程笔记】8: 使用VBO和IBO绘制立方体 【转】的更多相关文章

  1. OpenGL ES 2.0 渲染管线 学习笔记

    图中展示整个OpenGL ES 2.0可编程管线 图中Vertex Shader和Fragment Shader 是可编程管线: Vertex Array/Buffer objects 顶点数据来源, ...

  2. 《OpenGL® ES™ 3.0 Programming Guide》读书笔记1 ----总览

    OpenGL ES 3.0 Graphics Pipeline OpenGL ES 3.0 Vertex Shader Transform feedback: Additionally, OpenGL ...

  3. OpenGL ES 3.0 基础知识

    首先要了解OpenGL的图形管线有哪些内容,再分别去了解其中的相关的关系: 管线分别包括了顶点缓冲区/数组对象,定点着色器,纹理,片段着色器,变换反馈,图元装配,光栅化,逐片段操作,帧缓冲区.其中顶点 ...

  4. OpenGL ES 2.0 Shader 调试新思路(二): 做一个可用的原型

    OpenGL ES 2.0 Shader 调试新思路(二): 做一个可用的原型 目录 背景介绍 请参考前文OpenGL ES 2.0 Shader 调试新思路(一): 改变提问方式 优化 ledCha ...

  5. OpenGL ES 3.0顶点着色器(一)

    OpenGL ES 3.0流程图 1.Vertex Shader(顶点着色器) 顶点着色实现了一种通用的可编程方法操作顶点. 顶点着色器的输入包括以下几个: • Shader program.程序的顶 ...

  6. 梳理 Opengl ES 3.0 (二)剖析一个GLSL程序

    OpenGL ES shading language 3.0 也被称作 GLSL,是个 C风格的编程语言. Opengl ES 3.0内部有两种可编程处理单元,即Vertex processor和Fr ...

  7. 梳理 Opengl ES 3.0 (一)宏观着眼

    Opengl ES 可以理解为是在嵌入式设备上工作的一层用于处理图形显示的软件,是Opengl 的缩水版本. 下图是它的工作流程示意图: 注意图中手机左边的EGL Layer Opengl ES是跨平 ...

  8. 【AR实验室】OpenGL ES绘制相机(OpenGL ES 1.0版本)

    0x00 - 前言 之前做一些移动端的AR应用以及目前看到的一些AR应用,基本上都是这样一个套路:手机背景显示现实场景,然后在该背景上进行图形学绘制.至于图形学绘制时,相机外参的解算使用的是V-SLA ...

  9. 在 OpenGL ES 2.0 上实现视差贴图(Parallax Mapping)

    在 OpenGL ES 2.0 上实现视差贴图(Parallax Mapping) 视差贴图 最近一直在研究如何在我的 iPad 2(只支持 OpenGL ES 2.0, 不支持 3.0) 上实现 视 ...

随机推荐

  1. CSS3动画(重要)

    CSS3 动画 CSS3,我们可以创建动画,它可以取代许多网页动画图像,Flash动画,和JAVAScripts. CSS3 @keyframes 规则 要创建CSS3动画,你将不得不了解@keyfr ...

  2. Codeforces Round #475 Div. 2 A B C D

    A - Splits 题意 将一个正整数拆分成若干个正整数的和,从大到小排下来,与第一个数字相同的数字的个数为这个拆分的权重. 问\(n\)的所有拆分的不同权重可能个数. 思路 全拆成1,然后每次将2 ...

  3. django unresolved template

    参考:https://stackoverflow.com/questions/8487410/pycharm-django-1-3-static-url-in-templates-unresolved ...

  4. python--celery

    有些时候我们的一些任务比较耗时,比如我们写了一个网站,用户注册的时候需要发送邮件.但是发送邮件的过程比较耗时,用户必须要等到我们将邮件发送成功之后才会得到响应.那么有没有一种办法,当用户点击发送邮件的 ...

  5. xpath用法(持续更新ing)

    article:选取所有article元素的所有子节点 /article:选取根元素article article/a:选取所有属于article的子元素的a元素 //div:选取所有div元素 ar ...

  6. linux常用命令 ps

    linux常用命令 ps Linux中的ps命令是Process Status的缩写.ps命令用来列出系统中当前运行的那些进程.ps命令列出的是当前那些线程的快照,就是执行ps命令的那个时刻的那些进程 ...

  7. KVM(五)libvirt 介绍

    1. Libvirt 是什么 为什么需要Libvirt? Hypervisor 比如 qemu-kvm 的命令行虚拟机管理工具参数众多,难于使用. Hypervisor 种类众多,没有统一的编程接口来 ...

  8. [转载]如何将OFBIZ(opentaps)默认数据库迁移至mysql((2

    原文地址:如何将OFBIZ(opentaps)默认数据库迁移至mysql(利昂原创)作者:利昂 ofbiz自带的数据库是Derby,这是一种小型的适合与测试系统的数据库,但不适合在产品级系统中使用,所 ...

  9. nginx [error] open() "/usr/local/nginx/logs/nginx.pid" failed的解决

    今天关闭nginx后重启不了: nginx -s reload 结果报错: nginx: [error] open() "/usr/local/nginx/logs/nginx.pid&qu ...

  10. (五)mysql表操作和约束条件

    (1)表基本操作 1)创建表 create table 表名(字段 数据类型 约束条件) mysql> create table student1(id int,name varchar(50) ...