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

【OpenGL】绘制彩色立方体

参考文章:https://www.opengl-tutorial.org/cn/beginners-tutorials/tutorial-4-a-colored-cube/

一、绘制立方体

立方体有六个方形表面,而OpenGL只支持画三角形,因此需要画12个三角形,每个面两个。我们用定义三角形顶点的方式来定义这些顶点。

// Our vertices. Three consecutive floats give a 3D vertex; Three consecutive vertices give a triangle.
// A cube has 6 faces with 2 triangles each, so this makes 6*2=12 triangles, and 12*3 vertices
static const GLfloat g_vertex_buffer_data[] = {-1.0f,-1.0f,-1.0f, // triangle 1 : begin-1.0f,-1.0f, 1.0f,-1.0f, 1.0f, 1.0f, // triangle 1 : end1.0f, 1.0f,-1.0f, // triangle 2 : begin-1.0f,-1.0f,-1.0f,-1.0f, 1.0f,-1.0f, // triangle 2 : end1.0f,-1.0f, 1.0f,-1.0f,-1.0f,-1.0f,1.0f,-1.0f,-1.0f,1.0f, 1.0f,-1.0f,1.0f,-1.0f,-1.0f,-1.0f,-1.0f,-1.0f,-1.0f,-1.0f,-1.0f,-1.0f, 1.0f, 1.0f,-1.0f, 1.0f,-1.0f,1.0f,-1.0f, 1.0f,-1.0f,-1.0f, 1.0f,-1.0f,-1.0f,-1.0f,-1.0f, 1.0f, 1.0f,-1.0f,-1.0f, 1.0f,1.0f,-1.0f, 1.0f,1.0f, 1.0f, 1.0f,1.0f,-1.0f,-1.0f,1.0f, 1.0f,-1.0f,1.0f,-1.0f,-1.0f,1.0f, 1.0f, 1.0f,1.0f,-1.0f, 1.0f,1.0f, 1.0f, 1.0f,1.0f, 1.0f,-1.0f,-1.0f, 1.0f,-1.0f,1.0f, 1.0f, 1.0f,-1.0f, 1.0f,-1.0f,-1.0f, 1.0f, 1.0f,1.0f, 1.0f, 1.0f,-1.0f, 1.0f, 1.0f,1.0f,-1.0f, 1.0f
};

OpenGL的缓冲由一些标准的函数(glGenBuffers, glBindBuffer, glBufferData, glVertexAttribPointer)来创建、绑定、填充和配置,具体操作可以看之前的章节

实际上绘制一个正方体和绘制三角形类似,只需要改变顶点参数,并且将绘制的顶点数量从3改为36,如下:

glDrawArrays(GL_TRIANGLES, 0, 12*3);

关于这段代码,有几点要说明一下:

  • 截至目前我们使用的三维模型都是固定的:只能在源码中修改模型,重新编译,然后祈祷不要出什么差错。我们将在第七课中学习如何动态地加载模型。
  • 实际上,每个顶点至少出现了三次(在以上代码中搜索”-1.0f,-1.0f,-1.0f”看看)。这严重浪费了内存空间。我们将在第九课中学习怎样对此进行优化。

现在您已具备绘制一个白色立方体的所有条件。试着让着色器运行起来吧:)

此时绘制出来的立方体应该是红色的,因为之前设置了片段着色器颜色为红色:

在这里插入图片描述

二、为立方体添加颜色

从概念上讲,颜色与位置是一回事:就是数据嘛。OpenGL术语中称之为”属性(attribute)”。其实我们之前已用glEnableVertexAttribArray()和glVertexAttribPointer()设置过属性了。现在加上颜色属性,代码很相似。

首先声明颜色:每个顶点一个RGB三元组。这里随机生成一些颜色,所以效果看起来可能不太好;您可以调整得更好些,例如把顶点的位置作为颜色值。

// One color for each vertex. They were generated randomly.
static const GLfloat g_color_buffer_data[] = {0.583f,  0.771f,  0.014f,0.609f,  0.115f,  0.436f,0.327f,  0.483f,  0.844f,0.822f,  0.569f,  0.201f,0.435f,  0.602f,  0.223f,0.310f,  0.747f,  0.185f,0.597f,  0.770f,  0.761f,0.559f,  0.436f,  0.730f,0.359f,  0.583f,  0.152f,0.483f,  0.596f,  0.789f,0.559f,  0.861f,  0.639f,0.195f,  0.548f,  0.859f,0.014f,  0.184f,  0.576f,0.771f,  0.328f,  0.970f,0.406f,  0.615f,  0.116f,0.676f,  0.977f,  0.133f,0.971f,  0.572f,  0.833f,0.140f,  0.616f,  0.489f,0.997f,  0.513f,  0.064f,0.945f,  0.719f,  0.592f,0.543f,  0.021f,  0.978f,0.279f,  0.317f,  0.505f,0.167f,  0.620f,  0.077f,0.347f,  0.857f,  0.137f,0.055f,  0.953f,  0.042f,0.714f,  0.505f,  0.345f,0.783f,  0.290f,  0.734f,0.722f,  0.645f,  0.174f,0.302f,  0.455f,  0.848f,0.225f,  0.587f,  0.040f,0.517f,  0.713f,  0.338f,0.053f,  0.959f,  0.120f,0.393f,  0.621f,  0.362f,0.673f,  0.211f,  0.457f,0.820f,  0.883f,  0.371f,0.982f,  0.099f,  0.879f
};

缓冲的创建、绑定和填充方法与之前一样:

GLuint colorbuffer;
glGenBuffers(1, &colorbuffer);
glBindBuffer(GL_ARRAY_BUFFER, colorbuffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(g_color_buffer_data), g_color_buffer_data, GL_STATIC_DRAW);

配置也一样:

// 2nd attribute buffer : colors
glEnableVertexAttribArray(1);
glBindBuffer(GL_ARRAY_BUFFER, colorbuffer);
glVertexAttribPointer(1,                                // attribute. No particular reason for 1, but must match the layout in the shader.3,                                // sizeGL_FLOAT,                         // typeGL_FALSE,                         // normalized?0,                                // stride(void*)0                          // array buffer offset
);

现在在顶点着色器中已经能访问这个新增的缓冲了:

// Notice that the "1" here equals the "1" in glVertexAttribPointer
layout(location = 1) in vec3 vertexColor;

这一课的顶点着色器没有什么复杂的效果,仅仅是简单地把颜色传递到片段着色器:

// Output data ; will be interpolated for each fragment.
out vec3 fragmentColor;void main(){[...]// The color of each vertex will be interpolated// to produce the color of each fragmentfragmentColor = vertexColor;
}

在片段着色器中要再次声明片段颜色:

// Interpolated values from the vertex shaders
in vec3 fragmentColor;

然后将其拷贝到输出颜色:

// Ouput data
out vec3 color;void main(){// Output color = color specified in the vertex shader,// interpolated between all 3 surrounding verticescolor = fragmentColor;
}

于是得到:

在这里插入图片描述

呃,太难看了。为了搞清楚出现这种情况原因,我们先看看画一个”远”和”近”的三角形会发生什么:

在这里插入图片描述

似乎挺好。现在画”远”三角形:

在这里插入图片描述

它遮住了”近”三角形!它本应该在”近”三角形后面的!我们的立方体问题就在这里:一些理应被遮挡的面,因为绘制次序靠后,竟然变成可见的了。我们将用深度缓冲(Z-Buffer)算法解决它。

便签1

如果您没发现问题,把摄像机放到(4,3,-3)试试

便签2

如果”颜色和位置同为属性”,那为什么颜色要声明 out vec3 fragmentColor,而位置不需要?实际上,位置有点特殊:它是唯一必须赋初值的(否则OpenGL不知道在哪画三角形)。所以在顶点着色器里, gl_Position是内置变量。

三、深度缓冲(Z-Buffer)The Z-Buffer

该问题的解决方案是:在缓冲中存储每个片段的深度(即”Z”值);并且每次绘制片段之前要比较当前与先前片段的深度值,看谁离摄像机更近。

您可以自己实现深度缓冲,但让硬件自动完成更简单:

// Enable depth test
glEnable(GL_DEPTH_TEST);
// Accept fragment if it closer to the camera than the former one
glDepthFunc(GL_LESS);

你还需要清除除了颜色以外每一帧(frame)的深度。

// Clear the screen
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

问题解决了。

四、完整代码

my_glwidget.h

//  
// Created by liuhang on 2025/9/16.  
//  #ifndef OPENGL_LEARNING_MY_GLWIDGET_H  
#define OPENGL_LEARNING_MY_GLWIDGET_H  #include<QOpenGLWidget>  
#include<QOpenGLFunctions>  
#include<QMatrix4x4>  
#include<QTimer>  class MyGLWidget : public QOpenGLWidget,protected QOpenGLFunctions  
{  Q_OBJECT  
public:  explicit MyGLWidget(QWidget* parent = nullptr);  ~MyGLWidget() override;  protected:  void initializeGL() override;  void paintGL() override;  void resizeGL(int w,int h) override;  void doMVP();  
private:  void loadShader(std::string const& vertex_shader_path,std::string const& fragment_shader_path);  private:  GLuint vertex_array_id;  GLuint vertex_buffer_id;  GLuint color_buffer_id;  GLuint vertex_shader_id;  GLuint fragment_shader_id;  GLuint shader_program_id;  GLint uniform_model_matrix_location;  GLint uniform_view_matrix_location;  GLint uniform_projection_matrix_location;  
};  #endif //OPENGL_LEARNING_MY_GLWIDGET_H

my_glwidget.cpp

//
// Created by liuhang on 2025/9/16.
//#include "my_glwidget.h"
#include<string>
#include<fstream>
#include<sstream>
#include<iostream>
#include<filesystem>
#include<QThread>
#include<math.h>// Our vertices. Three consecutive floats give a 3D vertex; Three consecutive vertices give a triangle.
// A cube has 6 faces with 2 triangles each, so this makes 6*2=12 triangles, and 12*3 vertices
static const GLfloat vertex_buffer_data[] = {-1.0f,-1.0f,-1.0f, // triangle 1 : begin-1.0f,-1.0f, 1.0f,-1.0f, 1.0f, 1.0f, // triangle 1 : end1.0f, 1.0f,-1.0f, // triangle 2 : begin-1.0f,-1.0f,-1.0f,-1.0f, 1.0f,-1.0f, // triangle 2 : end1.0f,-1.0f, 1.0f,-1.0f,-1.0f,-1.0f,1.0f,-1.0f,-1.0f,1.0f, 1.0f,-1.0f,1.0f,-1.0f,-1.0f,-1.0f,-1.0f,-1.0f,-1.0f,-1.0f,-1.0f,-1.0f, 1.0f, 1.0f,-1.0f, 1.0f,-1.0f,1.0f,-1.0f, 1.0f,-1.0f,-1.0f, 1.0f,-1.0f,-1.0f,-1.0f,-1.0f, 1.0f, 1.0f,-1.0f,-1.0f, 1.0f,1.0f,-1.0f, 1.0f,1.0f, 1.0f, 1.0f,1.0f,-1.0f,-1.0f,1.0f, 1.0f,-1.0f,1.0f,-1.0f,-1.0f,1.0f, 1.0f, 1.0f,1.0f,-1.0f, 1.0f,1.0f, 1.0f, 1.0f,1.0f, 1.0f,-1.0f,-1.0f, 1.0f,-1.0f,1.0f, 1.0f, 1.0f,-1.0f, 1.0f,-1.0f,-1.0f, 1.0f, 1.0f,1.0f, 1.0f, 1.0f,-1.0f, 1.0f, 1.0f,1.0f,-1.0f, 1.0f
};// One color for each vertex. They were generated randomly.
static const GLfloat color_buffer_data[] = {0.583f,  0.771f,  0.014f,0.609f,  0.115f,  0.436f,0.327f,  0.483f,  0.844f,0.822f,  0.569f,  0.201f,0.435f,  0.602f,  0.223f,0.310f,  0.747f,  0.185f,0.597f,  0.770f,  0.761f,0.559f,  0.436f,  0.730f,0.359f,  0.583f,  0.152f,0.483f,  0.596f,  0.789f,0.559f,  0.861f,  0.639f,0.195f,  0.548f,  0.859f,0.014f,  0.184f,  0.576f,0.771f,  0.328f,  0.970f,0.406f,  0.615f,  0.116f,0.676f,  0.977f,  0.133f,0.971f,  0.572f,  0.833f,0.140f,  0.616f,  0.489f,0.997f,  0.513f,  0.064f,0.945f,  0.719f,  0.592f,0.543f,  0.021f,  0.978f,0.279f,  0.317f,  0.505f,0.167f,  0.620f,  0.077f,0.347f,  0.857f,  0.137f,0.055f,  0.953f,  0.042f,0.714f,  0.505f,  0.345f,0.783f,  0.290f,  0.734f,0.722f,  0.645f,  0.174f,0.302f,  0.455f,  0.848f,0.225f,  0.587f,  0.040f,0.517f,  0.713f,  0.338f,0.053f,  0.959f,  0.120f,0.393f,  0.621f,  0.362f,0.673f,  0.211f,  0.457f,0.820f,  0.883f,  0.371f,0.982f,  0.099f,  0.879f
};MyGLWidget::MyGLWidget(QWidget *parent): QOpenGLWidget(parent)
{
#if 0timer.setInterval(1);connect(&timer,&QTimer::timeout,this,[this](){static float count = 0;count+= 0.01f;global_i = std::fabs(sin(count));this->update();timer.setInterval(1);timer.start();});#endif#if 0timer.setInterval(1);connect(&timer,&QTimer::timeout,this,[this](){global_i+= 1;if(fabs(global_i - 100) < 0.1f){global_i = 0;}this->update();timer.setInterval(1);timer.start();});timer.start();
#endif}MyGLWidget::~MyGLWidget()  {makeCurrent();glDeleteVertexArrays(1,&vertex_array_id);glDeleteBuffers(1,&vertex_buffer_id);doneCurrent();
}void MyGLWidget::initializeGL() {initializeOpenGLFunctions();glClearColor(0.2f,0.3f,0.3f,1.0f);//启动深度测试glEnable(GL_DEPTH_TEST);// Accept fragment if it closer to the camera than the former oneglDepthFunc(GL_LESS);glGenVertexArrays(1,&vertex_array_id);glBindVertexArray(vertex_array_id);glGenBuffers(1,&vertex_buffer_id);glBindBuffer(GL_ARRAY_BUFFER,vertex_buffer_id);glBufferData(GL_ARRAY_BUFFER,sizeof(vertex_buffer_data),vertex_buffer_data,GL_STATIC_DRAW);//描述顶点数组信息//描述顶点数组信息glVertexAttribPointer(0,3,GL_FLOAT,GL_FALSE,0,nullptr);//启用layout = 0为当前绑定的VAOglEnableVertexAttribArray(0);//================颜色信息================//生成颜色的VBO idglGenBuffers(1,&color_buffer_id);//绑定颜色VBOglBindBuffer(GL_ARRAY_BUFFER,color_buffer_id);//加入颜色数组到VBOglBufferData(GL_ARRAY_BUFFER,sizeof(color_buffer_data),color_buffer_data,GL_STATIC_DRAW);//描述颜色数组信息glVertexAttribPointer(1,3,GL_FLOAT,GL_FALSE,0,nullptr);//启动layout = 1绑定当前的VAOglEnableVertexAttribArray(1);//加载着色器loadShader("/Users/liuhang/CLionProjects/opengl-learning/opengl1-4-colored-cube/shader/shader.vert","/Users/liuhang/CLionProjects/opengl-learning/opengl1-4-colored-cube/shader/shader.frag");//解绑VAOglBindVertexArray(0);//解绑VBOglBindBuffer(GL_ARRAY_BUFFER, 0);
}void MyGLWidget::paintGL() {//清除颜色缓存和深度缓存glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);glBindVertexArray(vertex_array_id);//使用着色器程序glUseProgram(shader_program_id);doMVP();glDrawArrays(GL_TRIANGLES,0,3 * 12);
}void MyGLWidget::resizeGL(int w, int h) {glViewport(0, 0, w, h);
}void MyGLWidget::loadShader(std::string const& vertex_shader_path,std::string const& fragment_shader_path) {//创建顶点着色器vertex_shader_id = glCreateShader(GL_VERTEX_SHADER);//读取shader.vertstd::string vertex_shader_code;std::ifstream vertex_shader_stream(vertex_shader_path,std::ios::in);if(vertex_shader_stream.is_open()){std::stringstream ss;ss << vertex_shader_stream.rdbuf();vertex_shader_code = ss.str();vertex_shader_stream.close();}else{std::cout << "current path = " << std::filesystem::current_path() << std::endl;if (!std::filesystem::exists(vertex_shader_path)) {std::cerr << "File not found: " << vertex_shader_path << std::endl;}std::cerr << "read vertex_shader fail!" << std::endl;return;}//加载vertex着色器程序代码const char* vertex_shader_code_pointer = vertex_shader_code.c_str();glShaderSource(vertex_shader_id,1,&vertex_shader_code_pointer,nullptr);//编译vertex shaderglCompileShader(vertex_shader_id);//查看vertex编译结果GLint Result = GL_FALSE;int infoLogLength;glGetShaderiv(vertex_shader_id,GL_COMPILE_STATUS,&Result);glGetShaderiv(vertex_shader_id,GL_INFO_LOG_LENGTH,&infoLogLength);if(infoLogLength > 0){std::vector<char> vertex_shader_error_message(infoLogLength+1);glGetShaderInfoLog(vertex_shader_id, infoLogLength, nullptr, &vertex_shader_error_message[0]);std::cout << "vertex shader:" << &vertex_shader_error_message[0] << std::endl;}//创建片段着色器fragment_shader_id = glCreateShader(GL_FRAGMENT_SHADER);//读取shader.fragstd::string fragment_shader_code;std::ifstream fragment_shader_stream(fragment_shader_path,std::ios::in);if(fragment_shader_stream.is_open()){std::stringstream ss;ss << fragment_shader_stream.rdbuf();fragment_shader_code = ss.str();fragment_shader_stream.close();}else{std::cout << "current path = " << std::filesystem::current_path() << std::endl;if (!std::filesystem::exists(vertex_shader_path)) {std::cerr << "File not found: " << vertex_shader_path << std::endl;}std::cerr << "read fragment_shader fail!" << std::endl;}//加载fragment着色器程序代码const char* fragment_shader_code_pointer = fragment_shader_code.c_str();glShaderSource(fragment_shader_id,1,&fragment_shader_code_pointer,nullptr);//编译fragment shaderglCompileShader(fragment_shader_id);//查看fragment编译结果glGetShaderiv(fragment_shader_id,GL_COMPILE_STATUS,&Result);glGetShaderiv(fragment_shader_id,GL_INFO_LOG_LENGTH,&infoLogLength);if(infoLogLength > 0){std::vector<char> fragment_shader_error_message(infoLogLength+1);glGetShaderInfoLog(fragment_shader_id, infoLogLength, nullptr, &fragment_shader_error_message[0]);std::cout << "fragment shader compile error:" << &fragment_shader_error_message[0] << std::endl;}//创建着色器程序shader_program_id = glCreateProgram();//附加着色器到程序glAttachShader(shader_program_id,vertex_shader_id);glAttachShader(shader_program_id,fragment_shader_id);//链接shaderglLinkProgram(shader_program_id);//检查程序链接结果glGetProgramiv(shader_program_id,GL_LINK_STATUS,&Result);glGetProgramiv(shader_program_id,GL_INFO_LOG_LENGTH,&infoLogLength);if(infoLogLength > 0){std::vector<char>program_error_message(infoLogLength + 1);glGetProgramInfoLog(shader_program_id,infoLogLength,nullptr,&program_error_message[0]);std::cout << "program link:" << &program_error_message[0] << std::endl;}//删除着色器编译结果glDeleteShader(vertex_shader_id);glDeleteShader(fragment_shader_id);
}void MyGLWidget::doMVP()
{//模型矩阵QMatrix4x4 model_matrix;model_matrix.setToIdentity();//观察矩阵QVector3D camera_pos = {4,4,-3};QVector3D look_dir = {1,0,0};QVector3D up_dir = {0,1,0};//lookAtQMatrix4x4 view_matrix;view_matrix.lookAt(camera_pos,look_dir,up_dir);//投影矩阵float angle = 45;float aspect = this->width() * 1.0f / this->height();//距离相机的位置,渲染范围:0.1-100float near_plane = 0.1f;float far_plane = 100.f;QMatrix4x4 projection_matrix;projection_matrix.perspective(angle,aspect,near_plane,far_plane);//获取uniform变量uniform_model_matrix_location = glGetUniformLocation(shader_program_id,"model_matrix");uniform_view_matrix_location = glGetUniformLocation(shader_program_id,"view_matrix");uniform_projection_matrix_location = glGetUniformLocation(shader_program_id,"projection_matrix");//矩阵传入shaderglUniformMatrix4fv(uniform_model_matrix_location,1,GL_FALSE,model_matrix.data());glUniformMatrix4fv(uniform_view_matrix_location,1,GL_FALSE,view_matrix.data());glUniformMatrix4fv(uniform_projection_matrix_location,1,GL_FALSE,projection_matrix.data());}

shader.vert

#version 330 core  layout(location = 0) in vec3 vertex_posion_modelspace;  
layout(location = 1) in vec3 vertex_color;  
out vec3 fragment_color;  uniform mat4 model_matrix;  
uniform mat4 view_matrix;  
uniform mat4 projection_matrix;  void main(){  mat4 MVP_matrix = projection_matrix * view_matrix * model_matrix;  fragment_color = vertex_color;  gl_Position = MVP_matrix * vec4(vertex_posion_modelspace,1.0);  
}

shader.frag

#version 330 core  in vec3 fragment_color;  
out vec3 color;  void main(){  color = fragment_color;  
}
http://www.dtcms.com/a/393232.html

相关文章:

  • 21.继承与混入
  • Python 开发!ImprovePdf 用算法提升PDF清晰度,免费开源工具
  • P1879 [USACO06NOV] Corn Fields G-提高+/省选-
  • 二分答案:跳石头
  • 注解学习,简单测试框架
  • python 自定义装饰器 + 框架
  • 【星闪】Hi2821 | KEYSCAN矩阵按键扫描
  • ​​[硬件电路-282]:PWM信号通用解读、应用场景及在步进电机中的应用详解
  • 给链表装上“后视镜”:深入理解双向链表的自由与高效
  • Off-Grid Direction of Arrival Estimation Using Sparse Bayesian Inference (II)
  • Unity中的渲染管线
  • PyMuPDF 库
  • 【故障】windows7开机后能出现windows启动界面,但停在黑屏很久才进入系统界面
  • tqdm 库
  • 模块化编程规范与分层设计指南
  • Photoshop - Photoshop 调整照片的颜色强度
  • 【ROS2】通讯机制 Topic 常用命令行
  • DeepSeek的GPU优化秘籍:解锁大规模AI训练的底层效率
  • Gemini in Chrome深度解析:反垄断胜诉后,Chrome开启AI智能浏览时代!
  • 如何修复 Google Chrome 上的白屏问题
  • Camera2原生api级 Demo答
  • 【Redis】Redis缓存与数据库DB数据如何保持同步?
  • 考研408---C语言复习
  • 批量抓取图片
  • WinDivert学习文档之五-————编程API(十一)
  • 【打印菱形】
  • XC7Z100-2FFG900I Xilinx AMD Zynq-7000 FPGA SoC
  • 成本价的SEO优化服务供应链
  • dock生命周期体验-生到死的命令
  • 软件测试方案-模板一