• OpenGL原理与实践——核心模式(二):Shader变量、Shader类的封装以及EBO


    目录

    Shader内的一些关键字

    向量

    举例:shader之间的数据传输,并实现渐变颜色

    举例:C++向shader传输数据的过程

    代码整理——shader类的封装

    加入颜色信息

    索引绘制——EBO

    整体代码以及渲染结果


    Shader内的一些关键字

    • in:上个阶段传来的变量
    • out:输出下个阶段的内容
    • uniform:在C++程序传入shader的内容
    • main 函数:进行一系列操作

    向量

    操作非常灵活

    举例:shader之间的数据传输,并实现渐变颜色

    vertexShader.glsl

    1. #version 330 core
    2. layout (location = 0) in vec3 aPos;//由C++代码中VBO绑定的锚定点输入。
    3. out vec4 vertexColor;
    4. void main()
    5. {
    6. gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);
    7. vertexColor=vec4(0.5,0.0,0.0,1.0);
    8. };

    fragmentShader.glsl 

    1. #version 330 core
    2. out vec4 FragColor;
    3. in vec4 vertexColor;
    4. void main()
    5. {
    6. //FragColor = vec4(1.0f, 0.5f, 0.2f, 1.0f);
    7. FragColor = vertexColor;
    8. };

    vertexShader中的aPos,由

    1. //对哪个锚点进行操作:layout=0的锚点,读3个顶点,类型为float,不需要归一化,每次步长为3个float大小,从0处开始读
    2. glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);

    进行读入;

    随后vertexShader输出一个vertexColor传入下一个阶段,也就是fragmentShader。

    fragmentShader接受vertexShader的输入(vertexColor),同时再输出一个FragColor。

    举例:C++向shader传输数据的过程

    直接上代码,注意需要把glUserProgram放在前面,启用shaderProblem这个状态

    1. //渲染
    2. void render() {
    3. glUseProgram(shaderProgram);
    4. float _time = glfwGetTime();
    5. float _green = sin(_time) * 0.5f + 0.5f;
    6. int _location = glGetUniformLocation(shaderProgram, "ourColor");
    7. glUniform4f(_location, 0.0f, _green, 0.0f, 1.0f);
    8. glBindVertexArray(VAO);
    9. //以三角形模式绘制,从第0个顶点开始,起作用的有3个点
    10. glDrawArrays(GL_TRIANGLES, 0, 3);
    11. glUseProgram(0);
    12. }

    渲染结果:

    代码整理——shader类的封装

    创建一个新的Shader类,抽象出一些重合度比较高的代码:

    Shader.h

    1. #pragma once
    2. #include "Base.h"
    3. class Shader
    4. {
    5. private:
    6. unsigned int m_shaderProgram;
    7. public:
    8. Shader() {
    9. m_shaderProgram = 0;
    10. }
    11. ~Shader() {}
    12. public:
    13. void initShader(const char* _vertexPath, const char* _fragPath);
    14. void start() { glUseProgram(m_shaderProgram); }
    15. void end() { glUseProgram(0); }
    16. };

    然后把之前的代码copy进initShader即可。

    另外将一些常用的头文件集成到Base.h中:

    1. //Base.h
    2. #pragma once
    3. #include
    4. #include
    5. #include
    6. #include
    7. #include
    8. #include

    加入颜色信息

    我们在vertices中为三个顶点新添加颜色信息:

    1. float vertices[] = {
    2. //顶点信息 颜色信息
    3. -0.5f, -0.5f, 0.0f, 1.0f,0.0f,0.0f,
    4. 0.5f, -0.5f, 0.0f, 0.0f,1.0f,0.0f,
    5. 0.0f, 0.5f, 0.0f, 0.0f,0.0f,1.0f
    6. };

    同时,添加了这些信息之后,我们就需要为它们新分配一个layout,并且激活这个layout:

    这里注意步长变更为6个float,同时颜色的开始地址为第三个float处。

    1. //对哪个锚点进行操作:layout=0的锚点,读3个顶点,类型为float,不需要归一化,每次步长为3个float大小,从0处开始读
    2. glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)0);
    3. glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)(sizeof(float)*3));
    4. //打开锚点:激活
    5. glEnableVertexAttribArray(0);
    6. glEnableVertexAttribArray(1);

    之后,我们既然已经分配了layout=1的GPU空间,并令他为颜色的信息,那么在shader中也应该有所体现,需要读取layout=1处的颜色信息:

    1. //vertexShader.glsl
    2. #version 330 core
    3. layout (location = 0) in vec3 aPos;
    4. layout (location = 1) in vec3 aColor;
    5. out vec4 outColor;
    6. void main()
    7. {
    8. gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);
    9. outColor = vec4(aColor, 1.0f);
    10. };

    并将颜色信息传入到fragmentShader中:

    1. //fragmentShader.glsl
    2. #version 330 core
    3. out vec4 FragColor;
    4. in vec4 outColor;
    5. void main()
    6. {
    7. FragColor = outColor;
    8. };

    渲染结果如下: 

    索引绘制——EBO

    EBO就是用来存储顶点索引的一块区域。

    EBO的创建与绑定

    与VAO和VBO一样的流程:

    1. glGenBuffers(1, &EBO);
    2. glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
    3. glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);

     另外需要注意的是render中需要更换绘制函数:

    1. void render() {
    2. _shader.start();
    3. glBindVertexArray(VAO);
    4. //以三角形模式绘制,从第0个顶点开始,起作用的有3个点
    5. //glDrawArrays(GL_TRIANGLES, 0, 3);
    6. //以三角形模式绘制,用顶点索引
    7. glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
    8. _shader.end();
    9. }

    整体代码以及渲染结果

    1. #include "Base.h"
    2. #include "Shader.h"
    3. void framebuffer_size_callback(GLFWwindow* window, int width, int height);
    4. void processInput(GLFWwindow* window);
    5. void initModel();
    6. void initShader(const char* _vertexPath, const char* _fragPath);
    7. void render();
    8. unsigned int VBO = 0;
    9. unsigned int VAO = 0;
    10. unsigned int EBO = 0;
    11. Shader _shader;
    12. int main() {
    13. //初始化OpenGL上下文环境,OpenGL是一个状态机,会保存当前状态下的渲染状态以及管线的状态
    14. glfwInit();
    15. //,3版本以上
    16. glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    17. glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
    18. //用OpenGL核心开发模式
    19. glfwWindowHint(GLFW_OPENGL_PROFILE,GLFW_OPENGL_CORE_PROFILE);
    20. //创建窗体
    21. GLFWwindow* window = glfwCreateWindow(800, 600, "OpenGl Core", nullptr, nullptr);
    22. if (window == nullptr) {
    23. std::cout << "Failed to create GLFW window" << std::endl;
    24. glfwTerminate();
    25. return -1;
    26. }
    27. //把当前上下文绑定至当前窗口
    28. glfwMakeContextCurrent(window);
    29. //通过glad绑定各种函数指针
    30. if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) {
    31. std::cout << "Failed to initialize GLAD" << std::endl;
    32. return -1;
    33. }
    34. //视口:需要渲染的东西在哪里
    35. glViewport(0, 0, 800, 600);
    36. //当Frame大小变动,调用回调函数调整视口大小
    37. glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
    38. initModel();
    39. initShader("vertexShader.glsl", "fragmentShader.glsl");
    40. //防止窗口结束退出
    41. while (!glfwWindowShouldClose(window)) {
    42. processInput(window);
    43. //擦除画布,用定义的颜色填充
    44. glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
    45. glClear(GL_COLOR_BUFFER_BIT);
    46. render();
    47. //双缓冲
    48. glfwSwapBuffers(window);
    49. glfwPollEvents();
    50. }
    51. //结束,释放资源
    52. glfwTerminate();
    53. return 0;
    54. }
    55. void framebuffer_size_callback(GLFWwindow* window, int width, int height) {
    56. glViewport(0, 0, width, height);
    57. }
    58. void processInput(GLFWwindow* window) {
    59. if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS) {
    60. glfwSetWindowShouldClose(window, true);
    61. }
    62. }
    63. //渲染
    64. void render() {
    65. _shader.start();
    66. glBindVertexArray(VAO);
    67. //以三角形模式绘制,从第0个顶点开始,起作用的有3个点
    68. //glDrawArrays(GL_TRIANGLES, 0, 3);
    69. //以三角形模式绘制,用顶点索引
    70. glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
    71. _shader.end();
    72. }
    73. //构建模型数据:VBO,VAO
    74. void initModel() {
    75. float vertices[] = {
    76. //顶点信息 颜色信息
    77. 0.5f, 0.5f, 0.0f, 1.0f,0.0f,0.0f,
    78. 0.5f, -0.5f, 0.0f, 0.0f,1.0f,0.0f,
    79. -0.5f, -0.5f, 0.0f, 0.0f,0.0f,1.0f,
    80. -0.5f, 0.5f, 0.0f, 0.0f,1.0f,0.0f
    81. };
    82. unsigned int indices[] = {
    83. 0,1,3,
    84. 1,2,3
    85. };
    86. glGenVertexArrays(1, &VAO);
    87. glBindVertexArray(VAO);
    88. //EBO同样要位于VAO的管理之下
    89. glGenBuffers(1, &EBO);
    90. glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
    91. glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
    92. //之后的VBO便属于了VAO的管理范围
    93. glGenBuffers(1, &VBO);
    94. //绑定哪一种buffer,
    95. glBindBuffer(GL_ARRAY_BUFFER, VBO);
    96. //分配显存:分配哪种buffer,分配显存大小,分配地址,使用数据的方式
    97. glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
    98. //对哪个锚点进行操作:layout=0的锚点,读3个顶点,类型为float,不需要归一化,每次步长为3个float大小,从0处开始读
    99. glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)0);
    100. glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)(sizeof(float)*3));
    101. //打开锚点:激活
    102. glEnableVertexAttribArray(0);
    103. glEnableVertexAttribArray(1);
    104. //解绑
    105. glBindBuffer(GL_ARRAY_BUFFER, 0);
    106. glBindVertexArray(0);
    107. }
    108. //
    109. void initShader(const char* _vertexPath, const char* _fragPath) {
    110. _shader.initShader(_vertexPath, _fragPath);
    111. }

    渲染结果

  • 相关阅读:
    css设置文本属性
    基因组注释(Annotation)
    uni-app 经验分享,从入门到离职(四)——页面栈以及页面跳转的 API(开发经验总结)
    提问:为什么整数乘于0.01小数会变成很多小数?java+js
    [附源码]JAVA毕业设计健身房管理系统(系统+LW)
    Dubbo源码-Provider服务端ServiceBean初始化和属性注入
    TcpServer::start都做了些什么
    JEB反编译器crack版功能和选项
    [Linux入门]---进程的概念
    Mapstruct 搭配MP分页食用 - 参考自ballcat项目
  • 原文地址:https://blog.csdn.net/Jason6620/article/details/128025232