半年没有写点东西总结下了。有时候脑中想法很多,却又感觉文字功底着实有限,无法把自由散漫的思想分门别类地记下来,好在可以与人交流,掏空自己,然后吸收些新东西;有时候却什么都不想,就翻翻书,享受前人的精神盛宴。
还是写技术性的东西好,可以有章可循,写得不好,就算别人看不大懂,自己肯定是理解,若干日月后翻出来看还可以嘲笑以前的自己,知道自己走过了哪些里程碑。
这一两年涉及太广,也没专心下了深入研究一门,最近又学了点opengl,简单记录下遇到的一些问题。
虽然windows上opengl的库才1.1版本,但最后发现也够了,我只是为了一个窗口显示多通道视频而已。版本问题很麻烦,因为程序手册不一样,入门去学的路径也不一样了。之前企图用最新版的4.3,更新了显卡驱动,又用了诸如glew和glut的扩展库,按照红宝书的来,却发现glsl语言相关的函数怎么也编译不过,网上资料又少,无奈,此路不通。本来,我只是写一个低层库,opengl的扩展库能不用就尽量不用,于是决定学下nehe的"legacy tutorials",窗口创建之类的虽然有点麻烦,但也能验证问题。
我的库给别人用的,别人的工程时mfc工程,有自己窗口,如果要绘图,需要获取别人的窗口句柄和画布,创建自己的Rendering Context,这里有段代码,之前也没仔细研究,后面发现问题就出现在这上面:
bool CGLDevice::InitialWnd(HWND hwnd)
{
#ifdef WIN32
static PIXELFORMATDESCRIPTOR pfd= // pfd Tells Windows How We Want Things To Be
{
sizeof(PIXELFORMATDESCRIPTOR), // Size Of This Pixel Format Descriptor
1, // Version Number
PFD_DRAW_TO_WINDOW | // Format Must Support Window
PFD_SUPPORT_OPENGL | // Format Must Support OpenGL
PFD_DOUBLEBUFFER, // Must Support Double Buffering
PFD_TYPE_RGBA, // Request An RGBA Format
32, // Select Our 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, // Accumulation Bits Ignored
16, // 16Bit Z-Buffer (Depth Buffer)
0, // No Stencil Buffer
0, // No Auxiliary Buffer
PFD_MAIN_PLANE, // Main Drawing Layer
0, // Reserved
0, 0, 0 // Layer Masks Ignored
};
HDC hDC=NULL; // Private GDI Device Context
GLuint PixelFormat; // Holds The Results After Searching For A Match
if (!(hDC=GetDC(hwnd))) // Did We Get A Device Context?
{
return false; // Return FALSE
}
if (!(PixelFormat=ChoosePixelFormat(hDC,&pfd))) // Did Windows Find A Matching Pixel Format?
{
return false; // Return FALSE
}
if(!SetPixelFormat(hDC,PixelFormat,&pfd)) // Are We Able To Set The Pixel Format?
{
return false; // Return FALSE
}
if (!(m_hrc=wglCreateContext(hDC))) // Are We Able To Get A Rendering Context?
{
return false; // Return FALSE
}
if(!wglMakeCurrent(hDC,m_hrc)) // Try To Activate The Rendering Context
{
return false; // Return FALSE
}
//ShowWindow (hwnd, SW_NORMAL); // Make The Window Visible
#endif
return true;
}
这段代码如果使用glut的话很简单,几行搞定:
glutInitWindowPosition(400, 0);
glutInitWindowSize(m_WindowWidth, m_WindowHeight);
glutCreateWindow("test");
用了上面的代码,出了些摸不着头脑的问题: glGenTextures和glBindTexture失败,后来想到是不是线程的问题,把窗口、画布初始化和opengl的操作放在同一个线程才把问题解决。也不知道为什么会有这样的直觉,后来研究了这段代码,发现原因:
wglMakeCurrent——The wglMakeCurrent function makes a specified OpenGL rendering context the calling thread's current rendering context.这个函数是把调用该函数的线程的渲染环境指定为opengl的渲染环境。原来如此,果然是线程相关的。
另外还有一些小细节,比如RGB转到YUV后,是要把top和bottom颠倒的:在做这样的一段输出图片的操作前交换坐标
float tmp;
tmp = m_SurfaceStruct.SourceRectF.top;
m_SurfaceStruct.SourceRectF.top = m_SurfaceStruct.SourceRectF.bottom;
m_SurfaceStruct.SourceRectF.bottom = tmp;
glTexCoord2f(m_SurfaceStruct.SourceRectF.left, m_SurfaceStruct.SourceRectF.bottom);
glVertex2f(m_SurfaceStruct.ViewRectF.left, m_SurfaceStruct.ViewRectF.bottom);
glTexCoord2f(m_SurfaceStruct.SourceRectF.right, m_SurfaceStruct.SourceRectF.bottom);
glVertex2f(m_SurfaceStruct.ViewRectF.right, m_SurfaceStruct.ViewRectF.bottom);
glTexCoord2f(m_SurfaceStruct.SourceRectF.right, m_SurfaceStruct.SourceRectF.top);
glVertex2f(m_SurfaceStruct.ViewRectF.right, m_SurfaceStruct.ViewRectF.top);
glTexCoord2f(m_SurfaceStruct.SourceRectF.left, m_SurfaceStruct.SourceRectF.top);
glVertex2f(m_SurfaceStruct.ViewRectF.left, m_SurfaceStruct.ViewRectF.top);
下面贴一段windows平台的测试程序,这里直接用了glut。程序的功能是把两幅图交替显示,模拟动画的效果,且进行了简单的遮罩:
//#include "GL/glew.h"
#include "GL/glut.h"
#include <stdio.h>
#include <time.h>
static GLint ImageWidth[4];
static GLint ImageHeight[4];
static GLubyte* PixelData[4] = {0};
static GLubyte* tmpData[4] = {0};
static const GLint BMP_INFO_OFFSET = 0x0012;
static const GLint BMP_DATA_OFFSET = 0x0036;
static const unsigned short WindowWidth = 1024;
static const unsigned short WindowHeight = 768;
GLuint Texture[4];
class KsTime
{
private:
__int64 m_base, m_start, m_stop;
bool m_print;
public:
KsTime(bool e_print = false);
~KsTime();
void restart();
void elapsed();
double get_elapsed();
void set_print(bool e_print);
};
KsTime::KsTime(bool e_print) : m_print(e_print)
{
QueryPerformanceFrequency((LARGE_INTEGER *)(&m_base));
restart();
}
KsTime::~KsTime() { if (m_print) elapsed(); }
void KsTime::restart(){ QueryPerformanceCounter((LARGE_INTEGER *)(&m_start)); }
void KsTime::elapsed()
{
QueryPerformanceCounter((LARGE_INTEGER *)(&m_stop));
printf("%.6f\n", (double)(m_stop - m_start) / m_base);
}
double KsTime::get_elapsed()
{
QueryPerformanceCounter((LARGE_INTEGER *)(&m_stop));
return (double)(m_stop - m_start) / m_base;
}
void KsTime::set_print(bool e_print) { m_print = e_print; }
void myDisplay();
KsTime kst;
void timeFunc(int value)
{
kst.restart();
myDisplay();
// 大约25fps
int d = 40 - (int)(kst.get_elapsed()*1000);
if (d <= 0) d = 0;
glutTimerFunc(d, timeFunc, 0);
}
void Init()
{
for (int i=0; i<2; i++)
{
glGenTextures(1, &Texture[i]);
glBindTexture(GL_TEXTURE_2D, Texture[i]);
//每一个都要设定
//下面两个GL_CLAMP的设定去掉后是无缝的效果
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
//glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, ImageWidth[i], ImageHeight[i], 0, GL_BGR_EXT, GL_UNSIGNED_BYTE, PixelData[i]);// 生成空纹理
}
{//color fill
glGenTextures(1, &Texture[2]);
glBindTexture(GL_TEXTURE_2D, Texture[2]);
//每一个都要设定
//下面两个GL_CLAMP的设定去掉后是无缝的效果
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
}
glClearColor (0.0f, 0.0f, 0.0f, 0.0f); // Black Background
glClearDepth (1.0f); // Depth Buffer Setup
glDepthFunc (GL_LEQUAL); // The Type Of Depth Testing
glEnable (GL_DEPTH_TEST); // Enable Depth Testing
glEnable(GL_COLOR_MATERIAL); // Enable Color Material (Allows Us To Tint Textures)
//glEnable(GL_TEXTURE_2D); // Enable Texture Mapping
}
void myDisplay()
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Clear The Screen And The Depth Buffer
//glLoadIdentity();
static int dis = 0;
if (dis++ % 2 == 0)
{
memcpy(tmpData[0], PixelData[0], ImageWidth[0]*ImageHeight[0]*3);
memcpy(tmpData[1], PixelData[1], ImageWidth[0]*ImageHeight[0]*3);
}
else
{
memcpy(tmpData[0], PixelData[1], ImageWidth[0]*ImageHeight[0]*3);//为方便,两幅图长宽一样
memcpy(tmpData[1], PixelData[0], ImageWidth[0]*ImageHeight[0]*3);
}
glEnable(GL_TEXTURE_2D);
{//bmp1
glBindTexture(GL_TEXTURE_2D, Texture[0]);
if (glIsTexture(Texture[0]))
{
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, ImageWidth[0], ImageHeight[0], 0, GL_BGR_EXT, GL_UNSIGNED_BYTE, tmpData[0]);
glBegin(GL_QUADS); // Begin Drawing A Single Quad
// We Fill The Entire 1/4 Section With A Single Textured Quad.
glTexCoord2f(0.0f, 0.0f); glVertex2f(-1.0f, 0.0f); //把原图的坐标(glTexCoord2f) 绑定到视图坐标(glVertex2f)
glTexCoord2f(1.0f, 0.0f); glVertex2f(0.0f, 0.0f);
glTexCoord2f(1.0f, 1.0f); glVertex2f(0.0f, 1.0f);
glTexCoord2f(0.0f, 1.0f); glVertex2f(-1.0f, 1.0f);
glEnd();
}
}
{//bmp2
glBindTexture(GL_TEXTURE_2D, Texture[1]);
if (glIsTexture(Texture[1]))
{
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, ImageWidth[1], ImageHeight[1], 0, GL_BGR_EXT, GL_UNSIGNED_BYTE, tmpData[1]);
glBegin(GL_QUADS); // Begin Drawing A Single Quad
// We Fill The Entire 1/4 Section With A Single Textured Quad.
glTexCoord2f(0.0f, 0.0f); glVertex2f(0.0f, 0.0f); //把原图的坐标(glTexCoord2f) 绑定到视图坐标(glVertex2f)
glTexCoord2f(0.5f, 0.0f); glVertex2f(1.0f, 0.0f);
glTexCoord2f(0.5f, 0.5f); glVertex2f(1.0f, 1.0f);
glTexCoord2f(0.0f, 0.5f); glVertex2f(0.0f, 1.0f);
glEnd();
}
}
{//color fill
glBindTexture(GL_TEXTURE_2D, Texture[2]);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 1, 1, 0, GL_RGB, GL_UNSIGNED_BYTE, PixelData[2]);
glBegin(GL_QUADS); // Begin Drawing A Single Quad
// We Fill The Entire 1/4 Section With A Single Textured Quad.
glTexCoord2f(0.0f, 0.0f); glVertex2f(-1.0f, -1.0f); //把原图的坐标(glTexCoord2f) 绑定到视图坐标(glVertex2f)
glTexCoord2f(1.0f, 0.0f); glVertex2f(0.0f, -1.0f);
glTexCoord2f(1.0f, 1.0f); glVertex2f(0.0f, 0.0f);
glTexCoord2f(0.0f, 1.0f); glVertex2f(-1.0f, 0.0f);
glEnd();
}
glDisable(GL_TEXTURE_2D);
glFlush();
//glutSwapBuffers();
}
//读出来的BMP文件是BGR的顺序 不是RGB的顺序
int LoadImg(const char* pFileName, GLubyte*& pPixelBuf, GLubyte*& pTmpData, GLint &width, GLint &height)
{
FILE *pFile = fopen(pFileName, "rb");
if (!pFile)
{
return -1;
}
// 读取图像大小信息
fseek(pFile, BMP_INFO_OFFSET, SEEK_SET);
fread(&width, sizeof(width), 1, pFile);
fread(&height, sizeof(height), 1, pFile);
GLint PixelLength = width * 3;
if (PixelLength % 4 != 0)
{
PixelLength += (4 - PixelLength % 4);// BMP图像是4像素对齐的
}
GLint count = PixelLength * height;
pPixelBuf = new GLubyte[count];
if (!pPixelBuf)
{
return -1;
}
pTmpData = new GLubyte[count];
memset(pTmpData, 0, count);
memset(pPixelBuf, 0, count);
fseek(pFile, 0, SEEK_END);
int ll = ftell(pFile);
fseek(pFile, BMP_DATA_OFFSET, SEEK_SET);
GLint Len = 0, ret = 0;
if(count != ll - BMP_DATA_OFFSET)
{
return -1;
}
while(ret>=0 && Len < count)
{
if (count - Len > 1024 * 20)
{
ret = fread(pPixelBuf + Len, sizeof(GLubyte), 1024 * 20, pFile);
}
else
{
ret = fread(pPixelBuf + Len, sizeof(GLubyte), count - Len, pFile);
}
Len += ret;
if (ret == 0)
{
int err = ferror(pFile);
if (feof(pFile))
{
printf("file eof\n");
}
}
}
fclose(pFile);
return 0;
}
int main(int argc, char *argv[])
{
if (LoadImg("test.bmp", PixelData[0], tmpData[0], ImageWidth[0], ImageHeight[0]) < 0)
{
return 0;
}
if (LoadImg("test2.bmp", PixelData[1], tmpData[1], ImageWidth[1], ImageHeight[1]) < 0)
{
return 0;
}
PixelData[2] = new GLubyte[3];
PixelData[2][0] = 0;
PixelData[2][1] = 0;
PixelData[2][2] = 255;
ImageWidth[2] = 1;
ImageHeight[2] = 1;
int tmp;
glutInit(&tmp, NULL);
glutInitDisplayMode(GLUT_RGB | GLUT_SINGLE);
glutInitWindowPosition(400, 0);
glutInitWindowSize(WindowWidth, WindowHeight);
glutCreateWindow("test");
const char* version = (const char*)glGetString(GL_VERSION);
//const char* extensions = (const char*)glGetString(GL_EXTENSIONS);
printf("OpenGL 版本:%s\n", version);
Init();
glutDisplayFunc(&myDisplay);
glutTimerFunc(400, timeFunc, 0);
glutMainLoop();
for (int i=0; i<4; i++)
{
if (PixelData[i])
{
delete PixelData[i];
}
if (tmpData[i])
{
delete tmpData[i];
}
}
return 0;
}
其中定时那部分的代码是网上找的,觉得很蹊跷..看了下glutTimerFunc的源码,发现glut本身是有定时器的,因此不需要上面代码中的KsTime,直接把回调函数写成这样就行:
void timeFunc(int value)
{
myDisplay();
glutTimerFunc(100, timeFunc, 0);
}