SDL2透明面
SDL2 Transparent surface
我正在尝试创建一个透明表面,将其 blit 到更大的表面(我的屏幕大小),然后创建它的纹理,复制到渲染器,最后渲染呈现。
我看到许多其他论坛说我必须使用 SetBlendMode(用于表面、纹理和渲染器)、ColorKey、SetSurfaceBlendMode。我都试过了,但我似乎无法让它工作。我读到 SDL_BlitScaled 不适合组合具有透明像素的表面,但我完全不知道我必须做什么。
我注意到,当我使用 SDL_CreateRGBSurface(带有 alpha 值)创建表面时,表面的 PixelFormat 是 RGB888 而不是 RGBA8888(这是我期望的,因为我提供了 alpha 值)。使用 SDL_ConvertSurfaceFormat 没有帮助。
有人能告诉我我错过了什么吗?
完整代码:已删除,我很抱歉
请注意,我已经删除了让透明度生效的尝试
渲染器:
mRenderer = SDL_CreateRenderer(mWindow, -1, SDL_RENDERER_ACCELERATED);
我的渲染循环:
void CApp::Render()
{
SDL_RenderClear(mRenderer);
mBackGround->Render(mRenderer);
mForeGround->Render(mRenderer);
SDL_RenderPresent(mRenderer);
}
我正在发送的大表面:
mSurface = SDL_CreateRGBSurface(0, CApp::Window_W(), CApp::Window_H(), 32, 0, 0, 0, 0);
从 spritesheet 获取图块的代码:
bool Map::GetTilesFromSpriteSheet(SDL_Surface *pSpriteSheet, int pTile_w, int pTile_h)
{
if(pSpriteSheet->w % pTile_w == 0 && pSpriteSheet->h % pTile_h == 0) {
SDL_Rect srcRect;
srcRect.w = pTile_w;
srcRect.h = pTile_h;
for(int y = 0; y < pSpriteSheet->h / pTile_h; y++) {
for(int x = 0; x < pSpriteSheet->w / pTile_w; x++) {
srcRect.x = x*pTile_w;
srcRect.y = y*pTile_h;
SDL_Surface* tempSurface = SDL_CreateRGBSurface(0, pTile_w, pTile_h, 32, 0, 0, 0, 0);
if(SDL_BlitSurface(pSpriteSheet, &srcRect, tempSurface, nullptr)==0) {
mTiles.push_back(tempSurface);
} else {
Log("Error extracting tile (%d,%d)(w,h): %s", x, y, SDL_GetError());
return false;
}
}
}
Log("Number of tiles: %d", static_cast<int>(mTiles.size()));
} else {
Log("Background spritesheet is incompatible with tile dimensions (%d,%d)(w,h).", pTile_w, pTile_h);
return false;
}
return true;
}
这是我组合瓷砖以创建大表面的地方:
bool Map::GenerateMap(std::vector<std::vector<int>> &pMap, std::vector<SDL_Surface*> &pTiles, SDL_Surface* pDestination)
{
SDL_Rect rect;
rect.w = mDstTile_W;
rect.h = mDstTile_H;
SDL_Surface* transparent = SDL_CreateRGBSurface(0, mDstTile_W, mDstTile_H, 32, 0, 0, 0, 0);
for(int y = 0; y < static_cast<int>(pMap.size()); y++) {
for(int x = 0; x < static_cast<int>(pMap.at(static_cast<unsigned long>(y)).size()); x++) {
rect.x = x*mDstTile_W;
rect.y = y*mDstTile_H;
int index = static_cast<int>(pMap.at(static_cast<unsigned long>(y)).at(static_cast<unsigned long>(x)));
if(index < 0) {
if(SDL_BlitScaled(transparent, nullptr, pDestination, &rect) != 0) {
Log("Error blitting transparent surface to destination: %s", SDL_GetError());
}
} else if(SDL_BlitScaled(pTiles[static_cast<unsigned long>(index)],
nullptr, pDestination, &rect) != 0) {
Log("Error blitting surface to destination: %s", SDL_GetError());
return false;
}
}
}
SDL_FreeSurface(transparent);
return true;
}
最后,这是将大表面渲染到屏幕的代码,首先是创建纹理:
void ForeGround::Render(SDL_Renderer* pRenderer)
{
if(mTexture) SDL_DestroyTexture(mTexture);
mTexture = SDL_CreateTextureFromSurface(pRenderer, mSurface);
if(mTexture == nullptr) {
Log("Unable to create foreground texture: %s", SDL_GetError());
} else if (SDL_RenderCopy(pRenderer, mTexture, nullptr, nullptr)) {
Log("Unable to render foreground: %s", SDL_GetError());
}
}
如@keltar 所述,我没有创建具有 alpha 值的表面。
我不得不更改 SDL_CreateRGBSurface
调用。下面的代码片段创建了一个空的、透明的表面,如果需要我可以在其中进行 blit(例如,spritesheet 中的图块)。
SDL_Surface* tempSurface = SDL_CreateRGBSurface(..., 32, 0xff, 0xff00, 0xff0000, 0xff000000);
下面的代码片段创建了一个黑色、不透明的表面。
SDL_Surface* tempSurface = SDL_CreateRGBSurface(..., 32, 0xff, 0xff00, 0xff0000, 0x00000000);
这对我来说已经足够了,我不必必须使用SDL_SetRenderDrawBlendMode
、SDL_SetSurfaceBlendMode
、SDL_SetTextureBlendMode
或SDL_ConvertSurface
.快速查看 SDL wiki 显示:
By default surfaces with an alpha mask are set up for blending as with
SDL_SetSurfaceBlendMode(surface, SDL_BLENDMODE_BLEND)
这解释了为什么我不必调用任何这些函数。非常感谢 keltar 的快速回答!
我正在尝试创建一个透明表面,将其 blit 到更大的表面(我的屏幕大小),然后创建它的纹理,复制到渲染器,最后渲染呈现。
我看到许多其他论坛说我必须使用 SetBlendMode(用于表面、纹理和渲染器)、ColorKey、SetSurfaceBlendMode。我都试过了,但我似乎无法让它工作。我读到 SDL_BlitScaled 不适合组合具有透明像素的表面,但我完全不知道我必须做什么。
我注意到,当我使用 SDL_CreateRGBSurface(带有 alpha 值)创建表面时,表面的 PixelFormat 是 RGB888 而不是 RGBA8888(这是我期望的,因为我提供了 alpha 值)。使用 SDL_ConvertSurfaceFormat 没有帮助。
有人能告诉我我错过了什么吗?
完整代码:已删除,我很抱歉
请注意,我已经删除了让透明度生效的尝试
渲染器:
mRenderer = SDL_CreateRenderer(mWindow, -1, SDL_RENDERER_ACCELERATED);
我的渲染循环:
void CApp::Render()
{
SDL_RenderClear(mRenderer);
mBackGround->Render(mRenderer);
mForeGround->Render(mRenderer);
SDL_RenderPresent(mRenderer);
}
我正在发送的大表面:
mSurface = SDL_CreateRGBSurface(0, CApp::Window_W(), CApp::Window_H(), 32, 0, 0, 0, 0);
从 spritesheet 获取图块的代码:
bool Map::GetTilesFromSpriteSheet(SDL_Surface *pSpriteSheet, int pTile_w, int pTile_h)
{
if(pSpriteSheet->w % pTile_w == 0 && pSpriteSheet->h % pTile_h == 0) {
SDL_Rect srcRect;
srcRect.w = pTile_w;
srcRect.h = pTile_h;
for(int y = 0; y < pSpriteSheet->h / pTile_h; y++) {
for(int x = 0; x < pSpriteSheet->w / pTile_w; x++) {
srcRect.x = x*pTile_w;
srcRect.y = y*pTile_h;
SDL_Surface* tempSurface = SDL_CreateRGBSurface(0, pTile_w, pTile_h, 32, 0, 0, 0, 0);
if(SDL_BlitSurface(pSpriteSheet, &srcRect, tempSurface, nullptr)==0) {
mTiles.push_back(tempSurface);
} else {
Log("Error extracting tile (%d,%d)(w,h): %s", x, y, SDL_GetError());
return false;
}
}
}
Log("Number of tiles: %d", static_cast<int>(mTiles.size()));
} else {
Log("Background spritesheet is incompatible with tile dimensions (%d,%d)(w,h).", pTile_w, pTile_h);
return false;
}
return true;
}
这是我组合瓷砖以创建大表面的地方:
bool Map::GenerateMap(std::vector<std::vector<int>> &pMap, std::vector<SDL_Surface*> &pTiles, SDL_Surface* pDestination)
{
SDL_Rect rect;
rect.w = mDstTile_W;
rect.h = mDstTile_H;
SDL_Surface* transparent = SDL_CreateRGBSurface(0, mDstTile_W, mDstTile_H, 32, 0, 0, 0, 0);
for(int y = 0; y < static_cast<int>(pMap.size()); y++) {
for(int x = 0; x < static_cast<int>(pMap.at(static_cast<unsigned long>(y)).size()); x++) {
rect.x = x*mDstTile_W;
rect.y = y*mDstTile_H;
int index = static_cast<int>(pMap.at(static_cast<unsigned long>(y)).at(static_cast<unsigned long>(x)));
if(index < 0) {
if(SDL_BlitScaled(transparent, nullptr, pDestination, &rect) != 0) {
Log("Error blitting transparent surface to destination: %s", SDL_GetError());
}
} else if(SDL_BlitScaled(pTiles[static_cast<unsigned long>(index)],
nullptr, pDestination, &rect) != 0) {
Log("Error blitting surface to destination: %s", SDL_GetError());
return false;
}
}
}
SDL_FreeSurface(transparent);
return true;
}
最后,这是将大表面渲染到屏幕的代码,首先是创建纹理:
void ForeGround::Render(SDL_Renderer* pRenderer)
{
if(mTexture) SDL_DestroyTexture(mTexture);
mTexture = SDL_CreateTextureFromSurface(pRenderer, mSurface);
if(mTexture == nullptr) {
Log("Unable to create foreground texture: %s", SDL_GetError());
} else if (SDL_RenderCopy(pRenderer, mTexture, nullptr, nullptr)) {
Log("Unable to render foreground: %s", SDL_GetError());
}
}
如@keltar 所述,我没有创建具有 alpha 值的表面。
我不得不更改 SDL_CreateRGBSurface
调用。下面的代码片段创建了一个空的、透明的表面,如果需要我可以在其中进行 blit(例如,spritesheet 中的图块)。
SDL_Surface* tempSurface = SDL_CreateRGBSurface(..., 32, 0xff, 0xff00, 0xff0000, 0xff000000);
下面的代码片段创建了一个黑色、不透明的表面。
SDL_Surface* tempSurface = SDL_CreateRGBSurface(..., 32, 0xff, 0xff00, 0xff0000, 0x00000000);
这对我来说已经足够了,我不必必须使用SDL_SetRenderDrawBlendMode
、SDL_SetSurfaceBlendMode
、SDL_SetTextureBlendMode
或SDL_ConvertSurface
.快速查看 SDL wiki 显示:
By default surfaces with an alpha mask are set up for blending as with
SDL_SetSurfaceBlendMode(surface, SDL_BLENDMODE_BLEND)
这解释了为什么我不必调用任何这些函数。非常感谢 keltar 的快速回答!