在Ubuntu上使用OpenGL实现一个简单的着色器示例可以按照以下步骤进行。这个示例将展示如何创建一个窗口并渲染一个带有顶点和片段着色器的简单三角形。
环境准备
- 安装必要的库
首先,你需要确保已安装OpenGL开发库以及GLFW(用于创建窗口)和GLEW(用于加载OpenGL扩展)。你可以使用以下命令进行安装:
sudo apt update
sudo apt install build-essential libglfw3-dev libglew-dev libglm-dev
- 创建项目文件结构
创建一个新的目录,并在其中创建源文件:
mkdir OpenGLShaderExample
cd OpenGLShaderExample
touch main.cpp shader.h shader.cpp
编写代码
shader.h
- 定义着色器类
#ifndef SHADER_H
#define SHADER_H
#include <glad/glad.h>
#include <string>
class Shader {
public:
unsigned int ID;
Shader(const char* vertexPath, const char* fragmentPath);
void use();
};
#endif // SHADER_H
shader.cpp
- 实现着色器类
#include "shader.h"
#include <iostream>
#include <fstream>
#include <sstream>
std::string readFile(const char* filePath) {
std::ifstream file;
std::stringstream buffer;
file.open(filePath);
if (!file.is_open()) {
std::cerr << "Unable to open file: " << filePath << std::endl;
return "";
}
buffer << file.rdbuf();
return buffer.str();
}
Shader::Shader(const char* vertexPath, const char* fragmentPath) {
// 1. 获取顶点/片段 shader 源码
std::string vertexCode = readFile(vertexPath);
std::string fragmentCode = readFile(fragmentPath);
const char* vShaderCode = vertexCode.c_str();
const char* fShaderCode = fragmentCode.c_str();
// 2. 编译顶点 shader
unsigned int vertex = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertex, 1, &vShaderCode, NULL);
glCompileShader(vertex);
// 3. 检查编译错误
int success;
char infoLog[512];
glGetShaderiv(vertex, GL_COMPILE_STATUS, &success);
if (!success) {
glGetShaderInfoLog(vertex, 512, NULL, infoLog);
std::cerr << "ERROR: Vertex Shader compilation failed\n" << infoLog << std::endl;
return;
}
// 4. 编译片段 shader
unsigned int fragment = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragment, 1, &fShaderCode, NULL);
glCompileShader(fragment);
// 检查编译错误
glGetShaderiv(fragment, GL_COMPILE_STATUS, &success);
if (!success) {
glGetShaderInfoLog(fragment, 512, NULL, infoLog);
std::cerr << "ERROR: Fragment Shader compilation failed\n" << infoLog << std::endl;
return;
}
// 5. 着色器程序链接
ID = glCreateProgram();
glAttachShader(ID, vertex);
glAttachShader(ID, fragment);
glLinkProgram(ID);
// 删除着色器,因为它们已经被链接到程序中,之后不再需要了
glDeleteShader(vertex);
glDeleteShader(fragment);
}
void Shader::use() {
glUseProgram(ID);
}
main.cpp
- 主程序
#include <glad/glad.h>
#include <GLFW/glfw3.h>
#include <iostream>
#include "shader.h"
void framebuffer_size_callback(GLFWwindow* window, int width, int height) {
glViewport(0, 0, width, height);
}
int main() {
glfwInit();
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR , 3 );
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR , 3 );
glfwWindowHint(GLFW_OPENGL_PROFILE , GLFW_OPENGL_CORE_PROFILE );
GLFWwindow *window = glfwCreateWindow(800 ,600 , "OpenGL Example" , nullptr , nullptr );
if (window == nullptr ) {
std :: cout <<"Failed to create GLFW window" <<std :: endl ;
glfwTerminate();
return -1 ;
}
glfwMakeContextCurrent(window);
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) {
std :: cout <<"Failed to initialize GLAD" <<std :: endl ;
return -1 ;
}
float vertices[] = {
-0.5f , -0.5f ,0.0f ,
0.5f ,-0.5f ,0.0f ,
0.f , 0.5f ,0.f
};
unsigned int VBO , VAO;
glGenVertexArrays(1 ,&VAO);
glGenBuffers(1,&VBO);
// Bind the Vertex Array Object first,
// then bind and set vertex buffer(s)
// and attribute pointers.
glBindVertexArray(VAO);
glBindBuffer(GL_ARRAY_BUFFER,VBO);
glBufferData(GL_ARRAY_BUFFER,sizeof(vertices),vertices , GL_STATIC_DRAW);
// Position attribute
glVertexAttribPointer(0 ,3 , GL_FLOAT , GL_FALSE ,
sizeof(float) *3,
(void*)nullptr );
/** Activate the attribute */
/* Normally you'd just set this as a parameter on the function above,
but we'll do it here for simplicity */
/* You can also specify different data types like UNSIGNED_INT or DOUBLE*/
/* Set as enabled*/
/// In modern OpenGL this is done automatically via the call to `VertexAttribPointer`
/* Just specifying it here for demonstration purposes */
/** Note that layout(location=…) in your shaders will automatically enable these attributes so you don’t have to call this separately.
https://learnopengl.com/Getting-started/Shaders#vertex-attributes */
/** tell OpenGL how to interpret the VBO's data and link it with the currently active VAO */
/**
Each call may take parameters for:
where we want our data located on our array buffers,
number of components stored per value (in our case x,y,z),
whether we want them normalized between [0...1] or not?
spacing between consecutive values in memory,
and at which location in memory we start reading from (offset).
**/
/** Setup the shaders */
Shader my_shader("vertex_shader.glsl", "fragment_shader.glsl");
while (!glfwWindowShouldClose(window)) {
processInput(window);
my_shader.use();
/* Draw triangles */
/* Same reason why we've called our Buffer Data before - need correct state activated */
/*
We now bind our VAO into context so that the following calls use its settings.
*/
/** Clear the colorbuffer*/
/// This sets a clear color for our window when drawing frames.
/// We're going to put back black by default since it's easier for us as beginners
/** Render here */
/*
Here we invoke `draw elements' with usage pattern -> Tell opengl what kind of drawing should be invoked.
Pass `count' which denotes how many vertices were drawn.
Replace `count' according to how many arrays we're drawing from our Buffer Arrays.
The parameter type can also differ according to your requirement e.g EBO (elements buffer objects)
*/
/** Make sure we use appropriate states etc before calling draw functions */
/**** Call render function here */
glm_mat4x4_set_uniform(my_shader.ID,"model_matrix_name_here", mat4_identity());
/***** View transformations etc can go here ********/
/****** Rendering Done Now **********************************************/
glfwSwapBuffers(window);
glfwPollEvents();
}
/** Cleanup resources once rendering has completed all tasks through iteration loop **/
/// All being removed once done until next rendering process initiated
gluDeleteSubWindows(VAO,VBO,NULL));
GLuint generatedShadersIdArray[10];
shader_cleanup(generatedShadersIdArray,count_needed_to_clear_shaders_types_within_scope_of_loop_outside_main_setup_code_area)
;
}
/** Shutting down the entire program post cleanup phase occurred after iterative loop comes through proper exits **/
gladDestroyLoaderFunc();
exit(EXIT_SUCCESS);
}
顶点和片段着色器文件
创建两个新文件来存放顶点和片段着色器代码。
vertex_shader.glsl
#version 330 core
layout(location = 0) in vec3 position;
void main() {
gl_Position = vec4(position.x, position.y, position.z, 1.0);
}
fragment_shader.glsl
#version 330 core
out vec4 color;
void main() {
color = vec4(1.0f, 0.5f, 0.2f, 1.0f); // Orange color output
}
编译与运行
现在,可以使用下面的命令来编译并运行你的程序:
g++ main.cpp shader.cpp -o OpenGLExample -lGLEW -lGLFW -ldl -lX11 && ./OpenGLExample
如果一切顺利,你应该会看到一个窗口,里面显示了一个橙色的三角形。
总结
这个示例展示了如何在Ubuntu上设置OpenGL环境、编写基础的顶点和片段着色器,并通过简单的应用程序呈现结果。你可以根据自己的需求扩展这个基础框架,实现更复杂的图形效果。
内容由零声教学AI助手提供,问题来源于学员提问