render.h
#pragma once
#include <windows.h>
class GLRenderer
{
// 窗口句柄 的作用是什么??
public:
GLRenderer(HWND hwnd, int width, int height);
~GLRenderer();
void Renderer();
//拖动的时候改变大小
void Resize(int width, int height);
private:
bool CreateGLContext();
private:
HDC dc;//设备上下文
HGLRC rc;//opengl上下文 也是渲染上下文
HWND hwnd;
};
render.cpp:
#include "GLRenderer.h"
#include <assert.h>
//#define GLEW_STATIC
#include <GL/glew.h>
#pragma comment(lib, "glew32.lib")
//#pragma comment(lib, "glew32s.lib")//静态库 需要顶一个宏 #define GLEW_STATIC
//引入opengl库
#pragma comment(lib, "opengl32.lib")
GLRenderer::GLRenderer(HWND hwnd, int width, int height)
{
this->hwnd = hwnd;
CreateGLContext();
//把创建的opengl上下文 置为当前的opengl上下文
wglMakeCurrent(dc, rc);
//初始化glew
assert(glewInit() == GLEW_OK);
Resize(width, height);
}
GLRenderer::~GLRenderer()
{
//opengl上下文置为null
wglMakeCurrent(NULL, NULL);
if (rc)
//删除渲染上下文
wglDeleteContext(rc);
if (dc)
//释放设备上下文
ReleaseDC(hwnd, dc);
}
void GLRenderer::Renderer()
{
glClearColor(1.0f, 0.0f, 0.0f, 1.0f);
//清理颜色缓冲区 深度缓冲区
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
//交换一下上缓冲区
SwapBuffers(dc);
}
void GLRenderer::Resize(int width, int height)
{
//窗口大小改变 的时候 改变下glew的视口大小
glViewport(0, 0, width, height);
}
bool GLRenderer::CreateGLContext()
{
//获取设备上下文
dc = GetDC(hwnd);
//概念:深度缓冲 模板缓冲区位深
PIXELFORMATDESCRIPTOR pfd;//???结构体
ZeroMemory(&pfd, sizeof(pfd));
pfd.nSize = sizeof(pfd);
//指定结构体版本号,设置为1
pfd.nVersion = 1;
//rgba 4个字节
pfd.cColorBits = 32;
//设置深度缓冲 空间物体 对于眼睛有距离 记录这些点的层次
pfd.cDepthBits = 24;
//设置模板缓冲区位深 :屏幕上的像素点保留一个无符号的整形
pfd.cStencilBits = 8;
//设置像素类型
pfd.iPixelType = PFD_TYPE_RGBA;
//可以绘制到窗口
// 第三个参数设置双缓冲 机制 有两张图 一个前一个后 前面显示,后面绘画 绘画完了 放在前面 显示放在后面 开始绘画 绘画完了 放在前面 两者交替循环
pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
int format = 0;
format = ChoosePixelFormat(dc, &pfd);
if (!format)
{
throw;//实际开发不是这么做
}
SetPixelFormat(dc, format, &pfd);
//openfl上下文
rc = wglCreateContext(dc);
return true;
}
运行结果:
有些不明白 的是 渲染前有断时间为空档期 需要等候几秒 ,这个 是渲染的第一次 是这样的吗
如果是渲染几个不一样的 都要等待的话 是不是 性能有些浪费 ,是不是 哪里会有 初始化的问题 的补助 才导致 第一张 有点 缓冲等待 呢 ?
补充代码 main.cpp:
#include <iostream>
using namespace std;
#include"Engine.h"
//APIENTRY :和C约定的入口
//HINSTANCE :
//LPWSTR:命令行参数
//int nCmdShow :窗口显示模式
//量类型 HINSTANCE HINSTANCE 是Windows里的一中数据类型
// ,其实就是一个无符号的长整形,是32位的,是用于标示(记录)一个程序的实例
int APIENTRY wWinMain(_In_ HINSTANCE hIntance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPWSTR lpCmdLine, _In_ int nCmdShow)
{
Engine engine(hIntance, "window_class");
engine.initialze("window title", 800, 600, nCmdShow);
int time = 0;
while (engine.ProcessMessage())
{
Sleep(10);
if (time++ > 200)
{
//测试过200秒后就退出 close消息测试
//engine.Close();
engine.Render();
}
}
return 0;
}
engine.h:
#pragma once
#include<Windows.h>
#include<string>
class GLRenderer;
//这里没有引入render.h文件 而是声明 这样做的的优势是什么
class Engine
{
public:
Engine(HINSTANCE hInstance, std::string window_class);
~Engine();
bool initialze(std::string wind_title, int width, int height, int nCmdShow);
//注册窗口类
bool RegisterWindowsClass();
//处理消息的方法
bool ProcessMessage();
void Close();
void Render();
private:
HWND handle;//窗口句柄
HINSTANCE hInstance;//实例句柄
std::string wind_title;//窗口标题
std::wstring wind_title_wide;//宽字符标题 用于unicode
std::string window_class;//窗口类 ??
std::wstring window_class_wide;
//窗口 宽高
int width;
int height;
GLRenderer* renderer = nullptr;
private:
//窗口句柄 消息类型 CALLBACK标准性约定
static LRESULT CALLBACK HandleMessageSetup(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
//重定向的方法
static LRESULT HandleMessageRedict(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
//重定向到这个方法
LRESULT winProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
//创建 和销魂
void OnStart();
void OnClose();
};
engin,cpp:
#include "Engine.h"
#include"GLRenderer.h"
Engine::Engine(HINSTANCE hInstance, std::string window_class)
{
this->hInstance = hInstance;
this->window_class = window_class;
this->window_class_wide = std::wstring(window_class.begin(), window_class.end());
RegisterWindowsClass();
}
bool Engine::initialze(std::string wind_title, int width, int height, int nCmdShow)
{
this->wind_title = wind_title;
this->wind_title_wide = std::wstring(wind_title.begin(), wind_title.end());
this->width = width;
this->height = height;
int nWindMertricsX = GetSystemMetrics(SM_CXSCREEN);
int nWindMertricsY = GetSystemMetrics(SM_CYSCREEN);
RECT wr;
wr.left = (nWindMertricsX - width) / 2;
wr.top = (nWindMertricsY - height) / 2;
wr.right = wr.left+width;
wr.bottom= wr.top+height;//上下左右 有其他的可以
//WS_OVERLAPPEDWINDOW 样式 False不包含菜单
AdjustWindowRect(&wr, WS_OVERLAPPEDWINDOW, FALSE);
handle=CreateWindowEx(0,//扩展风格
#ifdef UNICODE
this->window_class_wide.c_str(),
this->wind_title_wide.c_str(),
#else
this->window_class.c_str(),
this->wind_title.c_str(),
#endif //
WS_OVERLAPPEDWINDOW,//风格
wr.left,
wr.top,
wr.right - wr.left,
wr.bottom - wr.top,//屏幕上下左右
NULL,
NULL,
hInstance,
this);
if (!handle)//窗口句柄
{
throw;
return false;
}
OnStart();
ShowWindow(handle, nCmdShow);//最大化最小化 nCmdshow 显示模式
UpdateWindow(handle);//更新窗口
SetForegroundWindow(handle);//把窗口激活到前列线程,能接受键盘 鼠标事件
SetFocus(handle);
return true;
}
bool Engine::RegisterWindowsClass()
{
WNDCLASSEX wcex;
//置零
ZeroMemory(&wcex, sizeof(WNDCLASSEX));
//WNDCLASSEX大小
wcex.cbSize = sizeof(WNDCLASSEX);
//窗口风格
wcex.style = CS_HREDRAW|CS_VREDRAW|CS_OWNDC;
//创口处理函数
//wcex.lpfnWndProc = DefWindowProc;
wcex.lpfnWndProc = HandleMessageSetup;
//wcex.style = WS_OVERLAPPEDWINDOW;
//wcex.lpfnWndProc = NULL;
wcex.cbClsExtra = 0;//??
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
//默认图标
wcex.hIcon = LoadIcon(NULL, IDI_APPLICATION);
//默认光标
wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
//画布的背景颜色
wcex.hbrBackground = (HBRUSH)GetStockObject(DKGRAY_BRUSH);
//设置菜单
wcex.lpszMenuName = NULL;
//小图标
wcex.hIconSm = NULL;
#ifdef UNICODE
wcex.lpszClassName = this->window_class_wide.c_str();
#else
wcex.lpszClassName = this->window_class.c_str();
#endif // !UNICODE
if (!RegisterClassEx(&wcex))
{
int error = GetLastError();
throw;//注意:实际开发中 不能这么做
return false;
}
return true;
}
bool Engine::ProcessMessage()
{
//定义msg 并且置0
MSG msg;
ZeroMemory(&msg, sizeof(MSG));
while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
//转移这份消息
TranslateMessage(&msg);
//分发消息
DispatchMessage(&msg);
}
if (msg.message == WM_NULL)
{
if (!IsWindow(handle))
return false;
}
if (!handle)
{
return false;
}
return true;
}
LRESULT Engine::HandleMessageSetup(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_NCCREATE://NCCREATE的消息
{
const CREATESTRUCT* pCreate = reinterpret_cast<CREATESTRUCT*>(lParam);
Engine* engine = reinterpret_cast<Engine*>(pCreate->lpCreateParams);
if (!engine)
{
throw;
}
//设置用户数据
SetWindowLongPtr(hwnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(engine));
//窗口处理函数
SetWindowLongPtr(hwnd, GWLP_WNDPROC, reinterpret_cast<LONG_PTR>(HandleMessageRedict));
return DefWindowProcW(hwnd, uMsg, wParam, lParam);
}
default:
return DefWindowProcW(hwnd, uMsg, wParam, lParam);
}
return LRESULT();
}
LRESULT Engine::HandleMessageRedict(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
Engine* engine = reinterpret_cast<Engine*>(GetWindowLongPtr(hwnd, GWLP_USERDATA));
//return LRESULT();
return engine->winProc(hwnd, uMsg, wParam, lParam);
}
//窗口处理函数
LRESULT Engine::winProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
//窗口的生命周期做好
switch (uMsg)
{
case WM_CLOSE:
DestroyWindow(hwnd);
this->handle = NULL;
OnClose();
return 0;
case WM_DESTROY:
//发出退出消息
PostQuitMessage(0);
return 0;
case WM_SIZE:
//拖动的时候改变大小
//获取 客户区的大小
RECT rect;
GetClientRect(hwnd, &rect);
if (renderer)
{
renderer->Resize(rect.right-rect.left, rect.bottom-rect.top);
}
return 0;
default:
return DefWindowProcW(hwnd, uMsg, wParam, lParam);
break;
}
//return LRESULT();
return DefWindowProcW(hwnd, uMsg, wParam, lParam);
}
void Engine::OnStart()
{
if (renderer)
{
delete renderer;
}
renderer = NULL;//已经要置为空
renderer = new GLRenderer(handle, width, height);
}
void Engine::OnClose()
{
//SendMessage(handle, WM_CLOSE, 0, 0)
delete renderer;;
renderer = NULL;
}
Engine::~Engine()
{
if (!UnregisterClass(
#ifdef UNICODE
this->window_class_wide.c_str(),
#else
this->window_class.c_str(),
#endif //
hInstance))
{
throw;
}
}
void Engine::Close()
{
SendMessage(handle, WM_CLOSE, 0, 0);
}
void Engine::Render()
{
if (renderer)
{
renderer->Renderer();
}
}