SDL_Image,d3d9与OpenGL Shader混用方法(一)

前言

注意:本文并非最好的解决办法,并且也不适用特殊情况,但是测试期间最快的解决办法。

大体思路:将SDL常用API渲染的结果保存为一张图,在OGL里作为背景texture载入。

背景

作为一个SDL新手,就在不久前,我还以为openGL shader流程可以和SDL常用库混用。

比如用SDL_image.h里的API画几个图片API,需要带shader特效时就用openGL画,加到原画图流程中就行了。

比如原本用SDL_Image载入了一张背景图:

想在左上角添加上一个shader实现的漩涡

SDL_Image,d3d9与OpenGL Shader混用方法(一)_第1张图片

我之前是心想应该可以实现吧,不然这种情况下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);

这时候问题出现了。

问题描述

问题一:场景与GL 交替闪烁

这是可以理解的。依照上面的代码,我们会通过SDL_RenderPresent先画场景(接下来,场景就是SDL常用API画出来的结果),通过查看源码,我们发现RenderPresent里先是调用了d3d/ogl的实际渲染和swap函数画对渲染和swap到window上。 而下面GLRender两行也进行了清屏和渲染。

那我想,既然大家都是同一个window上的用gl画的,那我把SDL_RenderPresent里的swap去了,只留其中的渲染逻辑,然后GL_Render里的清屏去了,不就行了?

问题二:非源码版SDL的困扰

结果由于我是非源码版,既改不了SDL_RenderPresent里的逻辑,也没找到替代方案。不过,我发现了一个更严重的问题。

问题三:SDL骗人的OpenGL窗口

我的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渲染的背景图,如图:

SDL_Image,d3d9与OpenGL Shader混用方法(一)_第2张图片

想要的效果其实是背景图不用漩涡shader,而在左上角贴一个有漩涡效果的图片。

但我觉得接下来是纯ogl过程,在已有背景贴图的情况下再画加上几个不同shader画的其他图片应该是可行的。

如果成功了,会有 “SDL常用API与OpenGL Shader混用方法(二)篇”

你可能感兴趣的:(图形API)