OpenGL3.3教程-----First Triangle

本文介绍如何使用OpenGL中的顶点缓冲对象(VBO)来提高渲染效率。通过实例演示了VBO的创建过程及如何利用VBO进行三角形和四边形的渲染。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Vertex Buffer Object (VBO):顶点缓冲对象

你可能已经早就听过VBO了,它是学习OpenGL最重要的东西之一。VBO是存储在GPU Ram上的任意数据,他们可以快速被访问,故可加速渲染。VBO可以任何东西,关键是告知OpenGL如何解释它们。例如,可以用一个VBO来存储顶点数据(x,y,z)值,可以用VBO来存储顶点颜色值,纹理坐标值,顶点颜色值。如果有多个VBO,可以将它们存入VAO(顶点数组对象),那么就可以在所有对象之间快速切换了。

老版本的OpenGL 使用glBegin() 和 glEnd(),这会导致CPU的瓶颈,它的问题是必须把每个参数传给GPU。但是现在,我们仅是加载我们的对象数据(或者场景数据)到GPU里面,告诉OpenGL 如何解释它们,就可以开始渲染了。本例中,我们渲染一个三角形和一个四边形,不使用颜色。在 initScene中,我们设置顶点数据,然后用他们创建两个VBO.



float fTriangle[9]; // Data to render triangle (3 vertices, each has 3 floats)
float fQuad[12]; // Data to render quad using triangle strips (4 vertices, each has 3 floats)

UINT uiVBO[2]; 

void initScene(LPVOID lpParam) 

   glClearColor(0.0f, 0.5f, 1.0f, 1.0f); 

   // Setup triangle vertices
   fTriangle[0] = -0.4f; fTriangle[1] = 0.1f; fTriangle[2] = 0.0f; 
   fTriangle[3] = 0.4f; fTriangle[4] = 0.1f; fTriangle[5] = 0.0f; 
   fTriangle[6] = 0.0f; fTriangle[7] = 0.7f; fTriangle[8] = 0.0f; 
  
   // Setup quad vertices
  
   fQuad[0] = -0.2f; fQuad[1] = -0.1f; fQuad[2] = 0.0f; 
   fQuad[3] = -0.2f; fQuad[4] = -0.6f; fQuad[5] = 0.0f; 
   fQuad[6] = 0.2f; fQuad[7] = -0.1f; fQuad[8] = 0.0f; 
   fQuad[9] = 0.2f; fQuad[10] = -0.6f; fQuad[11] = 0.0f; 

   // Now we create two VBOs
   glGenBuffers(2, uiVBO); 
  
   glBindBuffer(GL_ARRAY_BUFFER, uiVBO[0]); 
   glBufferData(GL_ARRAY_BUFFER, 9*sizeof(float), fTriangle, GL_STATIC_DRAW); 

   glBindBuffer(GL_ARRAY_BUFFER, uiVBO[1]); 
   glBufferData(GL_ARRAY_BUFFER, 12*sizeof(float), fQuad, GL_STATIC_DRAW); 



glGenBuffers中,第一个参数是VBO的个数,第二参数指向存储数据的地方(本例中就是uiVBO中)。这样在uiVBO中,存储了两个缓冲区域的ID,使用这两个ID就可以读取修改这些缓冲区域。接着告诉OpenGL,调用glBindBuffer来使用第一块缓冲区域,第一个参数指向使用哪种的缓冲区域类型,第二参数缓冲区域 ID,接着调用glBufferData,将数据加载到GPU.第一个参数是缓冲区域类型,第二个是缓冲区域字节大小,第三个参数是客户端内存中源数据,最后一个参数告诉OpenGL缓冲数据的作用。本例中,我们不会改变数据,它们是静态的,所以我们设为GL_STATIC_DRAW,其他类型如GL_DYNAMIC_DRAW,GL_STREAM_DRAW ,OpenGL根据它们做一些性能优化。四边形我们也使用这两个函数,现在有了数据集了,下面看下渲染函数。


void renderScene(LPVOID lpParam) 

   // Typecast lpParam to COpenGLControl pointer
   COpenGLControl* oglControl = (COpenGLControl*)lpParam; 

   // We just clear color
   glClear(GL_COLOR_BUFFER_BIT); 

   glEnableVertexAttribArray(0); 
   // Triangle render
   glBindBuffer(GL_ARRAY_BUFFER, uiVBO[0]); 
   glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0); 
   glDrawArrays(GL_TRIANGLES, 0, 3); 

   // Quad render using triangle strip
   glBindBuffer(GL_ARRAY_BUFFER, uiVBO[1]); 
   glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0); 
   glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); 

   oglControl->swapBuffers(); 


第一个函数glVertexAttribPointer(),用它来设置顶点属性,如之前所讲,顶点有很多属性,如位置、颜色等(shaders中,可以告知OpenGL如何使用这些属性),第一个参数是顶点属性索引,本例中是0既是顶点位置,由于没使用shader,顶点在渲染管道里没有任何改变,进入管道前后的数值是一样的。注意的是我们没告知OpenGL属性索引0表示位置,这是默认的,所以函数的参数的顺序是:属性集的索引,每个顶点属性的元素个数(本例是3,位置包含了x,y,z三个值),数据类型,数据是否要归一化,属性之间的字节偏移(我们把数据紧凑放在内存中,故是0),最后一个参数是个指针,指向第一个元素。

效果图如下:


当然可以!下面是一个完整的、最小化的OpenGL程序示例,该程序将使用现代OpenGL(版本3.3及以上)在屏幕上绘制一个彩色的三角形。我们将包括所有的必要步骤和源码,确保你可以直接复制粘贴并在本地环境中运行它。 ### 完整的OpenGL绘制定制三角形 示例代码 #### 需求准备 首先,你需要安装以下库: - [GLFW](https://www.glfw.org/):用于管理窗口和用户输入。 - [GLEW 或 GLAD](http://glew.sourceforge.net/):用于加载OpenGL函数指针。 - CMake(可选):如果你想要更方便地管理和构建项目的话。 然后下载并解压相关资源文件到合适位置,比如`shaders/vertex_shader.glsl`和`shaders/fragment_shader.glsl`. #### 源码 **main.cpp** ```cpp #include <glad/glad.h> #include <GLFW/glfw3.h> #include <iostream> // Shader source code for simplicity included here. const char* vertexShaderSource = R"( #version 330 core layout (location = 0) in vec3 aPos; // Vertex positions void main() { gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0); })"; const char* fragmentShaderSource = R"( #version 330 core out vec4 FragColor; void main() { FragColor = vec4(1.0f, 0.5f, 0.2f, 1.0f); // Orange color })"; // Function prototypes unsigned int compileShader(unsigned int type, const std::string& source); unsigned int createShader(const std::string& vertexSrc, const std::string& fragmentSrc); int main() { // Initialize GLFW library and configure settings if (!glfwInit()) return -1; glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); #ifdef __APPLE__ glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); #endif // Create windowed mode window with specific width, height, title GLFWwindow* window = glfwCreateWindow(800, 600, "LearnOpenGL", NULL, NULL); if (!window) { std::cout << "Failed to create GLFW window" << std::endl; glfwTerminate(); return -1; } glfwMakeContextCurrent(window); // Load all OpenGL function pointers using glad if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) { std::cout << "Failed to initialize GLAD" << std::endl; return -1; } // Set up viewport size int screenWidth, screenHeight; glfwGetFramebufferSize(window, &screenWidth, &screenHeight); glViewport(0, 0, screenWidth, screenHeight); // Compile shaders into shader program unsigned int shaderProgram = createShader(vertexShaderSource, fragmentShaderSource); glUseProgram(shaderProgram); // Define triangle&#39;s vertices coordinates data float vertices[] = { -0.5f, -0.5f, 0.0f, 0.5f, -0.5f, 0.0f, 0.0f, 0.5f, 0.0f }; // Generate buffer objects IDs unsigned int VBO, VAO; glGenVertexArrays(1, &VAO); glGenBuffers(1, &VBO); // Bind buffers and set attribute pointer 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); // Main loop until the user closes the window or presses ESC key while(!glfwWindowShouldClose(window)) { // Input handling can be added here... // Render commands go below this line... glClearColor(0.2f, 0.3f, 0.3f, 1.0f); glClear(GL_COLOR_BUFFER_BIT); // Draw our first triangle by binding its corresponding vertex array object and calling draw arrays glBindVertexArray(VAO); glDrawArrays(GL_TRIANGLES, 0, 3); // Swap front and back buffers so that what we&#39;ve drawn becomes visible on screen glfwSwapBuffers(window); // Poll events like keyboard input, mouse movement etc., from OS queue which are then passed through callbacks registered earlier glfwPollEvents(); } // Clean up resources before exitting application glDeleteVertexArrays(1, &VAO); glDeleteBuffers(1, &VBO); glDeleteProgram(shaderProgram); // Terminate GLFW when done drawing everything glfwDestroyWindow(window); glfwTerminate(); return 0; } /** * Helper functions for compiling individual shaders as well as linking them together within one single program. */ unsigned int compileShader(unsigned int type, const std::string& source){ GLuint id = glCreateShader(type); const char* src = source.c_str(); // Convert string content to c-style null terminated string since openGL only accepts those types of strings during compilation phase. glShaderSource(id, 1, &src, nullptr); // Pass above converted value along with other arguments required per specification defined under documentation page linked at bottom right corner inside visual studio IDE itself after installing extension called &#39;resharper&#39;. glCompileShader(id); GLint 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,NULL,message ); std:: cout <<"SHADER COMPILE ERROR : \n "<<message<<std:: endl ; glDeleteShader(id ); return 0 ; } else { return id ; }} unsigned int createShader(const std::string& vertexSrc,const std::string& fragmentSrc ){ unsigned int prog=glCreateProgram (); unsigned int vs=compileShader(GL_VERTEX_SHADER ,vertexSrc ); unsigned int fs=compileShader(GL_FRAGMENT_SHADER ,fragmentSrc ); if(vs==0||fs==0){return 0;} glAttachShader(prog ,vs ); glAttachShader(prog ,fs ); glLinkProgram(prog ); glValidateProgram(prog ); glDeleteShader(vs ); glDeleteShader(fs ); return prog ; }} ``` 此段代码实现了基本功能,即打开一个新的GLFW窗口,在其中显示橙色填充的一个简单三角形。请按照说明安装所需的第三方依赖项,并根据实际情况调整路径和其他配置信息后尝试编译执行上述代码。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值