我正在学习 SDL2,但我也在使用使用 OpenGL 调用的 imgui 库。从我在网上阅读的各种博客来看,我无法轻松混合 SDL2 渲染器和 OpenGL 调用;我要么使用其中之一。
我读过的大多数教程都使用渲染器,所以我不太明白如何在没有渲染器的情况下使用SDL2来绘制图元或绘制精灵。
以此为例:http://lazyfoo.net/tutorials/SDL/11_clip_rendering_and_sprite_sheets/index.php
他创建了 SDL 渲染器:
gRenderer = SDL_CreateRenderer( gWindow, -1, SDL_RENDERER_ACCELERATED );
然后他用它来渲染图像:
void LTexture::render( int x, int y, SDL_Rect* clip )
{
//Set rendering space and render to screen
SDL_Rect renderQuad = { x, y, mWidth, mHeight };
//Set clip rendering dimensions
if( clip != NULL )
{
renderQuad.w = clip->w;
renderQuad.h = clip->h;
}
//Render to screen
SDL_RenderCopy( gRenderer, mTexture, clip, &renderQuad );
}
然后他调用他的
render()
从 spritesheet 中绘制图像:
//Clear screen
SDL_SetRenderDrawColor( gRenderer, 0xFF, 0xFF, 0xFF, 0xFF );
SDL_RenderClear( gRenderer );
//Render top left sprite
gSpriteSheetTexture.render( 0, 0, &gSpriteClips[ 0 ] );
//Render top right sprite
gSpriteSheetTexture.render( SCREEN_WIDTH - gSpriteClips[ 1 ].w, 0, &gSpriteClips[ 1 ] );
//Render bottom left sprite
gSpriteSheetTexture.render( 0, SCREEN_HEIGHT - gSpriteClips[ 2 ].h, &gSpriteClips[ 2 ] );
//Render bottom right sprite
gSpriteSheetTexture.render( SCREEN_WIDTH - gSpriteClips[ 3 ].w, SCREEN_HEIGHT - gSpriteClips[ 3 ].h, &gSpriteClips[ 3 ] );
//Update screen
SDL_RenderPresent( gRenderer );
我明白发生了什么,但现在我无法使用 SDL 渲染器,我该如何完成同样的任务?我现在不能使用任何SDL2绘图功能吗?我是否只使用原始 OpenGL 调用,并且只使用 SDL 进行键盘绑定?
基本上,如果我无法使用 SDL 渲染器,我不明白如何使用 SDL2 来绘制东西(就像在精灵示例中),因为它似乎与我正在使用的 GUI 库不兼容,它使用 OpenGL 调用进行渲染。
我现在不能使用任何SDL2绘图功能吗?我是否只使用原始 opengl 调用,并且只使用 SDL 进行键盘绑定?
正确。
为了直接使用 OpenGL API,必须使用标志
SDL_WINDOW_OPENGL
创建 SDL 窗口,并为其创建一个新的 OpenGL 上下文:
auto window = SDL_CreateWindow("Window name",
SDL_WINDOWPOS_UNDEFINED,
SDL_WINDOWPOS_UNDEFINED,
width, height, SDL_WINDOW_OPENGL | SDL_WINDOW_FULLSCREEN);
auto context = SDL_GL_CreateContext(window);
如果成功,只需像在任何其他上下文处理程序中一样执行对 OpenGL 的调用。问题中链接的同一网站提供了使用 OpenGL 与 SDL 2 的教程,包括legacy 和modern。请注意,两者都不涉及创建 SDL 渲染器。 事实上,使用它是不合适的,因为这两个 API 是互斥的:您应该使用其中之一。 这个答案快速解释了为什么你不应该混合它们。
尽管如此,有一些方法可以使用 OpenGL 从某些 SDLRenderer 调用中实现相同的输出(因为相同的渲染器具有 OpenGL 实现!),但这自然是特定于具体情况的。想画一个简单的矩形吗?传统的
glBegin
和 glEnd
方法可能就足够了。想画精灵吗?您必须首先将其发送到 OpenGL 设备的内存。一旦了解了 OpenGL 的相关知识,制作合适的包装层通常是合适的。
如果您想使用依赖于 OpenGL 的现有库,替换其他绘图程序以使用此 API(或实用程序库,例如 GLU)是正确的方法。
另请注意,SDL 渲染器不必依赖于 OpenGL(它可能在 Windows 上使用 Direct3D)。
如果我理解您想知道如何在不使用 SDL 函数的情况下渲染到 SDL 屏幕,那么您想使用 opengl 函数。 SDL 支持 opengl,要使用 SDL 使用 opengl 渲染内容,您必须遵循以下最低步骤:在创建窗口之前,调用
SDL_GL_SetAttribute
来设置 OpenGL 属性,然后使用 SDL_WINDOW_OPENGL
标志创建窗口,之后您必须创建 opengl 上下文(每个 gl 应用程序都有一个)调用 SDL_GL_CreateContext
,请参阅 SDL Wiki 了解我提到的功能:CategoryVideo - SDL Wiki
这里有一段代码作为示例:
void start(SDL_Window** window, SDL_GLContext* gl){
if(SDL_Init(SDL_INIT_VIDEO) < 0){
throw std::runtime_error(SDL_GetError());
}
//set the gl version to be loaded
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 1);
*window = SDL_CreateWindow("Widget Tests", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, WINDOW_WIDTH, WINDOW_HEIGHT, SDL_WINDOW_OPENGL);
if(*window == nullptr){
throw std::runtime_error(SDL_GetError());
}
//create the opengl context
*gl = SDL_GL_CreateContext(*window);
if(*gl == nullptr){
throw std::runtime_error(SDL_GetError());
}
}
int main(){
SDL_Window* window;
SDL_GLContext gl;
...
//init sdl and opengl
start(&window, &gl);
glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
glMatrixMode(GL_PROJECTION_MATRIX);
glLoadIdentity();
glMatrixMode(GL_MODELVIEW_MATRIX);
glLoadIdentity();
glViewport(0, 0, WINDOW_WIDTH, WINDOW_HEIGHT);
while(running){
while(SDL_PollEvent(&event)){
switch(event.type){
...
}
}
glClear(GL_COLOR_BUFFER_BIT);
//... draw calls with opengl
SDL_GL_SwapWindow(window);//update the window
}
}
自 2021 年 10 月 12 日起 亲爱的 ImGui 现在正式支持 SDL_Renderer 后端 您需要版本 >= 1.85 且 SDL 版本 >= 2.0.18+
如果您想遵循基于 SDL_Renderer 的教程,请使用它。
另外作为一个技术问题,如果您使用 OpenGL 后端,您仍然可以使用 SDL_Renderer 渲染到离屏渲染目标,然后将其作为 OpenGL 纹理上传到 OpenGL。 Dear ImGui 通过精确地执行此一般过程来提供多个后端。
或者,您可以直接写入 OpenGL 帧缓冲区(不需要纹理)。对于初学者教程,只需使用 >v1.85 和 SDL_Renderer 即可。
由于您使用的是 ImGui,因此您不需要为 2D 游戏使用 SDL 渲染器或 OpenGL 绘图函数。 ImGui 有适合您的功能。这就是您使用 ImGui 的原因,对吗?制作一个全屏窗口并获取其绘制列表,并使用
AddImage()
等功能来绘制图像。有关其他类似功能,请参阅 imgui.h。
// Create a full screen window
ImGuiIO& io = ImGui::GetIO();
const ImGuiWindowFlags mainFlags = ImGuiWindowFlags_NoResize
| ImGuiWindowFlags_NoCollapse
| ImGuiWindowFlags_NoTitleBar
| ImGuiWindowFlags_NoScrollbar
| ImGuiWindowFlags_NoBackground
| ImGuiWindowFlags_NoMove
| ImGuiWindowFlags_NoSavedSettings
| ImGuiWindowFlags_NoBringToFrontOnFocus;
ImGui::SetNextWindowPos(ImVec2(0.0, 0.0));
ImGui::SetNextWindowSize(ImVec2(io.DisplaySize.x, io.DisplaySize.y));
ImGui::Begin("Game scene", NULL, mainFlags);
// Get draw list
ImDrawList* draw_list = ImGui::GetWindowDrawList();
// Get viewport
ImVec2 canvasViewportStart = ImGui::GetCursorScreenPos();
// Load texture (to be implemented by you)
auto texture = loadTexture("texture.png");
// Draw the texture
draw_list->AddImage(texture, ImVec2(canvasViewportStart.x, canvasViewportStart.y), ImVec2(canvasViewportStart.x + 32, canvasViewportStart.y + 32));
// Handle mouse input
if (ImGui::IsWindowHovered()) {
const ImVec2 mousePosAbs = ImGui::GetMousePos();
const ImVec2 mousePos = ImVec2(mousePosAbs.x - canvasViewportStart.x, mousePosAbs.y - canvasViewportStart.y);
if (ImGui::IsMouseClicked(ImGuiMouseButton_Left)) {
// ...
}
}
ImGui::End();
// Create other windows, call ImGui::Render()
如果您想直接使用 OpenGL 绘图函数(也许是 3D 游戏),那也是可能的。在这种情况下,您不应为场景创建 ImGui 窗口。在渲染 ImGui 窗口之前渲染场景。将代码放在 ImGui OpenGL 示例中的渲染调用之间:
ImGui::Render();
// Clear the screen
ImGuiIO& io = ImGui::GetIO();
glViewport(0, 0, (int)io.DisplaySize.x, (int)io.DisplaySize.y);
glClearColor(0, 0, 0, 255);
glClear(GL_COLOR_BUFFER_BIT);
// Render your own game scene here
// Note: You don't need this if you are using ImGui draw functions
// Render ImGui windows
ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
SDL_GL_SwapWindow(window);