注意:本文并非最好的解决办法,并且也不适用特殊情况,但是测试期间最快的解决办法。
大体思路:将SDL常用API渲染的结果保存为一张图,在OGL里作为背景texture载入。
作为一个SDL新手,就在不久前,我还以为openGL shader流程可以和SDL常用库混用。
比如用SDL_image.h里的API画几个图片API,需要带shader特效时就用openGL画,加到原画图流程中就行了。
比如原本用SDL_Image载入了一张背景图:
想在左上角添加上一个shader实现的漩涡
我之前是心想应该可以实现吧,不然这种情况下SDL不就啥用都没有了?
然而实际情况是这样的,原本的SDL渲染流程是创建一个renderer(封装了d3d/ogl渲染操作的抽象类),然后在循环中:
while(...)
{
...
SDL_RenderClear(renderer);
Game.Update(deltaTime);
SDL_RenderPresent(renderer);
...
}
然后如果你看了类似LazyFoo这样的在SDL中使用OGL教程:
https://lazyfoo.net/tutorials/OpenGL/34_glsl_texturing/index.php
那你可能会想我一样尝试将GL的渲染循环加上去,即:
SDL_RenderClear(renderer);
GAME.Update(deltaTime*3);
SDL_RenderPresent(renderer);
GLRender();
SDL_GL_SwapWindow(window);
这时候问题出现了。
这是可以理解的。依照上面的代码,我们会通过SDL_RenderPresent先画场景(接下来,场景就是SDL常用API画出来的结果),通过查看源码,我们发现RenderPresent里先是调用了d3d/ogl的实际渲染和swap函数画对渲染和swap到window上。 而下面GLRender两行也进行了清屏和渲染。
那我想,既然大家都是同一个window上的用gl画的,那我把SDL_RenderPresent里的swap去了,只留其中的渲染逻辑,然后GL_Render里的清屏去了,不就行了?
结果由于我是非源码版,既改不了SDL_RenderPresent里的逻辑,也没找到替代方案。不过,我发现了一个更严重的问题。
我的window是这样创建的:
window = SDL_CreateWindow(
gameConfig["windowTitle"].c_str(), // window title
SDL_WINDOWPOS_UNDEFINED, // initial x position
SDL_WINDOWPOS_UNDEFINED, // initial y position
windowW, // width, in pixels
windowH, // height, in pixels
SDL_WINDOW_OPENGL // flags - see below
);
很明显最后一个参数,我以为SDL会通过openGL渲染窗口,然而通过renderDoc抓帧发现实际使用d3d9渲染的场景!
真有你的,这样即使是源码版,我也别想从window里拿到它画场景用的ogl_context了,除非我还查查哪里导致了它不用ogl渲染。并且,既然SDL内部是用d3d9渲染的,那ogl可能也是老旧版本,就别想用新ogl的特性了?
emmm,我心有不甘,让我直接放弃已有的API,用ogl自己造一堆轮子着实太浪费时间了。虽然各种搜索表示,要用ogl shader,那SDL就只能用最基本的窗口和按键API了,但我觉得可以抢救一下。
奥卡姆剃刀吧。受最近微信小游戏的开发影响,我一下子就想到了先截屏,然后再当texture给opengl渲染的办法。
讲道理,每帧用一个方法把d3d9渲染的结果存到一个SDL_Surface里,然后绑到gl_texture上就行了,类似:
glTexImage2D(GL_TEXTURE_2D, 0, Mode, Surface->w, Surface->h, 0, Mode, GL_UNSIGNED_BYTE, Surface->pixels);
不过我从网上参考的这段保存截屏图片的代码,写进surface的格式应该不符合glTexImage2D要求的格式,所以直接拿来用会在
glTexImage2D里崩。
bool saveScreenshot(std::string filepath, SDL_Window* SDLWindow, SDL_Renderer* SDLRenderer) {
SDL_Surface* saveSurface = NULL;
SDL_Surface* infoSurface = NULL;
infoSurface = SDL_GetWindowSurface(SDLWindow);
if (infoSurface == NULL) {
std::cerr << "Failed to create info surface from window in saveScreenshotBMP(string), SDL_GetError() - " << SDL_GetError() << "\n";
}
else {
unsigned char * pixels = new (std::nothrow) unsigned char[infoSurface->w * infoSurface->h * infoSurface->format->BytesPerPixel];
if (pixels == 0) {
std::cerr << "Unable to allocate memory for screenshot pixel data buffer!\n";
return false;
}
else {
if (SDL_RenderReadPixels(SDLRenderer, &infoSurface->clip_rect, infoSurface->format->format, pixels, infoSurface->w * infoSurface->format->BytesPerPixel) != 0) {
std::cerr << "Failed to read pixel data from SDL_Renderer object. SDL_GetError() - " << SDL_GetError() << "\n";
pixels = NULL;
return false;
}
else {
saveSurface = SDL_CreateRGBSurfaceFrom(pixels, infoSurface->w, infoSurface->h, infoSurface->format->BitsPerPixel, infoSurface->w * infoSurface->format->BytesPerPixel, infoSurface->format->Rmask, infoSurface->format->Gmask, infoSurface->format->Bmask, infoSurface->format->Amask);
if (saveSurface == NULL) {
std::cerr << "Couldn't create SDL_Surface from renderer pixel data. SDL_GetError() - " << SDL_GetError() << "\n";
return false;
}
SDL_SaveBMP(saveSurface, filepath.c_str());
//IMG_SavePNG(saveSurface, filepath.c_str());
SDL_FreeSurface(saveSurface);
saveSurface = NULL;
}
delete[] pixels;
}
SDL_FreeSurface(infoSurface);
infoSurface = NULL;
}
return true;
}
然鹅,直接读取图片生成的surface在glTexImage2D里就行:
SDL_Surface* Surface = IMG_Load("D:/HumanTree/1.jpg");
那我再次奥卡姆剃刀,代码也不改了。干脆就每帧保存图片读取图片,然后如果帧数能过得去就行了。
结果还真过得去,不过前提是用上面的函数,可以看见里面是截屏保存了bmp,我试过换成png,效率低得多,帧数不行。
不过bmp保存和读取的时候,rgb的顺序是反的,得在shader处理一下:
vec4 finalColor = texture(tex, uv0);\
gl_FragColor = vec4(finalColor.b,finalColor.g,finalColor.r,1.0f); \
至此,我们通过
“SDL原生d3d9渲染场景-》
d3d9渲染截屏,但不swap到window-》
读取截屏图片,设置到ogl贴图。”
的流程,实现了ogl中渲染SDL常用API和shader结合的过程。
虽然目前只是做到了ogl shader应用到了d3d渲染的背景图,如图:
想要的效果其实是背景图不用漩涡shader,而在左上角贴一个有漩涡效果的图片。
但我觉得接下来是纯ogl过程,在已有背景贴图的情况下再画加上几个不同shader画的其他图片应该是可行的。
如果成功了,会有 “SDL常用API与OpenGL Shader混用方法(二)篇”