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

【OpenGL/C++】面向对象扩展——测试环境

文章目录

    • RunBase类展示:
    • RunBase继承类展示:
    • Main函数内容(使用示例):

在使用各种类对OpenGL的流程和API进行抽象和封装后,为了测试方便,还可以写一些类来对其进行进一步的封装。我写这个封装是为了方便测试使用。

封装的功能:

  • 固定渲染流程,无需重复书写
  • 外部调用Run来执行渲染代码
  • 继承类重写Init和RenderLoop函数来执行渲染初始化和渲染循环中的操作
  • 继承类重写UseCameraControl函数并修改返回值来确定是否开启摄像机操作

注意事项:

  • 考虑实现的简单性,摄像机操作功能写在了类里并使用静态单例来注册到glfw上,并没有分离出去,我个人在使用时直接在main函数中替换具体类名来切换不同的测试示例。
  • 类并不负责创建窗口等最开始的操作,下述有完整的代码展示。

RunBase类展示:

#pragma once
#include <GL/glew.h>
#include <GLFW/glfw3.h>

#include "Shader.h"
#include "Camera.h"

#include <iostream>

#include <glm/glm.hpp>

using std::cout;
using namespace glm;

class RunBase
{
public:
    //单例
    static RunBase* Instance;
    
    // Camera attributes
    Camera camera;
    bool keys[1024];
    GLfloat lastX = 400, lastY = 300;
    bool firstMouse = true;
    float deltaTime;

    // Light attributes
    glm::vec3 lightPos;

    //构造函数初始化参数
    RunBase()
    {
        camera.Position = { 0, 0, 3 };
        lightPos = { 1.2f, 1.0f, 2.0f };

        Instance = this;
    }

    //渲染流程,外部调用
    virtual void Run(GLFWwindow* window, float targetFrameTime)
    {
        deltaTime = targetFrameTime;

        //开启深度测试
        glEnable(GL_DEPTH_TEST);

        
        if (UseCameraControl())
        {
            //隐藏指针
            glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);

            //摄像机按键绑定
            glfwSetKeyCallback(window, key_callback);
            glfwSetCursorPosCallback(window, mouse_callback);
            glfwSetScrollCallback(window, scroll_callback);
        }

        Init(window, targetFrameTime);

        //在渲染循环中绘制指定的顶点数组对象,索引缓存,着色器
        while (!glfwWindowShouldClose(window))
        {
            CheckFrameTimeOut(targetFrameTime);

            RenderLoop(window, targetFrameTime);

            glfwSwapBuffers(window);

            if (UseCameraControl())
                DoMovement();
            glfwPollEvents();
        }
    }

    //渲染前的准备,类可重写
    virtual void Init(GLFWwindow* window, float targetFrameTime)
    {

    }

    //渲染中操作,类可重写
    virtual void RenderLoop(GLFWwindow* window, float targetFrameTime)
    {

    }

    //是否开启相机控制
    virtual bool UseCameraControl()
    {
        return true;
    }

    //移动函数
    virtual void DoMovement()
    {
        if (keys[GLFW_KEY_W])
            camera.ProcessKeyboard(FORWARD, deltaTime);
        if (keys[GLFW_KEY_S])
            camera.ProcessKeyboard(BACKWARD, deltaTime);
        if (keys[GLFW_KEY_A])
            camera.ProcessKeyboard(LEFT, deltaTime);
        if (keys[GLFW_KEY_D])
            camera.ProcessKeyboard(RIGHT, deltaTime);
    }

    void CheckFrameTimeOut(float targetFrameTime)
    {
        // 获取当前时间
        double currentTime = glfwGetTime();

        // 计算帧间隔
        static double lastFrameTime = currentTime;
        double frameDelta = currentTime - lastFrameTime;

        // 计算帧时间
        double frameTime = targetFrameTime - frameDelta;

        // 等待剩余的帧时间
        if (frameTime > 0.0)
        {
            glfwWaitEventsTimeout(frameTime);
        }
    }

    static void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode)
    {
        if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS)
            glfwSetWindowShouldClose(window, GL_TRUE);

        if (key >= 0 && key < 1024)
        {
            if (action == GLFW_PRESS)
                Instance->keys[key] = true;
            else if (action == GLFW_RELEASE)
                Instance->keys[key] = false;
        }
    }

    static void mouse_callback(GLFWwindow* window, double xpos, double ypos)
    {
        if (Instance->firstMouse)
        {
            Instance->lastX = xpos;
            Instance->lastY = ypos;
            Instance->firstMouse = false;
        }

        GLfloat xoffset = xpos - Instance->lastX;
        GLfloat yoffset = Instance->lastY - ypos;  // Reversed since y-coordinates go from bottom to left

        Instance->lastX = xpos;
        Instance->lastY = ypos;

        Instance->camera.ProcessMouseMovement(xoffset, yoffset);
    }

    static void scroll_callback(GLFWwindow* window, double xoffset, double yoffset)
    {
        Instance->camera.ProcessMouseScroll(yoffset);
    }
};

RunBase* RunBase::Instance = nullptr;

RunBase继承类展示:

LearnOpenGL的教程示例,十个箱子在固定位置旋转,并有各自的贴图,在制作了RunBase类后,使用时只需要专注于教程和测试的图形学部分。无需重复复制和束缚在其他C++代码上。

#pragma once
#include <GL/glew.h>
#include <GLFW/glfw3.h>

#include "Renderer.h"
#include "VertexBuffer.h"
#include "VertexArray.h"
#include "VertexBufferLayout.h"
#include "Texture.h"
#include "Shader.h"
#include "Camera.h"

#include <iostream>
#include "RunBase.hpp"

#include <glm/gtc/type_ptr.hpp>

using std::cout;
using namespace glm;

class MyTest : public RunBase
{
public:
    VertexArray vao;
    VertexBuffer vbo;
    VertexBufferLayout layout;

    Shader shader;

    Renderer renderer;

    Texture containerTexture;

    glm::vec3* cubePositions;

    MyTest() : RunBase()
    {
        shader.Init("res/shaders/Learn2.shader");
    }

    virtual void Init(GLFWwindow* window, float targetFrameTime) override
    {
        GLfloat vertices[] = {
            -0.5f, -0.5f, -0.5f,  0.0f, 0.0f,
             0.5f, -0.5f, -0.5f,  1.0f, 0.0f,
             0.5f,  0.5f, -0.5f,  1.0f, 1.0f,
             0.5f,  0.5f, -0.5f,  1.0f, 1.0f,
            -0.5f,  0.5f, -0.5f,  0.0f, 1.0f,
            -0.5f, -0.5f, -0.5f,  0.0f, 0.0f,

            -0.5f, -0.5f,  0.5f,  0.0f, 0.0f,
             0.5f, -0.5f,  0.5f,  1.0f, 0.0f,
             0.5f,  0.5f,  0.5f,  1.0f, 1.0f,
             0.5f,  0.5f,  0.5f,  1.0f, 1.0f,
            -0.5f,  0.5f,  0.5f,  0.0f, 1.0f,
            -0.5f, -0.5f,  0.5f,  0.0f, 0.0f,

            -0.5f,  0.5f,  0.5f,  1.0f, 0.0f,
            -0.5f,  0.5f, -0.5f,  1.0f, 1.0f,
            -0.5f, -0.5f, -0.5f,  0.0f, 1.0f,
            -0.5f, -0.5f, -0.5f,  0.0f, 1.0f,
            -0.5f, -0.5f,  0.5f,  0.0f, 0.0f,
            -0.5f,  0.5f,  0.5f,  1.0f, 0.0f,

             0.5f,  0.5f,  0.5f,  1.0f, 0.0f,
             0.5f,  0.5f, -0.5f,  1.0f, 1.0f,
             0.5f, -0.5f, -0.5f,  0.0f, 1.0f,
             0.5f, -0.5f, -0.5f,  0.0f, 1.0f,
             0.5f, -0.5f,  0.5f,  0.0f, 0.0f,
             0.5f,  0.5f,  0.5f,  1.0f, 0.0f,

            -0.5f, -0.5f, -0.5f,  0.0f, 1.0f,
             0.5f, -0.5f, -0.5f,  1.0f, 1.0f,
             0.5f, -0.5f,  0.5f,  1.0f, 0.0f,
             0.5f, -0.5f,  0.5f,  1.0f, 0.0f,
            -0.5f, -0.5f,  0.5f,  0.0f, 0.0f,
            -0.5f, -0.5f, -0.5f,  0.0f, 1.0f,

            -0.5f,  0.5f, -0.5f,  0.0f, 1.0f,
             0.5f,  0.5f, -0.5f,  1.0f, 1.0f,
             0.5f,  0.5f,  0.5f,  1.0f, 0.0f,
             0.5f,  0.5f,  0.5f,  1.0f, 0.0f,
            -0.5f,  0.5f,  0.5f,  0.0f, 0.0f,
            -0.5f,  0.5f, -0.5f,  0.0f, 1.0f
        };

        cubePositions = new vec3[]{
            glm::vec3(0.0f,  0.0f,  0.0f),
            glm::vec3(2.0f,  5.0f, -15.0f),
            glm::vec3(-1.5f, -2.2f, -2.5f),
            glm::vec3(-3.8f, -2.0f, -12.3f),
            glm::vec3(2.4f, -0.4f, -3.5f),
            glm::vec3(-1.7f,  3.0f, -7.5f),
            glm::vec3(1.3f, -2.0f, -2.5f),
            glm::vec3(1.5f,  2.0f, -2.5f),
            glm::vec3(1.5f,  0.2f, -1.5f),
            glm::vec3(-1.3f,  1.0f, -1.5f)
        };
		vbo.Init(vertices, sizeof(vertices));
        
        //创建顶点缓存布局,并链接到顶点数组上
        layout.Push<float>(3);
        layout.Push<float>(2);

        //缓存和布局绑定到vao上
        vao.AddBuffer(vbo, layout);

        //设置纹理
        containerTexture.Init("res/textures/container2.png");
        containerTexture.Bind();
        shader.Bind();
        shader.SetUniform1i("u_Texture", 0);
    }

    virtual void RenderLoop(GLFWwindow* window, float targetFrameTime) override
    {
        renderer.Clear();

        glm::mat4 view(1), proj(1);

        //视图矩阵
        view = camera.GetViewMatrix();
        shader.SetUniformMat4f("view", glm::value_ptr(view));
		//投影矩阵
        proj = glm::perspective<GLfloat>(camera.Zoom, 640 / 480, .1f, 100.f);
        shader.SetUniformMat4f("projection", glm::value_ptr(proj));

        //模型矩阵(模型位置)
        for (GLuint i = 0; i < 10; i++)
        {
            glm::mat4 model(1);
            model = glm::translate(model, cubePositions[i]);
            GLfloat angle = 20.0f * i;
            model = glm::rotate(model, angle + ((GLfloat)glfwGetTime() * (i % 2 ? 1 : -1)), glm::vec3(1.0f, 0.3f, 0.5f));
            shader.SetUniformMat4f("model", glm::value_ptr(model));

            renderer.Draw(vao, 36, shader);
        }
    }

    virtual bool UseCameraControl() override
    {
        return true;
    }
};

Main函数内容(使用示例):

在制作该类前,我一直直接通过使用全局的函数名来调用不同的渲染代码,后来在使用glfw的按键绑定相关代码时出现了重定义的错误,后来想直接把这些东西放在类里,最后干脆再封装一下渲染流程,最后就成了这个RunBase类。

#define GLEW_STATIC

#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include <iostream>

#include "RunBase.hpp"
#include "Test1.hpp"
#include "learn1.hpp"
#include "learn2.hpp"
#include "light_learn_1.hpp"
#include "MyTest.hpp"

using std::cout;
using std::endl;

#define TargetFrameRate 60


int main(void)
{
	/* Initialize the library */
	if (!glfwInit())
		return -1;


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


	/* Create a windowed mode window and its OpenGL context */
	GLFWwindow* window = glfwCreateWindow(640, 480, "Hello World", NULL, NULL);
	if (!window)
	{
		glfwTerminate();
		return -1;
	}

	/* Make the window's context current */
	glfwMakeContextCurrent(window);

	//这样做能让GLEW在管理OpenGL的函数指针时更多地使用现代化的技术
	glewExperimental = GL_TRUE;

	//初始化GLEW(要在初始化了OpenGL上下文之后)
	if (glewInit() != GLEW_OK)
		return -1;

	//目标帧时间
	const double targetFrameTime = 1.0 / TargetFrameRate;

	//课程代码
	//Test1(window);
	//Learn1(window, targetFrameTime);
	//Learn2(window, targetFrameTime);

	RunBase* runable = new MyTest();
	runable->Run(window, targetFrameTime);

	glfwTerminate();
	return 0;
}

http://www.kler.cn/a/502139.html

相关文章:

  • 顶层const与底层const
  • Sui Move:基本概览一
  • 17_Redis管道技术
  • js:正则表达式
  • 41_Lua函数
  • Vue3初学之组件通信
  • FlashAttention的原理及其优势
  • HTTP/HTTPS ④-对称加密 || 非对称加密
  • 使用WeakHashMap实现缓存自动清理
  • 特制一个自己的UI库,只用CSS、图标、emoji图 第二版
  • MySQL Binlog 同步工具go-mysql-transfer Lua模块使用说明
  • Django创建数据表、模型、ORM操作
  • 饿汉式单例与懒汉式单例模式
  • 前端学习-事件对象与典型案例(二十六)
  • 25/1/13 算法笔记<嵌入式> 继续学习Esp32
  • uiautomator2 实现找图点击
  • 记一次学习skynet中的C/Lua接口编程解析protobuf过程
  • FreeSWITCH Sofia SIP 模块常用命令整理
  • 如何设计一个 RPC 框架?需要考虑哪些点?
  • 计算机网络 笔记 网络层1
  • 远程和本地文件的互相同步
  • 深度学习——pytorch基础入门
  • GPT 系列论文精读:从 GPT-1 到 GPT-4
  • 机器翻译优缺点
  • 2025第3周 | JavaScript中es7新增的特性
  • Kafka 超级简述