当前位置: 首页 > news >正文

opengl课前要点梳理

窗口的创建和初始化

以下的都以glfw作为窗口管理库,不用glut的原因是glut虽然简单但是功能有限

1.用glfwInit()函数初始化窗口,如果创建失败返回-1

    /* Initialize the library */if (!glfwInit())return -1;


2.设置窗口的属性

    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);

3.设置窗口的显示属性,如果生成失败就推出程序

 window = glfwCreateWindow(640, 480, "Hello World", NULL, NULL);if (!window){glfwTerminate();return -1;}

4.设置 OpenGL 上下文为当前上下文: 使用 glfwMakeContextCurrent() 将刚刚创建的 OpenGL 上下文设为当前上下文。

   glfwMakeContextCurrent(window);

5.确保扩展管理库GLEW的初始化,这个主要是让下面写的函数可以被正常使用

  // 初始化 GLEWif (glewInit() != GLEW_OK){std::cout << "Error initializing GLEW!" << std::endl;return -1;}


6.检测窗口是否关闭

while (!glfwWindowShouldClose(window))
{/* glClear() 是 OpenGL 用于清除缓存的函数。GL_COLOR_BUFFER_BIT 作为参数,表示清除颜色缓冲区。 */glClear(GL_COLOR_BUFFER_BIT);/* 当渲染操作完成后,glfwSwapBuffers() 函数会交换前后缓冲区。这样,你绘制的新帧(在后缓冲区)就会显示在屏幕上,而旧的内容则被替换掉。这个操作确保了平滑的图像更新,避免了直接在前缓冲区上绘制导致的闪烁或撕裂问题。 */glfwSwapBuffers(window);/* 收集和处理所有的输入事件(如键盘按下、鼠标移动等),并更新相应的状态。*/glfwPollEvents();
}


7.最后,调用 glfwTerminate() 来释放所有分配的资源
 

glfwTerminate();


创建顶点缓冲区

1.首先我们要创建一个缓冲区,而opengl中的缓冲区都是由unsigned int类型的数据来指定的,就比如说我要访问哪个buffer就直接通过编号访问就行了,所以这里就用调用API创建一个buffer,并且为其指定Handler编号

    unsigned int buffer;glGenBuffers(1, &buffer);

2.将这个buffer绑定到opengl的指定缓冲区中

        这里就是将刚刚定义好的buffer绑定到GL的数组顶点缓冲区中

    glBindBuffer(GL_ARRAY_BUFFER, buffer);

3.给缓冲区指定大小和类型,后面的STTATIC表示只能做一次修改多次使用的数据

 float position[6]{-0.5f,0.5f,0.5f,-0.5f,0.5f,0.5f};    
glBufferData(GL_ARRAY_BUFFER, 6 * sizeof(float), position, GL_STATIC_DRAW);

4.接触绑定的缓冲区

glBindBuffer(GL_ARRAY_BUFFER, 0)

创建顶点数组 对象
 

unsigned int VAO1;
glGenVertexArrays(1, &VAO1);
glBindVertexArray(VAO1);

VAO的作用就是如果绑定了VAO就可以保存当前上下文配置的VBO配置的数据,也就是说在渲染的时候就不用再依次绑定VBO并且配置数据了 


没有VAO

glBindBuffer(GL_ARRAY_BUFFER, VBO1); // 绑定 VBO1glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(float), (void*)0);glEnableVertexAttribArray(0);glDrawArrays(GL_TRIANGLES, 0, 3); // 绘制第一个对象

有VAO

    glBindVertexArray(VAO1); // 绑定 VAO1glDrawArrays(GL_TRIANGLES, 0, 3); // 绘制第一个对象

 
指定顶点缓冲数组的布局函数glVertexAttribPointer()

对于上面的过程,GPU只是知道了这个缓冲区存放的一串数据,但是并不知道这些数据的具体含义,所以我们就需要顶点布局来规定这些字节的布局。

顶点不仅仅是代表了坐标,可能还有法向量,颜色,纹理等等信息,所以我们要区分这些信息,所以这就是glVertexAttribPointer()要做的事情

记住,布局的仍然使用的是CPU和C++,shader用的才是GPU内的东西,shader仍然需要根据匹配刚刚对应的布局

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


 

  • 属性的索引 (index):每个顶点属性(如位置、颜色、纹理坐标等)在顶点着色器中都有一个索引,用于在缓冲区中查找该属性数据。这个索引是通过 index 参数指定的。比如位置属性常用 index = 0,颜色属性常用 index = 1,纹理坐标常用 index = 2

  • 每个属性的组件数量 (size):第二个参数 size 表示每个顶点属性由多少个组件组成,例如位置数据通常是 3 个 float,表示 (x, y, z),颜色可能是 3 个 floatunsigned byte,表示 (r, g, b)

  • 数据类型 (type):第三个参数 type 是数据的类型,常见的有 GL_FLOATGL_UNSIGNED_BYTE 等,表示每个组件的数据类型。这个参数与 size 一起决定了每个属性的总大小。

  • 是否归一化 (normalized):第四个参数 normalized 用来指示是否要将定点数据(如整型数据)规范化到 [0, 1](无符号)或 [-1, 1](有符号)。如果你的数据是浮点数(如 GL_FLOAT),通常设为 GL_FALSE,因为浮点数不需要归一化。

  • 步幅 (stride) 和偏移量 (pointer)

    • stride:指的是两个连续顶点属性数据之间的间隔(字节数)。如果属性是连续排列的,可以设置为 0,OpenGL 会自动计算步幅。如果顶点缓冲区存储了多个顶点属性(如位置、颜色、纹理坐标),你需要指定每个顶点所有属性的总大小,确保 OpenGL 能正确找到下一个顶点的数据。
    • pointer:是这个属性在每个顶点数据块中的偏移量,表示属性在每个顶点中的起始位置。通常它是基于字节偏移来计算的。

别忘了还要启用这个属性glEnableVertexAttribArray(0);,后面是刚刚定义的坐标 
 

shader如何在opengl当中被运行

shader又可以分成顶点着色器片段着色器(像素着色器)

先将我们刚刚在CPU上定义好的顶点通过顶点着色器访问到屏幕上正确的位置,然后再通过像素着色器就能够得到频幕上想要的图像了

片段着色器基本就是定义再刚刚的顶点着色器内绘制怎样的颜色,由于片段着色器是每一个像素都要绘制一遍,所以提升性能一般都是在片段着色器

这些shader在游戏中就是将玩家的相机当作是位置参数,shader读取这些参数,将这些参数通过一系列的矩阵计算,计算出当前的颜色,最终投放到你的显示屏上去

 

自己写一个shader

第一个是顶点着色器

定义了一个位置信息position,location表明在顶点的第零个属性获取这个信息

gl_Position是shader自带的属性,意思就是显示的位置就是position的位置

第二个是片段着色器

定义了一个颜色(location = 0)表明对颜色附件 0设置像素颜色,而渲染的默认颜色附件就是零
color = vec4(1.0,0.0,0.0,1.0);这个就表明将颜色设置为红色,并且将不透明度设置成1,也即是完全显示

#Shader Vertex
#version 330 corelayout(location = 0) in vec4 position;void main()
{gl_Position = position;
};#Shader fragment
#version 330 corelayout(location = 0) out vec4 color;void main()
{color = vec4(1.0,0.0,0.0,1.0);
};

如何在CPU中编译运行shader

static unsigned int CompileShader(unsigned int type, const string& source)
{unsigned int id = glCreateShader(type); // 创建着色器对象const char* src = source.c_str(); // 获取着色器源代码的 C 字符串形式glShaderSource(id, 1, &src, nullptr); // 将源代码附加到着色器对象int result;glGetShaderiv(id, GL_COMPILE_STATUS, &result); // 获取编译状态if (result == GL_FALSE){int length;glGetShaderiv(id, GL_INFO_LOG_LENGTH, &length); // 获取错误日志长度char* message = (char*)alloca(length * sizeof(char)); // 在栈上分配内存glGetShaderInfoLog(id, length, &length, message); // 获取编译错误信息cout << "Failed to compile" << (type == GL_VERTEX_SHADER ? "vertex" : "fragment") << " shader!" << endl; // 打印失败信息cout << message << endl; // 打印具体的错误信息glDeleteShader(id); // 删除着色器对象return 0; // 返回 0 表示编译失败}// 返回成功的着色器 ID(未在成功路径上返回,需补充 return id;)
}static unsigned int CreateShader(const string& vertexShader, const string& fragmentShader)
{unsigned int program = glCreateProgram(); // 创建一个着色器程序对象unsigned int vs = CompileShader(GL_VERTEX_SHADER, vertexShader); // 编译顶点着色器unsigned int fs = CompileShader(GL_FRAGMENT_SHADER, fragmentShader); // 编译片段着色器glAttachShader(program, vs); // 将顶点着色器附加到程序glAttachShader(program, fs); // 将片段着色器附加到程序glLinkProgram(program); // 链接程序glValidateProgram(program); // 验证程序glDeleteShader(vs); // 删除临时顶点着色器对象glDeleteShader(fs); // 删除临时片段着色器对象return program; // 返回链接成功的着色器程序 ID
}



完结散花

学了一个下午终于全部搞定了

下面是全部的代码

#include<GL/glew.h>
#include <GLFW/glfw3.h>#include<iostream>
#include<string>
#include<fstream>
#include<sstream>
using namespace std;struct ShaderProgramSource {string VertexSource;string FragmentSource;
};static ShaderProgramSource ParseShader(const string& filepath)
{ifstream stream(filepath);enum class ShaderType{NONE = -1,VERTEX =  0,FRAGMENT = 1};string line;stringstream ss[2];ShaderType type = ShaderType::NONE;while (getline(stream,line)){if (line.find("#Shader") != string::npos){if (line.find("Vertex") != string::npos)type = ShaderType::VERTEX;else if(line.find("fragment") != string::npos)type = ShaderType::FRAGMENT;}else{ss[(int)type] << line << '\n';}}return { ss[0].str(),ss[1].str() };
}static unsigned int CompileShader(unsigned int type, const string& source)
{unsigned int id = glCreateShader(type);//创建一个着色器对象,type传入的是着色器的类型const char* src = source.c_str();glShaderSource(id,1,&src,nullptr);//将源代码和着色器链接起来glCompileShader(id);//编译着色器int result;glGetShaderiv(id, GL_COMPILE_STATUS, &result);if (result == GL_FALSE){int length;glGetShaderiv(id, GL_INFO_LOG_LENGTH, &length);char* message = (char*)alloca(length * sizeof(char));glGetShaderInfoLog(id, length, &length, message);cout << "Failed to compile" << (type ==  GL_VERTEX_SHADER ? "vertex" : "fragment") << "shader!" << endl;cout << message << endl;glDeleteShader(id);return 0;}return id;
}static unsigned int CreateShader(const string& vertexShader, const string& fragmentShader)
{unsigned int program = glCreateProgram();//创建一个着色器程序unsigned int vs = CompileShader(GL_VERTEX_SHADER,vertexShader);unsigned int fs = CompileShader(GL_FRAGMENT_SHADER,fragmentShader);glAttachShader(program, vs);glAttachShader(program, fs);//将编译好的着色器给到这个着色器程序glLinkProgram(program);//将这个着色器程序和原来的程序链接glValidateProgram(program);//验证程序glDeleteShader(vs);glDeleteShader(fs);//链接完以后就不再需要了return program;
}int main(void)
{GLFWwindow* window;/* Initialize the library */if (!glfwInit())return -1;/* Create a windowed mode window and its OpenGL context */window = glfwCreateWindow(640, 480, "Hello World", NULL, NULL);if (!window){glfwTerminate();return -1;}/* Make the window's context current */glfwMakeContextCurrent(window);// 初始化 GLEWif (glewInit() != GLEW_OK){std::cout << "Error initializing GLEW!" << std::endl;return -1;}// 设置顶点数据float position[6]{-0.5f, -0.5f,  // 顶点 10.0f,  0.5f,  // 顶点 20.5f, -0.0f   // 顶点 3};unsigned int buffer;glGenBuffers(1, &buffer);glBindBuffer(GL_ARRAY_BUFFER, buffer);glBufferData(GL_ARRAY_BUFFER, 6 * sizeof(float), position, GL_STATIC_DRAW);glEnableVertexAttribArray(0);glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(float), 0);// 不要解绑顶点缓冲区//glBindBuffer(GL_ARRAY_BUFFER, 0); // 删除这行ShaderProgramSource source = ParseShader("base.shader");unsigned int shader = CreateShader(source.VertexSource, source.FragmentSource);cout << "Vertex" << endl;cout << source.VertexSource << endl;cout << "FRAGMENT" << endl;cout << source.FragmentSource << endl;glUseProgram(shader);/* Loop until the user closes the window */while (!glfwWindowShouldClose(window)){/* Render here */glClear(GL_COLOR_BUFFER_BIT);// 绘制三角形glDrawArrays(GL_TRIANGLES, 0, 3);/* Swap front and back buffers */glfwSwapBuffers(window);/* Poll for and process events */glfwPollEvents();}glDeleteProgram(shader);//删除着色器glfwTerminate();return 0;
}


base.shader

#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;void main()
{color = vec4(1.0, 0.0, 0.0, 1.0); 
}

 

 


http://www.mrgr.cn/news/53082.html

相关文章:

  • DS链式二叉树的基本操作和OJ题(12)
  • 深入探讨ASP.NET Core中间件及其请求处理管道特性
  • 高校企业数据可视化平台功能介绍/特色功能
  • ruoyi框架配置多数据源
  • RK平台 GPIO序号转换软件
  • 基于SpringBoot+Vue+uniapp微信小程序的宿舍报修系统的详细设计和实现
  • 【PCB】ADAS
  • 乐观锁和悲观锁详解
  • 华为HCIE-Security认证考试流程、考试内容
  • 解析带有MyBatis语法的SQL字符串,获取最终的可执行SQL
  • 如何看待阿里通义千问团队发布Qwen2.5 MATH,效果怎么样,这是中国的草莓吗?
  • 自动化工具
  • 【数据结构与算法】之链表详解
  • 绿幕虚拟直播五大“硬件环境”
  • C++从入门到起飞之——红黑树 全方位剖析!
  • C++11新特性(4)
  • C语言根据日期计算星期
  • Android12.0进入默认Launcher前黑屏的解决办法
  • salary、wage与pay有啥区别?柯桥学商务英语到泓畅学校
  • 网站防护,高可用,雷池配置同步教程