六、OpenGL 简单开始 --- 着色器变量(Uniforms、Vertex Arrays)

1. 着色器变量

image

2. 什么是 Uniforms?

统一变量(Uniforms) 是着色器程序(Program)特定的变量。之所以叫统一,主要区别于其他变量。
整个着色器程序的执行过程中(单次渲染: 各种着色器)和多个物体执行中(多次渲染),都可以访问并可以保持一致(如果不主动设置,如多个物体的渲染中光源属性)。

3. Uniforms 参考代码

3.1 着色器源码修改

使用 uniform 定义颜色。

#shader vertex
#version 330 core

layout(location = 0) in vec4 position;

void main()
{
   gl_Position = position;
};

#shader fragment
#version 330 core

layout(location = 0) out vec4 color;

uniform vec4 u_Color;

void main()
{
   color = u_Color;
};

3.2 着色器 C++ 部分修改

uniform 寻找位置id,并自定义颜色。

// !!!!!!!!!!! 着色器  !!!!!!!!!!!
// 源代码
ShaderProgramSource source = ParseShader("res/shaders/Basic.shader");
// 创建
unsigned int shader = CreateShader(source.VertexSource, source.FragmentSource);
// 使用
GLCall(glUseProgram(shader));

// Uniforms 
GLCall(int location = glGetUniformLocation(shader, "u_Color")); // 字符一定与源码文件的名字一样
ASSERT(location != -1); // 确保找得到
GLCall(glUniform4f(location, 0.8f, 0.3f, 0.8f, 1.0f)); // 在这里自定义颜色
// !!!!!!!!!!! 着色器  !!!!!!!!!!!
/* ------------------------- Draw A Triangle ------------------------- */

/* Loop until the user closes the window */
while (!glfwWindowShouldClose(window))
{
    /* Render here */
    glClear(GL_COLOR_BUFFER_BIT);

    // 清除、执行、断言检查 一次全包
    GLCall(glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, nullptr));

    /* Swap front and back buffers */
    glfwSwapBuffers(window);

    /* Poll for and process events */
    glfwPollEvents();
}

image

3.3 着色器 C++ 部分修改(变色区间)

// !!!!!!!!!!! 着色器  !!!!!!!!!!!
// 源代码
ShaderProgramSource source = ParseShader("res/shaders/Basic.shader");
// 创建
unsigned int shader = CreateShader(source.VertexSource, source.FragmentSource);
// 使用
GLCall(glUseProgram(shader));

// Uniforms 
GLCall(int location = glGetUniformLocation(shader, "u_Color")); // 字符一定与源码文件的名字一样
ASSERT(location != -1); // 确保找得到
// GLCall(glUniform4f(location, 0.8f, 0.3f, 0.8f, 1.0f)); // 在这里自定义颜色
// !!!!!!!!!!! 着色器  !!!!!!!!!!!

float r = 0.0f;
float increment = 0.05f;
/* ------------------------- Draw A Triangle ------------------------- */

/* Loop until the user closes the window */
while (!glfwWindowShouldClose(window))
{
    /* Render here */
    glClear(GL_COLOR_BUFFER_BIT);

    // 变色
    GLCall(glUniform4f(location, r, 0.3f, 0.8f, 1.0f));
    // 清除、执行、断言检查 一次全包
    GLCall(glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, nullptr));
    // 变色区间
    if (r > 1.0f)
        increment = -0.05f;
    else if (r < 0.0f)
        increment = 0.05f;
    r += increment;

    /* Swap front and back buffers */
    glfwSwapBuffers(window);

    /* Poll for and process events */
    glfwPollEvents();
}

image

image

4. 顶点数组 Vertex Arrays

简单说,Vertex Array 是 OpenGL 里特有的一种规范。

4.1 之前的工作

  • 在之前几篇笔记中,我们渲染顶点数据的流程是:
    • Vertex Buffer 存数据
    • Vertext Attribute Array 设置数据取用时的布局,
    • Index Buffer 存数据索引
    • Draw Call 使用数据。

一般使用时,再进行具体绑定:

// 使用 着色器
GLCall(glUseProgram(shader));
// 使用 Uniform
GLCall(glUniform4f(location, r, 0.3f, 0.8f, 1.0f));
// 绑定顶点缓冲区
GLCall(glBindBuffer(GL_ARRAY_BUFFER, buffer));
// 顶点属性布局
GLCall(glEnableVertexAttribArray(0));
GLCall(glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 2, 0));
// 绑定索引缓冲区
GLCall(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo));
// 渲染
GLCall(glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, nullptr));

4.2 现在的参考

  • 现在,带有 Vertex Array 的流程
    • 设置 Vertex Array 管理
    • Vertex Buffer 存数据
    • Vertext Attribute Array 设置数据取用时的布局,
    • Index Buffer 存数据索引
    • Draw Call 使用数据。

使用时,省略顶点缓冲区、顶点属性布局,只需要绑定 Vertex Array 即可:

// !!!!!!!!!!!顶点数组 !!!!!!!!!!!
unsigned int vao;
// 生成数组
GLCall(glGenVertexArrays(1, &vao));
GLCall(glBindVertexArray(vao));
// !!!!!!!!!!!顶点数组 !!!!!!!!!!!
// 使用 着色器
GLCall(glUseProgram(shader));
// 使用 Uniform
GLCall(glUniform4f(location, r, 0.3f, 0.8f, 1.0f));
// 自定义 vao
GLCall(glBindVertexArray(vao));
// 绑定索引缓冲区
GLCall(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo));
// 渲染
GLCall(glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, nullptr));

原因:设置顶点属性布局时,将顶点数组和顶点缓冲区关联在一起了。

4.3 之前就没有顶点数组吗?

是有的,只不过是因为编译文件的设置,存在一个默认的全局 vao;之后所有的顶点缓冲区都与这个 vao 关联。
可以通过,改变编译文件来测试:

/* 编译版本设置 (测试 顶点数组) */
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); // 主版本
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); // 副版本
// glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_COMPAT_PROFILE); // 兼容编译(包含一个全局 VAO)
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); // 非兼容编译(自己指定 VAO)

4.4 那到底用不用 vao?

具体因情况而定,做一些基准测试,决定是每个缓冲区一个 vao,还是只有一个全局 vao。(Nivida 发论文说不用快一些,但还是因情况而定比较好。)

posted @ 2024-05-13 15:47  bok_tech  阅读(118)  评论(0)    收藏  举报