#ifndef DEMO_H_
#define DEMO_H_
#include <glad/glad.h>
class Demo
{
public:
Demo();
virtual ~Demo(); // 必须是虚析构函数!否则 delete 子类会出问题
// 纯虚函数:必须由子类实现
virtual void init() { }
virtual void update(float dt) { }
virtual void render() { }
virtual void destroy() { }
virtual void setRenderParameter(bool enableCullFace = false,
GLenum cullMode = GL_BACK,
GLenum frontFace = GL_CCW);
virtual void beginRender();
// // 可选公共方法
// void beginRender();
//private:
// bool enableCullFace; //是否启用面剔除功能(即不渲染背对摄像机的面)
// GLenum cullMode; //指定要剔除的面类型:GL_BACK(背面)、GL_FRONT(前面)或 GL_FRONT_AND_BACK(双面)
// GLenum frontFace; //定义三角形的“正面”方向:GL_CCW(逆时针为正,默认),GL_CW(顺时针为正)
protected:
bool enableCullFace;
GLenum cullMode;
GLenum frontFace;
const char* vertexShaderSource; //顶点着色器的 GLSL 源代码字符串
const char* fragmentShaderSource; //默认片段着色器的 GLSL 源代码字符串
const char* fragmentShader1Source; //可选的第二种片段着色器源码(例如不同光照模型)
const char* fragmentShader2Source; //可选的第三种片段着色器源码(例如边缘检测、颜色变换等)
//public:
// virtual void setRenderParameter(bool enableCullFace = false, GLenum cullMode = GL_FRONT, GLenum frontFace = GL_CCW); //补注释
// virtual void beginRender(); // 开始渲染前调用,用于应用当前设置的渲染状态(如启用/禁用面剔除)
// virtual void render() = 0; // 纯虚函数:子类必须实现具体的渲染逻辑(如绘制几何体、使用着色器等)
};
#endif
#include "Rectangle.h"
#include <glad/glad.h>
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
#include <iostream>
Rectangle::Rectangle()
{
x = -1.0f;
y = 0.0f;
shader = nullptr;
VAO = 0;
}
Rectangle::~Rectangle()
{
if (shader) delete shader;
glDeleteVertexArrays(1, &VAO);
}
void Rectangle::initData()
{
// 创建着色器(从文件加载)
shader = new Shader("shaders/vertex.vert", "shaders/fragment.frag");
// 创建并配置 VAO
glGenVertexArrays(1, &VAO);
glBindVertexArray(VAO);
unsigned int VBO, EBO;
glGenBuffers(1, &VBO);
glGenBuffers(1, &EBO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
// 位置属性 (location = 0)
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);
// 解绑
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
std::cout << "Rectangle initialized with shader and VAO." << std::endl;
}
void Rectangle::renderObject()
{
if (!shader) return;
// 使用着色器
shader->use();
// 动画逻辑:正弦轨迹移动 + Z轴自转
x += 0.005f;
if (x > 1.1f) x = -1.1f;
float angle = x / 2.0f * 6.28f; // 转换为弧度
y = sin(angle);
// 构造 model 矩阵
glm::mat4 model = glm::mat4(1.0f);
model = glm::translate(model, glm::vec3(x, y, 0.0f)); // 平移到当前位置
model = glm::rotate(model, angle * 5.0f, glm::vec3(0, 0, 1)); // 自旋(更快一点)
// 获取 uniform 位置并上传
unsigned int modelLoc = glGetUniformLocation(shader->id, "model");
if (modelLoc != -1)
{
glUniformMatrix4fv(modelLoc, 1, GL_FALSE, glm::value_ptr(model));
}
// 绘制
glBindVertexArray(VAO);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
}
#include "Demo3_2.h"
Demo3_2::Demo3_2()
{
//rect.setShaderString(vertexShaderSource, fragmentShader1Source);
//rect.initData();
}
Demo3_2::~Demo3_2()
{
}
void Demo3_2::init()
{
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
rect.initData();
}
void Demo3_2::update(float dt) { }
void Demo3_2::render()
{
glClear(GL_COLOR_BUFFER_BIT);
rect.renderObject();
}
void Demo3_2::destroy() { }
#pragma once
#include "Rectangle.h"
#include "Demo.h"
class Demo3_2:public Demo
{
public:
Demo3_2();
~Demo3_2();
private:
Rectangle rect; //表示一个具体的矩形对象实例
// 注释:该成员变量封装了顶点数据、缓冲区(VBO/VAO)、着色器以及绘制逻辑在 render() 中会被调用来实际绘制矩形
public:
//void render(); //重写基类的纯虚函数,实现本 demo 的具体渲染逻辑
// 注释:在此函数中会调用 beginRender() 设置 OpenGL 状态,然后调用 rect.render() 执行矩形的绘制操作
void init() override;
void update(float dt) override;
void render() override;
void destroy() override;
};
#pragma once
#include <glad/glad.h>
#include <string>
#include <iostream>
#include <fstream>
#include <sstream>
using namespace std;
class Shader
{
public:
unsigned int id; //着色器程序(Program Object)的唯一标识符(OpenGL ID)
Shader() { id = 0; }
Shader(const char* vs, const char* fs)
{
unsigned int vertex, fragment; //临时变量,分别存储顶点着色器和片段着色器的 OpenGL 对象 ID
// 注释:- vertex: 通过 glCreateShader(GL_VERTEX_SHADER) 创建,- fragment: 通过 glCreateShader(GL_FRAGMENT_SHADER) 创建它们是独立编译的着色器单元,在链接前不会生效
vertex = glCreateShader(GL_VERTEX_SHADER); //创建一个顶点着色器对象,该对象将用于加载、编译顶点着色器源代码
fragment = glCreateShader(GL_FRAGMENT_SHADER); //创建一个片段着色器对象,用于处理像素颜色计算(光栅化后)
id = glCreateProgram(); //创建一个着色器程序对象,所有着色器必须附加到此程序才能链接运行链接成功后,可通过 glUseProgram(id) 启用整个管线
glShaderSource(vertex, 1, &vs,nullptr); //将 C++ 字符串 vs(顶点着色器源码)绑定到 vertex 着色器对象
glCompileShader(vertex); //编译顶点着色器,将之前设置的源码编译成 GPU 可执行的形式,若语法错误则编译失败,需调用 checkCompileErrors 检查
checkCompileErrors(vertex, "VERTEX"); //检查顶点着色器是否编译成功如果失败,打印详细的错误日志到控制台
glShaderSource(fragment, 1, &fs, nullptr); //将 fs 字符串作为源码传入片段着色器对象
glCompileShader(fragment); //编译片段着色器
checkCompileErrors(fragment, "FRAGMENT"); //检查片段着色器编译状态
glAttachShader(id, vertex); //将已编译的顶点着色器附加到程序对象 id 上
glAttachShader(id, fragment); //将已编译的片段着色器附加到程序对象 id 上
glLinkProgram(id); //链接着色器程序把所有附加的着色器合并为一个可执行程序检查接口匹配性(如 vs 输出与 fs 输入)、全局符号等成功后方可使用
glDeleteShader(vertex); //链接完成后,删除临时的顶点着色器对象
glDeleteShader(fragment); // 删除临时的片段着色器对象
}
Shader(const std::string& vertexPath, const std::string& fragmentPath)
{
// 读取着色器源码文件
std::string vertexCode = readFile(vertexPath);
std::string fragmentCode = readFile(fragmentPath);
const char* vShaderCode = vertexCode.c_str();
const char* fShaderCode = fragmentCode.c_str();
unsigned int vertex, fragment;
vertex = glCreateShader(GL_VERTEX_SHADER);
fragment = glCreateShader(GL_FRAGMENT_SHADER);
id = glCreateProgram();
glShaderSource(vertex, 1, &vShaderCode, nullptr);
glCompileShader(vertex);
checkCompileErrors(vertex, "VERTEX");
glShaderSource(fragment, 1, &fShaderCode, nullptr);
glCompileShader(fragment);
checkCompileErrors(fragment, "FRAGMENT");
glAttachShader(id, vertex);
glAttachShader(id, fragment);
glLinkProgram(id);
checkCompileErrors(id, "PROGRAM");
glDeleteShader(vertex);
glDeleteShader(fragment);
}
void use()
{
glUseProgram(id); //激活当前着色器程序使 OpenGL 渲染管线使用这个 program 进行绘制后续 draw call 都会使用该着色器逻辑
}
private:
std::string readFile(const std::string& path)
{
std::ifstream file;
std::stringstream buffer;
// 尝试打开文件
file.exceptions(std::ifstream::failbit | std::ifstream::badbit);
try
{
file.open(path);
buffer << file.rdbuf(); // 读取全部内容
file.close();
return buffer.str();
}
catch (const std::exception& e)
{
std::cout << "ERROR::SHADER::FILE_NOT_READ: " << e.what() << std::endl;
return "";
}
}
void checkCompileErrors(GLuint shader, std::string type) //用于检查着色器编译或程序链接是否出错
{
GLint success;
GLchar infoLog[1024];
if (type != "PROGRAM")
{
glGetShaderiv(shader, GL_COMPILE_STATUS, &success);
if (!success)
{
glGetShaderInfoLog(shader, 1024, NULL, infoLog);
std::cout << "ERROR::SHADER_COMPILATION_ERROR of type: " << type << "\n" << infoLog << "\n -- --------------------------------------------------- -- " << std::endl;
}
}
else
{
glGetProgramiv(shader, GL_LINK_STATUS, &success);
if (!success)
{
glGetProgramInfoLog(shader, 1024, NULL, infoLog);
std::cout << "ERROR::PROGRAM_LINKING_ERROR of type: " << type << "\n" << infoLog << "\n -- --------------------------------------------------- -- " << std::endl;
}
}
}
};
运行后没有东西
最新发布