小白的OpenGL3.3自学之路(4)OpenGL3.3之EBO跟

已经有了VAO跟VBO,那还需要EBO干什么。别急,我们画一个矩形来实战一下,代码如下:

#include 
#include 
using namespace std;

const unsigned int SCR_WIDTH = 800;
const unsigned int SCR_HEIGHT = 600;

const char *vertexShaderSource = "#version 330 core\n"
"layout (location = 0) in vec3 aPos;\n"
"void main()\n"
"{\n"
"   gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);\n"
"}\0";
const char *fragmentShaderSource = "#version 330 core\n"
"out vec4 FragColor;\n"
"void main()\n"
"{\n"
"   FragColor = vec4(1.0f, 0.5f, 0.2f, 1.0f);\n"
"}\n\0";

int main()
{
    glfwInit();
    GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "LearnOpenGL", NULL, NULL);

    glfwMakeContextCurrent(window);
    gladLoadGLLoader((GLADloadproc)glfwGetProcAddress);
    float vertices[] = {
        -0.5f,-0.5f,0.0f,
        0.5f,-0.5f,0.0f,
        0.5f,0.5f,0.0f,

        -0.5f, -0.5f, 0.0f,
        0.5f, 0.5f, 0.0f,
        -0.5f, 0.5f, 0.0f,
    };
    unsigned int VBO, VAO;
    glGenVertexArrays(1, &VAO);
    glGenBuffers(1, &VBO);
    glBindVertexArray(VAO);

    glBindBuffer(GL_ARRAY_BUFFER, VBO);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
    glEnableVertexAttribArray(0);

    int vertexShader = glCreateShader(GL_VERTEX_SHADER);
    glShaderSource(vertexShader, 1, &vertexShaderSource, NULL);
    glCompileShader(vertexShader);

    int fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
    glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL);
    glCompileShader(fragmentShader);

    int shaderProgram = glCreateProgram();
    glAttachShader(shaderProgram, vertexShader);
    glAttachShader(shaderProgram, fragmentShader);
    glLinkProgram(shaderProgram);

    while (!glfwWindowShouldClose(window))
    {
        glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT);

        glUseProgram(shaderProgram);
        glBindVertexArray(VAO); 
        glDrawArrays(GL_TRIANGLES, 0, 6);

        glfwSwapBuffers(window);
        glfwPollEvents();
    }
    return 0;
}

但是这个时候你会发现,用两个三角形画矩形时有两个点重复绘制了。也就是说矩形对角线上的两个点重复了。那么我就用EBO的方式记录顶点的索引来解决这个问题。(实质就是只存储不同的顶点,并设定绘制这些顶点的顺序。)下面上代码:

#include  
#include  
#include  
using namespace std; 
const unsigned int SCR_WIDTH = 800; 
const unsigned int SCR_HEIGHT = 600; 
const char *vertexShaderSource = "#version 330 core\n" 
"layout (location = 0) in vec3 aPos;\n" 
"void main()\n" 
"{\n" 
"   gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);\n" 
"}\0"; 
const char *fragmentShaderSource = "#version 330 core\n" 
"out vec4 FragColor;\n" 
"void main()\n" 
"{\n" 
"   FragColor = vec4(1.0f, 0.5f, 0.2f, 1.0f);\n" 
"}\n\0"; 
int main() 
{
     glfwInit();
     glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
     glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); 
     glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);     
     GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "LearnOpenGL", NULL, NULL);     
     glfwMakeContextCurrent(window);     
     gladLoadGLLoader((GLADloadproc)glfwGetProcAddress);
     
     float vertices[]={
     -0.5f,-0.5f,0.0f,
      0.5f,-0.5f,0.0f,
     -0.5f, 0.5f,0.0f,
     };

     unsigned int indices[]={
     0,1,2,
     0,2,3,
     };

     unsigned int VBO,VAO,EBO;
     glGenVertexArrays(1,&VAO);
     glGenBuffers(1,&VBO);
     glGenBuffers(1,&EBO);
     glBindVertexArray(VAO);
     glBindVertexArray(VAO);

     glBindBuffer(GL_ARRAY_BUFFER,VBO);
     glBufferData(GL_ARRAY_BUFFER,sizeof(vertices),vertices,GL_STATIC_DRAW);

     glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,EBO);
     glBufferData(GL_ELEMENT_ARRAY_BUFFER,sizeof(indices),indices,GL_STATIC_DRAW);

     glVertexAttribPointer(0,3,GL_FLOAT,GL_FALSE,3*sizeof(float),(void*)0);
     glEnableVertexAttribArray(0);

     unsigned int vertexShader;
     vertexShader=glCreateShader(GL_VERTEX_SHADER);
     glShaderSource(vertexShader,1,&vertexShaderSource,NULL);
     glCompileShader(fragmentShader);

     unsigned int fragmentShader;
     fragmentShader=glCreateShader(GL_FRAGMENT_SHADER);
     glShaderSource(fragmentShader,1,&fragmentShaderSource,NULL);
     glCompileShader(fragmentShader);

     unsigned int shaderProgram;
     shaderProgram=glCreateProgram();
     glAttachShader(shaderProgram,vertexShader);
     glAttachShader(shaderProgram,fragmentShader);
     glLinkProgram(shaderProgram);

     while(!glfwWindowShouldClose(window))
     {
           glClearColor(0.2f,0.3f,0.3f,1.0f);
           glClear(GL_COLOR_BUFFER_BIT);
           glUseProgram(shaderProgram);
           glBindVertexArray(VAO);
           glDrawElements(GL_TRIANGLES,6,GL_UNSIGNED_INT,0);
           glfwSwapBuffers(window);
           glfwPollEvents();
      }
      return 0;
}

着色器的写法

着色器开头要声明版本,接着是输入和输出变量,uniform和main函数。每个着色器的入口点都是main函数。在这个函数中我们处理所有的输入变量。并将结果输出到输出变量中。

着色器的数据类型

GLSL包含C语言的默认大部分数据类型:int,float,double,unsigned int,bool.另外GLSL中没有指针和字符串,也没有字符。返回值可以为void。

GLSL有两种容器类型,分别是向量和矩阵。下面我们来聊一聊向量:
向量是一个可以包含有1,2,3,4个分量的容器。分量的类型可以是前面默认基础类型的任意一个。

类型 特点
vec2,vec3,vec4 2分量,3分量,4分量浮点型向量
ivec2,ivec3,ivec4 2分量,3分量,4分量整型向量
uvec2,uvec3,uvec4 2分量,3分量,4分量无符号整型向量
bvec2,bvec3,bvec4 2分量,3分量,4分量布尔向量

位置向量的分量可以用vec.x,vec.y,vec.z,vec.w来获取他们的第1,2,3,4个分量。颜色向量的分量可以用vec.r,vec.g,vec.b,vec.a来获取他们的第1,2,3,4个分量。纹理向量的分量可以用vec.s,vec.t,vec.p,vec.q来获取他们的第1,2,3,4个分量。可以使用下列3组标识符中的任意一组:xyzw、rgba和stpq。但不能混合到一个向量中使用。另外向量还支持调换操作。例如,将颜色数据从RGB顺序转换到BGR顺序:FragColor.rgba=FragColor.bgra;
接下来聊一聊矩阵:矩阵类型只支持浮点数。如果是方阵的话就直接matn表示n行n列。如果不是方阵的话就直接matn*m表示m行n列。具体如下:

类型 特点
mat2 两行两列
mat3 三行三列
mat4 四行四列
mat2*3 三行两列
mat3*2 两行三列

关于存储限定符
限定符用于将变量标记为输入变量,输出变量或者是常量。

限定符 特点
const 一个编译时常量,或者说是一个不可修改,只可读的参数
in 一个从上一个处理阶段或者上一个函数输出中传递过来的变量
out 传递到下一个处理阶段或者在一个函数中指定一个返回值
uniform 一个从客户端代码传递过来的变量,在顶点之间不做改变

关于in和out有一点要注意:如果我们打算从一个着色器向另一个着色器发送数据。必须要在发送方着色器中声明一个输入,然后在接收方着色器中声明一个输出。当输入与输出的类型和名字都一样的时候。OpenGL就会把两个变量链接到一起。这么说可能会有一点模糊,那我们直接上代码吧。直接把顶点着色器跟片段着色器修改一下就好了。

const char *vertexShaderSource="#version 330 core\n"
"layout (location = 0) in vec3 aPos;\n"
"out vec4 vertexColor;\n"
"void main()\n"
"{\n"
"   gl_Position = vec4(aPos,1.0);\n"
"   vertexColor = vec4(0.5f,0.0f,0.0f,1.0f);\n"
"}\n";

const char *fragmentShaderSource="#version 330 core\n"
"out vec4 FragColor;\n"
"in vec4 vertexColor;\n"
"void main()\n"
"{\n"
"   FragColor=vertexColor;\n"
"}\n\0";

可以看到的是在顶点着色器源码里面我们out vec4 vertexColor输出了一个vec4类型的向量,叫vertexColor,然后在片段着色器源码里面我们in vec4 vertexColor输入了一个vec4类型的向量,叫vertexColor。这样就前后衔接上了。

关于uniform我们需要知道的是,这是一种从CPU中的应用像GPU中的着色器发送数据的方式。uniform是全局的,所以它可以被着色器程序的任意着色器随时访问。而且除非你主动更新,不然uniform的值不会改变。接下来我们在片段着色器里面声明一个uniform值来作为片段着色器的颜色输出。那么修改后的片段着色器代码如下:

const char *fragmentShaderSource="#version 330 core\n"
"out vec4 FragColor;\n"
"uniform vec4 ourColor;\n"
"void main()\n"
"{\n"
"   FragColor=ourColor;\n"
"}\n\0";

在片段着色器里面我们声明了一个uniform vec4类型的ourColor。然后把片段着色器的输出颜色FragColor设置为uniform值的内容。但是现在uniform还是空的。那么我们还如何为它添加颜色数据呢?看一行代码:

glUseProgram(shaderProgram);
float timeValue=glfwGetTime();
float greenValue=sin(timeValue)/2.0f+0.5f;
int vertexColorLocation=glGetUniformLocation(shaderProgram,"ourColor");
glUniform4f(vertexColorLocation,0.0f,greenValue,0.0f,1.0f);
glBindVertexArray(VAO);
glDrawArrays(GL_TRIANGLES,0,3);

首先找到着色器中uniform属性的位置值。通过glGetUniformLocation函数查询uniform ourColor的位置值。找到了以后通过glUniform4f函数来设置uniform值。这次我们来设置一个随时间改变的颜色值。(注意:在更新一个uniform之前你必须先写一句glUseProgram函数来激活着色器程序然后再在这个已经激活的程序中设置uniform值)。可以看出来,对于渲染循环中需要改变的顶点属性来说,使用uniform就很方便。

可是如果打算为每一个点单独设置一个颜色呢?设置和顶点个数一样多的uniform?如果顶点个数过大那显然不现实。那么我们就换一种方法。之前我们输入数据时只包含了xyz这种顶点位置属性,现在我们加上颜色属性。代码如下:

const char *vertexShaderSource = "#version 330 core\n"
"layout (location = 0) in vec3 aPos;\n"
"layout (location = 1) in vec3 aColor;\n"
"out vec3 ourColor;\n"
"void main()\n"
"{\n"
"   gl_Position = vec4(aPos,1.0);\n"
"   ourColor = aColor;\n"
"}\0"
const char *fragmentShaderSource = "#version 330 core\n"
"out vec4 FragColor;\n"
"in vec3 ourColor;\n"
"void main()\n"
"{\n"
"   FragColor = vec4(ourColor, 1.0);\n"
"}\n\0";

在前面的文章讲到过,layout (location=0)表示顶点位置属性。layout (location=1)表示顶点颜色属性。layout (location=2)表示顶点纹理属性。所以对应的,我们要使用glVertexAttribPointer函数更新顶点格式。

#位置属性
glVertexAttribPointer(0,3,GL_FLOAT,GL_FALSE,6*sizeof(float),(void*)0);
glEnableVertexAttribArray(0);
//颜色属性
glVertexAttribPointer(1,3,GL_FLOAT,GL_FALSE,6*sizeof(float),(void*)(3*sizeof(float)));
glEnableVertexAttribArray(1);

小白的OpenGL3.3自学之路(4)OpenGL3.3之EBO跟_第1张图片

好,这次就这样了。下次开始我们来学习着色器类。
推荐一个计算机视觉群。群号是736854977。群主是百度大牛。别的就不谈了。你懂的。

你可能感兴趣的:(OpenGL)