现代opengl 设计入门,与mfc 组合

在前面的入门中,都是一个main 函数,没有菜单,没有对话框,如果要的话,我就想到与mfc 的组合。mfc 有菜单,对话框,opengl做图形显示。

我先是参考 cv::namedWindow, GLFWwindow以及其他程序嵌入到MFC中的教程

1: 运行效果

现代opengl 设计入门,与mfc 组合_第1张图片

2:下载glfw, 并用cmake 建立编译环境,然后做如下一些小的修改。

在前面入门中看到,glfw中窗口的创建, 就是使用函数glfwCreateWindow. 在VS中, 找到glfwCreateWindow函数的定义位置, 是在 glfw3.h文件中, 新加入一个函数glfwCreateWindowEx声明, 如下: 

 *  @reentrancy This function must not be called from a callback.
 *
 *  @thread_safety This function must only be called from the main thread.
 *
 *  @sa @ref window_creation
 *  @sa glfwDestroyWindow
 *
 *  @since Added in version 3.0.  Replaces `glfwOpenWindow`.
 *
 *  @ingroup window
 */
GLFWAPI GLFWwindow* glfwCreateWindow(int width, int height, const char* title, GLFWmonitor* monitor, GLFWwindow* share);
GLFWAPI GLFWwindow* glfwCreateWindowEx(int width, int height, const char* title, GLFWmonitor* monitor, GLFWwindow* share,int hParent);

在原本glfwCreateWindow函数的参数列表中新加入了参数int hParent. 新加入的参数, 本应该是HWND类型, 但该类型定义于Windows.h中, 本着尽可能少的改动代码, 以int代替了HWND类型。
现在打开win32_platform.h文件, 找到其中struct _GLFWwindowWin32定义所在的位置, 新加入HWND handleParent, 用来保存父窗口的句柄作为参数传递给创建窗口的函数. 如下图所示: 

// Win32-specific per-window data
//
typedef struct _GLFWwindowWin32
{
    HWND                handle;
    HICON               bigIcon;
    HICON               smallIcon;

    GLFWbool            cursorTracked;
    GLFWbool            iconified;

    // The last received cursor position, regardless of source
    int                 lastCursorPosX, lastCursorPosY;
	HWND handleParent;

} _GLFWwindowWin32;

修改好参数结构体之后, 现在定位glfwCreateWindow函数的定义, 定义于文件window.c中. 复制glfwCreateWindow函数的定义, 粘贴在glfwCreateWindow函数的定义的下方, 更改函数名为glfwCreateWindowEx并加入参数int hParent. 在该函数的实现中找到_glfwPlatformCreateWindow函数的调用地方, 在其前方加入下述代码:

window->win32.handleParent = (HWND)hParent;  //----------liwenz-------------

效果如下: 

 // Save the currently current context so it can be restored later
    previous = _glfwPlatformGetCurrentContext();
    if (ctxconfig.client != GLFW_NO_API)
        glfwMakeContextCurrent(NULL);

	window->win32.handleParent = (HWND)hParent;  //----------liwenz-------------
    // Open the actual window and create its context
    if (!_glfwPlatformCreateWindow(window, &wndconfig, &ctxconfig, &fbconfig))
    {
        glfwMakeContextCurrent((GLFWwindow*) previous);
        glfwDestroyWindow((GLFWwindow*) window);
        return NULL;
    }

现在, 沿着_glfwPlatformCreateWindow函数的函数调用一直找到API CreateWindowExW函数的调用地方, 位于win32_window.c文件定义的static int createWindow(_GLFWwindow* window, const _GLFWwndconfig* wndconfig)函数中被调用. 在 CreateWindowExW函数前加入下述代码, 并将CreateWindowExW函数的倒数第四个参数改成window->win32.handleParent.

	//--------------------liwenz
	if (NULL != window->win32.handleParent) {
		exStyle = 0;
		style = WS_CHILDWINDOW | (wndconfig->visible ? WS_VISIBLE : 0);
		}

截图如下: 

    wideTitle = _glfwCreateWideStringFromUTF8Win32(wndconfig->title);
    if (!wideTitle)
    {
        _glfwInputError(GLFW_PLATFORM_ERROR,
                        "Win32: Failed to convert window title to UTF-16");
        return GLFW_FALSE;
    }
	
	//--------------------liwenz
	if (NULL != window->win32.handleParent) {
		exStyle = 0;
		style = WS_CHILDWINDOW | (wndconfig->visible ? WS_VISIBLE : 0);
		}

    window->win32.handle = CreateWindowExW(exStyle,
                                           _GLFW_WNDCLASSNAME,
                                           wideTitle,
                                           style,
                                           xpos, ypos,
                                           fullWidth, fullHeight,
                                           window->win32.handleParent, // No parent window  liwenz before NULL
                                           NULL, // No window menu
                                           GetModuleHandleW(NULL),
                                           NULL);

    free(wideTitle);

修改好了之后, 对代码进行编译, 还是运行前面示例进行验证. 仍然可以得到前面原始代码所展示的效果. 说明我们代码的修改没有对原本性能产生破坏。

3: 建立一个简单的mfc SDI 工程

按照以前教程 现代opengl 设计入门 准备第一个工程  添加include lib 目录,以及lib 的添加。

修改都在类CopenglmfcView里实现,添加 

在openglmfcView.h 文件头加

#include 
#include 

在类中添加4个事件的响应函数,openglmfcView.h 中如下显示

afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
    afx_msg BOOL OnEraseBkgnd(CDC* pDC);
    afx_msg void OnDestroy();
    afx_msg void OnSize(UINT nType, int cx, int cy);

在openglmfcView.cpp 中除了上面4个函数外,另要修改OnDraw

函数的代码就是分割我们前面例子,比如 现代opengl 设计入门,画图第一个三角形  这个最简单了。初始化代码放在 OnCreate, 结束释放代码放 OnDestroy,着色循环代码放OnDraw,framebuffer_size_callback的代码放OnSize。OnEraseBkgnd 只是取消 return CView::OnEraseBkgnd(pDC); 代之以 return 1;

    return 1; 
	//return CView::OnEraseBkgnd(pDC);

在代码中增加类变量,代替代码间的沟通,因为循环和初始间需要共用。取消回调函数设置部分。

public:
	GLFWwindow* m_window;
	int m_shaderProgram;
	unsigned int m_VAO,m_VBO;

最后代码如下:

OnDraw:

void CopenglmfcView::OnDraw(CDC* /*pDC*/)
{
	CopenglmfcDoc* pDoc = GetDocument();
	ASSERT_VALID(pDoc);
	if (!pDoc)
		return;

	// TODO: add draw code for native data here
	        // render
        // ------
        glClearColor(0.2f, 0.3f, 0.5f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT);
 
        // draw our first triangle
        glUseProgram(m_shaderProgram);
        glBindVertexArray(m_VAO); // seeing as we only have a single VAO there's no need to bind it every time, but we'll do so to keep things a bit more organized
        glDrawArrays(GL_TRIANGLES, 0, 3);
        // glBindVertexArray(0); // no need to unbind it every time 
 
        // glfw: swap buffers and poll IO events (keys pressed/released, mouse moved etc.)
        // -------------------------------------------------------------------------------
        glfwSwapBuffers(m_window);
        glfwPollEvents();

      InvalidateRect(NULL,FALSE); //???
}

OnCreate

int CopenglmfcView::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
	if (CView::OnCreate(lpCreateStruct) == -1)
		return -1;

	// TODO:  Add your specialized creation code here

	CClientDC* m_pDC = new CClientDC(this);

	//Failure to Get DC
	if( m_pDC == NULL )
		return FALSE;
	CWnd* pwndParent = this->GetParent();

	CRect rc;
	pwndParent->GetWindowRect(&rc);

	int width,height;
	width=1362;
	height=702;

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

	m_window = glfwCreateWindowEx(width, height, "Simple example", NULL, NULL, (int)GetParent()->m_hWnd);
	if(m_window==NULL)
	{
		MessageBox("windows fail");
	}
	glfwMakeContextCurrent(m_window);

	// glad: load all OpenGL function pointers
    // ---------------------------------------
    if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
    {
        //std::cout << "Failed to initialize GLAD" << std::endl;
		MessageBox("glad fail");
        return -1;
    }

	const char *vertexShaderSource = "#version 330 core\n"
    "layout (location = 0) in vec3 aPos;\n"
    "void main()\n"
    "{\n"
    "   gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);\n"
    "}\0";
const char *fragmentShaderSource = "#version 330 core\n"
    "out vec4 FragColor;\n"
    "void main()\n"
    "{\n"
    "   FragColor = vec4(1.0f, 0.5f, 0.2f, 1.0f);\n"
    "}\n\0";

	// build and compile our shader program
    // ------------------------------------
    // vertex shader
    int vertexShader = glCreateShader(GL_VERTEX_SHADER);
    glShaderSource(vertexShader, 1, &vertexShaderSource, NULL);
    glCompileShader(vertexShader);
    // check for shader compile errors
    int success;
    char infoLog[512];
    glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success);
    if (!success)
    {
        glGetShaderInfoLog(vertexShader, 512, NULL, infoLog);
        MessageBox("ERROR::SHADER::VERTEX::COMPILATION_FAILED\n" );
    }
    // fragment shader
    int fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
    glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL);
    glCompileShader(fragmentShader);
    // check for shader compile errors
    glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &success);
    if (!success)
    {
        glGetShaderInfoLog(fragmentShader, 512, NULL, infoLog);
        MessageBox("ERROR::SHADER::FRAGMENT::COMPILATION_FAILED\n");
    }
    // link shaders
    m_shaderProgram = glCreateProgram();
    glAttachShader(m_shaderProgram, vertexShader);
    glAttachShader(m_shaderProgram, fragmentShader);
    glLinkProgram(m_shaderProgram);
    // check for linking errors
    glGetProgramiv(m_shaderProgram, GL_LINK_STATUS, &success);
    if (!success) {
        glGetProgramInfoLog(m_shaderProgram, 512, NULL, infoLog);
        MessageBox("ERROR::SHADER::PROGRAM::LINKING_FAILED\n" );
    }
    glDeleteShader(vertexShader);
    glDeleteShader(fragmentShader);
 
    // set up vertex data (and buffer(s)) and configure vertex attributes
    // ------------------------------------------------------------------
    float vertices[] = {
        -0.5f, -0.5f, 0.0f, // left  
         0.5f, -0.5f, 0.0f, // right 
         0.0f,  0.5f, 0.0f  // top   
    }; 
 
    //unsigned int VBO, VAO;
    glGenVertexArrays(1, &m_VAO);
    glGenBuffers(1, &m_VBO);
    // bind the Vertex Array Object first, then bind and set vertex buffer(s), and then configure vertex attributes(s).
    glBindVertexArray(m_VAO);
 
    glBindBuffer(GL_ARRAY_BUFFER, m_VBO);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
 
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
    glEnableVertexAttribArray(0);
 
    // note that this is allowed, the call to glVertexAttribPointer registered VBO as the vertex attribute's bound vertex buffer object so afterwards we can safely unbind
    glBindBuffer(GL_ARRAY_BUFFER, 0); 
 
    // You can unbind the VAO afterwards so other VAO calls won't accidentally modify this VAO, but this rarely happens. Modifying other
    // VAOs requires a call to glBindVertexArray anyways so we generally don't unbind VAOs (nor VBOs) when it's not directly necessary.
    glBindVertexArray(0); 
 
    return 0;
}

其他都比较少:

BOOL CopenglmfcView::OnEraseBkgnd(CDC* pDC)
{
	// TODO: Add your message handler code here and/or call default
	return 1; 
	//return CView::OnEraseBkgnd(pDC);
}


void CopenglmfcView::OnDestroy()
{
	CView::OnDestroy();

	// TODO: Add your message handler code here
	    // optional: de-allocate all resources once they've outlived their purpose:
    // ------------------------------------------------------------------------
    glDeleteVertexArrays(1, &m_VAO);
    glDeleteBuffers(1, &m_VBO);
 
    // glfw: terminate, clearing all previously allocated GLFW resources.
    // ------------------------------------------------------------------
    glfwTerminate();

}


void CopenglmfcView::OnSize(UINT nType, int cx, int cy)
{
	CView::OnSize(nType, cx, cy);

	if ( 0 >= cx || 0 >= cy || nType == SIZE_MINIMIZED )
		return;

	glfwMakeContextCurrent(m_window);
	glViewport( 0, 0, cx, cy );
	InvalidateRect(NULL,FALSE);
	
}

4:调试时遇到的问题

glad.c 需要添加到工程,否则编译到链接时有说缺少链接函数。加了glad.c  说是如下错

glad.c : fatal error C1853: 'Debug\openglmfc.pch' precompiled header file is from a previous version of the compiler, or the precompiled header is C++ and you are using it from C (or vice versa)
这个选中glad.c ,鼠标右键,选择 properties, 然后C/C++->Precompiled Headers, 选择 Not Using Precompiles Headers, 然后就可以编译链接好了。如下图:

现代opengl 设计入门,与mfc 组合_第2张图片

5:存在的问题

初始时设置 width, height 小于实际屏幕尺寸时,画图和清屏在拉大时外框并没有跟着变化。 height 方向三角形尺寸有异样,尺寸小的时候显示不完全,一直到最大或设置尺寸才正常。如下图开始设置 width=800, 我的屏尺寸为1300多,左右的问题。上下方向,三角形并不在正中央,尺寸越小,越偏下,甚至看不到。

现代opengl 设计入门,与mfc 组合_第3张图片

你可能感兴趣的:(c++,opengl)