我已经删除了下面的代码来尝试隔离/说明我的问题。我有一个 MFC Picture CWnd 控件,我派生该控件来创建我自己的 CGLImage 控件。
我已经使用这种方法在 OpenGL 即时模式下绘制得很好。现在,我尝试使用其他 SDL 应用程序中的着色器程序,但在第二次调用 OnPaint() 方法时访问着色器程序变量时遇到问题。
我在第一次 OnPaint() 调用时创建一次着色器程序。程序句柄在后续调用中仍然有效,但对于后续 OnPaint() 调用,此行中的 hColor 返回始终为 -1:
GLuint hColor = glGetUniformLocation( g_SolidProgram, "vColor4" );
为什么我第一次访问hColor后就无法访问?当然,我不需要在每次 OnPaint() 调用时重新创建程序。
#include "stdafx.h"
#include "MFC_GL.h"
#include "GLImage.h"
#include <glew.h>
#include <gl\glu.h> // Header File For The GLu32 Library
#include <freeglut.h>
IMPLEMENT_DYNAMIC(CGLImage, CWnd)
CGLImage::CGLImage()
{
}
CGLImage::~CGLImage()
{
}
#define ID_UPDATE_TIMER 102
BEGIN_MESSAGE_MAP(CGLImage, CWnd)
ON_WM_PAINT()
ON_WM_TIMER()
END_MESSAGE_MAP()
int MySetPixelFormat( HDC hdc )
{
PIXELFORMATDESCRIPTOR pfd = {
sizeof(PIXELFORMATDESCRIPTOR), // size of this pfd
1, // version number
PFD_DRAW_TO_WINDOW | // support window
PFD_SUPPORT_OPENGL | // support OpenGL
PFD_DOUBLEBUFFER, // double buffered
PFD_TYPE_RGBA, // RGBA type
24, // 24-bit color depth
0, 0, 0, 0, 0, 0, // color bits ignored
0, // no alpha buffer
0, // shift bit ignored
0, // no accumulation buffer
0, 0, 0, 0, // accum bits ignored
32, // 32-bit z-buffer
0, // no stencil buffer
0, // no auxiliary buffer
PFD_MAIN_PLANE, // main layer
0, // reserved
0, 0, 0 // layer masks ignored
};
int iPixelFormat;
// get the device context's best, available pixel format match
if((iPixelFormat = ChoosePixelFormat(hdc, &pfd)) == 0)
{
MessageBox(NULL, "ChoosePixelFormat Failed", NULL, MB_OK);
return 0;
}
// make that match the device context's current pixel format
if(SetPixelFormat(hdc, iPixelFormat, &pfd) == FALSE)
{
MessageBox(NULL, "SetPixelFormat Failed", NULL, MB_OK);
return 0;
}
return 1;
}
#ifndef STRINGIFY
#define STRINGIFY(a) #a
#endif
char *fsSolid = STRINGIFY(
precision mediump float;
uniform vec4 vColor4;
void main()
{
gl_FragColor = vColor4;
}
);
char *vsSolid = STRINGIFY(
attribute vec4 vPosition;
uniform mat4 MVP;
void main()
{
gl_Position = MVP * vPosition;
}
);
GLuint BuildShader( char *pszSource, GLenum shaderType )
{
GLuint hShader = glCreateShader( shaderType );
glShaderSource( hShader, 1, (char const**) &pszSource, 0 );
glCompileShader( hShader );
GLint compileSuccess = GL_FALSE;
glGetShaderiv( hShader, GL_COMPILE_STATUS, &compileSuccess );
if( compileSuccess == GL_FALSE )
{
GLchar message[ 256 ];
glGetShaderInfoLog( hShader, sizeof( message ), 0, &message[ 0 ] );
printf( "FATAL: SHADER (%s) %s\n", pszSource, message );
exit( 1 );
}
return hShader;
}
GLuint BuildProgram( char *pszVertexShaderSource, char *pszFragmentShaderSource )
{
GLuint vShader = BuildShader( pszVertexShaderSource, GL_VERTEX_SHADER );
GLuint fShader = BuildShader( pszFragmentShaderSource, GL_FRAGMENT_SHADER );
GLuint hProgram = glCreateProgram();
glAttachShader( hProgram, vShader );
glAttachShader( hProgram, fShader );
glLinkProgram( hProgram );
GLint linkSuccess;
glGetProgramiv( hProgram, GL_LINK_STATUS, &linkSuccess );
if( linkSuccess == GL_FALSE )
{
GLchar message[ 256 ];
glGetProgramInfoLog( hProgram, sizeof( message ), 0, &message[ 0 ] );
printf( "FATAL: %s\n", message );
exit( 1 );
}
return hProgram;
}
int g_ScreenWidth = 320;
int g_ScreenHeight = 200;
GLuint g_SolidProgram;
void CGLImage::OnTimer( UINT_PTR nIDEvent )
{
switch( nIDEvent )
{
case ID_UPDATE_TIMER:
Invalidate();
break;
}
}
void CGLImage::OnPaint()
{
CPaintDC dc( this ); // device context for painting
HDC hdc = ::GetDC(m_hWnd);
MySetPixelFormat( hdc );
HGLRC hglrc = wglCreateContext( hdc );
if( hglrc )
{
// try to make it the thread's current rendering context
if( wglMakeCurrent( hdc, hglrc ) )
{
static BOOL fFirstTime = TRUE;
if( fFirstTime )
{
fFirstTime = FALSE;
//glewExperimental=GL_TRUE;
GLenum glewRC = glewInit();
if( glewRC != GLEW_OK ) {
printf( "glewInit: %s\n", glewGetErrorString( glewRC ) );
exit( 1 );
}
g_SolidProgram = BuildProgram( vsSolid, fsSolid );
m_UpdateTimer = ::SetTimer( m_hWnd, ID_UPDATE_TIMER, 100, NULL );
}
glUseProgram( g_SolidProgram );
GLuint hColor = glGetUniformLocation( g_SolidProgram, "vColor4" ); // ### THIS FAILS
SwapBuffers( hdc );
}
}
wglMakeCurrent(NULL, NULL) ;
::ReleaseDC (m_hWnd, hdc) ;
wglDeleteContext(hglrc);
}
更新:
看起来 wglCreateContext() 和 wglMakeCurrent() 调用以某种方式破坏了着色器程序。
glUseProgram( g_SolidProgram );
GLuint hColor = glGetUniformLocation( g_SolidProgram, "vColor4" ); // OK HERE
hglrc = wglCreateContext( hdc );
if( wglMakeCurrent( hdc, hglrc ) ) {
glUseProgram( g_SolidProgram );
hColor = glGetUniformLocation( g_SolidProgram, "vColor4" ); // BROKEN HERE
}
看起来 OpenGL 上下文可能在 OnPaint() 调用之间丢失。据我所知,MFC 可以对渲染上下文何时有效很挑剔。尝试这个改变:
void CGLImage::OnPaint() {
CPaintDC dc( this );
HDC hdc = ::GetDC(m_hWnd);
MySetPixelFormat( hdc );
if (!hglrc) {
hglrc = wglCreateContext( hdc );
if( hglrc ) {
wglMakeCurrent( hdc, hglrc );
// Init GLEW (assuming done elsewhere already)
g_SolidProgram = BuildProgram( vsSolid, fsSolid ); // Build shader once
m_UpdateTimer = ::SetTimer( m_hWnd, ID_UPDATE_TIMER, 100, NULL );
}
}
wglMakeCurrent( hdc, hglrc );
glUseProgram( g_SolidProgram );
GLuint hColor = glGetUniformLocation( g_SolidProgram, "vColor4" ); // Should work now
// ... rest of ur code
}