基于OpenGL编写一个简易的2D渲染框架02——搭建OpenGL环境

由于没有使用GLFW库,接下来得费一番功夫。

阅读这篇文章前请看一下这个网页:https://learnopengl-cn.github.io/01%20Getting%20started/02%20Creating%20a%20window/

以下,我摘取了一点片段

Windows上的OpenGL库

  如果你是Windows平台,opengl32.lib已经包含在Microsoft SDK里了,它在Visual Studio安装的时候就默认安装了。由于这篇教程用的是VS编译器,并且是在Windows操作系统上,我们只需将opengl32.lib添加进连接器设置里就行了。

GLEW

到这里,我们仍然有一件事要做。因为OpenGL只是一个标准/规范,具体的实现是由驱动开发商针对特定显卡实现的。由于OpenGL驱动版本众多,它大多数函数的位置都无法在编译时确定下来,需要在运行时查询。任务就落在了开发者身上,开发者需要在运行时获取函数地址并将其保存在一个函数指针中供以后使用。取得地址的方法因平台而异,在Windows上会是类似这样:

// 定义函数原型
typedef void (*GL_GENBUFFERS) (GLsizei, GLuint*);
// 找到正确的函数并赋值给函数指针
GL_GENBUFFERS glGenBuffers  = (GL_GENBUFFERS)wglGetProcAddress("glGenBuffers");
// 现在函数可以被正常调用了
GLuint buffer;
glGenBuffers(1, &buffer);

你可以看到代码非常复杂,而且很繁琐,我们需要对每个可能使用的函数都要重复这个过程。幸运的是,有些库能简化此过程,其中GLEW是目前最新,也是最流行的库。

编译和链接GLEW

  GLEW是OpenGL Extension Wrangler Library的缩写,它能解决我们上面提到的那个繁琐的问题。因为GLEW也是一个库,我们同样需要构建并将其链接进工程。GLEW可以从这里下载,你同样可以选择下载二进制版本,如果你的目标平台列在上面的话,或者下载源码编译,步骤和编译GLFW时差不多。记住,如果不确定的话,选择32位的二进制版本。

  我们使用GLEW的静态版本glew32s.lib(注意这里的“s”),将库文件添加到你的库目录,将include内容添加到你的include目录。接下来,在VS的链接器选项里加上glew32s.lib。注意GLFW3(默认)也是编译成了一个静态库。

  如果你希望静态链接GLEW,必须在包含GLEW头文件之前定义预处理器宏GLEW_STATIC

#define GLEW_STATIC
#include <GL/glew.h>

如果你希望动态链接,那么你可以省略这个宏。但是记住使用动态链接的话你需要拷贝一份.DLL文件到你的应用程序目录。

接下来要做的是链接 opengl32.lib 和 glew32s.lib这 两个库进工程

在Windows中,OpenGL命令是通过OpenGL Render Context(以下简称RC)来执行的。这个RC是OpenGL和Windows之间的纽带。

每个RC创建时,必须指定一个DC(Windows Device Context 也就是那个通常用于GDI的设备环境DC)。RC的绘制目标,就是这个DC所对应的窗口。

每次RC创建时,应该设置一下DC的像素格式。

添加一个新类 GraphicsContext 头文件

#pragma once
#define GLEW_STATIC

#include "Common.h"
#include <Windows.h>
#include "GLEW\glew.h"

namespace Simple2D
{
    class RenderWindow;

    class DLL_export GraphicsContext
    {
    public:
        GraphicsContext(RenderWindow* renderWindow);
        ~GraphicsContext();

        void createOpenGLContext();
        void flip();

    private:
        RenderWindow* pRenderWindow;

        HGLRC openglContext;
        HDC deviceContext;
    };
}

注意代码中的这个宏  #define GLEW_STATIC,没有它你可能编译错误。

它的实现

#include "GraphicsContext.h"
#include "RenderWindow.h"

namespace Simple2D
{
    GraphicsContext::GraphicsContext(RenderWindow* renderWindow)
        : pRenderWindow(renderWindow)
        , openglRenderContext(0)
        , deviceContext(0)
    {
        this->createOpenGLContext();
    }

    GraphicsContext::~GraphicsContext()
    {
        wglDeleteContext(openglRenderContext);
        ReleaseDC(pRenderWindow->getHwnd(), deviceContext);
    }

    void GraphicsContext::createOpenGLContext()
    {
        if ( openglRenderContext == 0 ) {
            deviceContext = GetDC(pRenderWindow->getHwnd());

            PIXELFORMATDESCRIPTOR pfd = { 0 };
            int color_deep = GetDeviceCaps(deviceContext, BITSPIXEL);

            pfd.nSize = sizeof(PIXELFORMATDESCRIPTOR);
            pfd.nVersion = 1;
            pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
            pfd.iPixelType = PFD_TYPE_RGBA;
            pfd.cColorBits = color_deep;
            pfd.cDepthBits = 0;
            pfd.cStencilBits = 0;
            pfd.iLayerType = PFD_MAIN_PLANE;

            int pixle_format = ChoosePixelFormat(deviceContext, &pfd);
            SetPixelFormat(deviceContext, pixle_format, &pfd);

            /* 创建 OpenGL 渲染上下文 */
            openglRenderContext = wglCreateContext(deviceContext);
            if ( openglRenderContext == 0 ) exit(0);

            /* 选择 openglRenderContext 作为当前线程的 openglRenderContext */
            if ( wglMakeCurrent(deviceContext, openglRenderContext) == 0 ) exit(1);

            /* GLEW 是用来管理 OpenGL 的函数指针的,所以在调用任何 OpenGL 的函数之前我们需要初始化GLEW */
            if ( glewInit() != GLEW_OK ) exit(1);

            /* 设置视口,大小为客户区大小 */
            SIZE size = pRenderWindow->getClientSize();
            glViewport(0, 0, size.cx, size.cy);
        }
    }

    void GraphicsContext::flip()
    {
        /* 使用一个自定义的颜色清空屏幕,这里使用红色 */
        glClearColor(1.0f, 0, 0, 1.0f);

        /* 调用glClear函数来清空屏幕的颜色缓冲 */
        glClear(GL_COLOR_BUFFER_BIT);

        // 交换当前缓冲区和后台缓冲区
        SwapBuffers(deviceContext);
    }
}

最后在主函数中创建 GraphicsContext

#pragma once

#include <Windows.h>
#include "RenderWindow.h"
#include "GraphicsContext.h"

using namespace Simple2D;

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
    RenderWindow window(DEFAULT_WIN_W, DEFAULT_WIN_H);
    GraphicsContext graphicsContext(&window);

    MSG msg = { 0 };
    while ( msg.message != WM_QUIT ) {
        if ( PeekMessage(&msg, 0, 0, 0, PM_REMOVE) ) {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
        else {
            graphicsContext.flip();
        }
    }

    return 0;
}

编译代码,如果出现以下错误

可以在项目中忽略这个库

运行后出现这个窗口,显示为红色,证明搭建成功了

需要源码的可以点击这个链接:http://files.cnblogs.com/files/ForEmail5/Simple2D-02.rar

时间: 2024-10-08 10:30:35

基于OpenGL编写一个简易的2D渲染框架02——搭建OpenGL环境的相关文章

基于OpenGL编写一个简易的2D渲染框架01——创建窗口

最近正在学习OpenGL,我认为学习的最快方法就是做一个小项目了. 如果对OpenGL感兴趣的话,这里推荐一个很好的学习网站 https://learnopengl-cn.github.io/ 我用的是 vs2013,使用C++语言编写项目.这个小项目叫Simple2D,意味着简易的2D框架.最终的目的是可以渲染几何图形和图片,最后尝试加上一个2D粒子系统和Box2D物理引擎,并编译一个简单的游戏. 第一步,就是创建一个Win32项目. 接下来,生成一个窗口.编写一个RenderWindow类,

基于OpenGL编写一个简易的2D渲染框架-05 渲染文本

阅读文章前需要了解的知识:文本渲染 https://learnopengl-cn.github.io/06%20In%20Practice/02%20Text%20Rendering/ 简要步骤: 获取要绘制的字符的 Unicode 码,使用 FreeType 库获取对应的位图数据,添加到字符表中(后面同样的字符可以再表中直接索引),将字符表上的字符填充到一张纹理上.计算每个字符的纹理坐标,使用渲染器绘制 注意的问题: 对于中英文混合的字符串,使用 char 存储时,英文字符占 1 个字节,而中

基于OpenGL编写一个简易的2D渲染框架-08 重构渲染器-整体架构

事实上,前面编写的渲染器 Renderer 非常简陋,虽然能够进行一些简单的渲染,但是它并不能满足我们的要求. 当渲染粒子系统时,需要开启混合模式,但渲染其他顶点时却不需要开启混合模式.所以同时渲染粒子系统和其他纹理时会得不到想要的结果,渲染器还存在许多的不足: 1.当渲染许多透明图形时,没有对其进行排序,使得本应透明的图形没有透明. 2.不能对不同的顶点使用不同的状态进行渲染. 渲染器要做的东西很简单,就是 1.传递数据到 GPU 2.设置 OpenGL 状态信息(Alpha测试.模板测试.深

基于OpenGL编写一个简易的2D渲染框架-04 绘制图片

阅读文章前需要了解的知识,纹理:https://learnopengl-cn.github.io/01%20Getting%20started/06%20Textures/ 过程简述:利用 FreeImage 库加载图像数据,再创建 OpenGL 纹理,通过 Canvas2D 画布绘制,最后又 Renderer 渲染器渲染 本来想用 soil 库加载图像数据的,虽然方便,但是加载有些格式的图像文件时会出现一些问题.最后,改用 FreeImage 库来加载图像了. 添加 FreeImage 库到工

基于OpenGL编写一个简易的2D渲染框架-09 重构渲染器-Shader

Shader 只是进行一些简单的封装,主要功能: 1.编译着色程序 2.绑定 Uniform 数据 3.根据着色程序的顶点属性传递顶点数据到 GPU 着色程序的编译 GLuint Shader::createShaderProgram(const char* vsname, const char* psname) { std::string vShaderSource, fShaderSource; std::ifstream vShaderFile, fShaderFile; vShaderF

基于OpenGL编写一个简易的2D渲染框架-11 重构渲染器-Renderer

假如要渲染一个纯色矩形在窗口上,应该怎么做? 先确定顶点的格式,一个顶点应该包含位置信息 vec3 以及颜色信息 vec4,所以顶点的结构体定义可以这样: struct Vertex { Vec3 position; Vec4 color; }; 然后填充矩形四个顶点是数据信息: Vertex* data = ( Vertex* ) malloc(sizeof( Vertex ) * 4); data[0].position.set(0, 0, 0); data[1].position.set(

基于OpenGL编写一个简易的2D渲染框架-13 使用例子

这是重构渲染器的最后一部分了,将会给出一个 demo,测试模板测试.裁剪测试.半透明排序等等: 上图是本次 demo 的效果图,中间的绿色图形展现的是模板测试. 模板测试 void init(Pass*& p1, Pass*& p2) { p1 = new Pass; p2 = new Pass; Shader* s1 = new Shader("Shader/defaultGeometryShader.vs", "Shader/defaultGeometry

基于OpenGL编写一个简易的2D渲染框架-12 重构渲染器-BlockAllocator

BlockAllocator 的内存管理情况可以用下图表示 整体思路是,先分配一大块内存 Chunk,然后将 Chunk 分割成小块 Block.由于 Block 是链表的一个结点,所以可以通过链表的形式把未使用的 Block 连接起来,并保存到 pFreeLists 中.当我们向 BlockAllocator 申请一块内存时,BlockAllocator 会通过 pFreeLists 表索引出一块空闲的 Block,并返回其地址.当我们不断申请内存的时候,BlockAllocator 会不断返

基于OpenGL编写一个简易的2D渲染框架-07 鼠标事件和键盘事件

这次为程序添加鼠标事件和键盘事件 当检测到鼠标事件和键盘事件的信息时,捕获其信息并将信息传送到需要信息的对象处理.为此,需要一个可以分派信息的对象,这个对象能够正确的把信息交到正确的对象. 实现思路: 要实现以上的功能,需要几个对象: 事件分派器:EventDispatcher,负责将 BaseEvent 分派给 EventListener 对象 事件监听器:EventListener,这只是一个接口类,接受 BaseEvent 的对象,真正的处理在它的子类中实现 事件:BaseEvent,储存