SDL2: 生成完全透明的纹理

16
如何使用SDL_CreateTexture创建透明的纹理?默认情况下,我使用以下代码创建纹理:

SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA8888,SDL_TEXTUREACCESS_TARGET, x, y);

然后我使用重定向输出到此纹理,对其进行着色。但是最终,在屏幕上渲染时任何(未更新的)像素都是黑色。

我尝试过不同的方法,包括使用:

 SDL_RenderClear(_Renderer);

我尝试过使用不同的混合模式,在透明矩形上绘制和创建纹理甚至进行绘画,但是结果仍然是不透明的纹理 :/

   SDL_Rect rect={0,0,Width,Height};
   SDL_SetRenderDrawBlendMode(_Renderer,SDL_BLENDMODE_BLEND);
   SDL_SetRenderDrawColor(_Renderer,255,255,255,0);
   SDL_RenderFillRect(_Renderer,&rect);

更具体地说:

    //this->texDefault.get()->get() = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA8888,SDL_TEXTUREACCESS_TARGET, x, y);
    SDL_SetRenderTarget(_Renderer.get()->get(), this->texDefault.get()->get());
    SDL_SetRenderDrawBlendMode(this->_Renderer.get()->get(),SDL_BLENDMODE_NONE);
    SDL_SetRenderDrawColor(this->_Renderer.get()->get(),255,0,255,0);
    SDL_RenderClear(this->_Renderer.get()->get());
    //SDL_Rect rect={0,0,Width,Height};
    //SDL_SetRenderDrawColor(this->_Renderer.get()->get(),255,255,255,255);
    //SDL_RenderFillRect(this->_Renderer.get()->get(),&rect);
    //SDL_RenderClear(this->_Renderer.get()->get());
    //SDL_SetRenderDrawBlendMode(this->_Renderer.get()->get(),SDL_BLENDMODE_NONE);
    SDL_SetRenderTarget(_Renderer.get()->get(), NULL);
    SDL_Rect rect= {relTop+Top,relLeft+Left,Height,Width};
    SDL_SetRenderDrawBlendMode(this->_Renderer.get()->get(),SDL_BLENDMODE_BLEND);
    SDL_RenderCopy(this->_Renderer.get()->get(), this->texDefault->get(), NULL, &rect);

无论我设置什么混合和透明度,这段代码始终产生非透明纹理。

结果是:

输入图像说明

也许有其他简单的方法在SDL2中创建透明的空纹理,例如x / y大小的完全透明png,但是加载此类图像文件有点毫无意义:/


你可以使用SDL_Surface和SDL_SetAlpha()来实现透明度(或半透明)。链接 - Mahesh Bansod
这与OpenGL有什么关系?它可能是建立在OpenGL之上的,但这里没有任何可以使用与OpenGL相关的东西来回答的内容 :-\ - Andon M. Coleman
2个回答

17
  • 首先,您需要设置渲染器混合模式:SDL_SetRenderDrawBlendMode(renderer,SDL_BLENDMODE_BLEND);
  • 其次,您需要设置纹理混合模式:SDL_SetTextureBlendMode(textures [i],SDL_BLENDMODE_BLEND);

这是我创建的可工作示例。 您可以使用键A和S更改第三个纹理的alpha通道,该通道在应用程序启动时处于不可见状态。

#include <iostream>
#include <vector>
#include <SDL.h>

void fillTexture(SDL_Renderer *renderer, SDL_Texture *texture, int r, int g, int b, int a)
{
    SDL_SetRenderTarget(renderer, texture);
    SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_NONE);
    SDL_SetRenderDrawColor(renderer, r, g, b, a);
    SDL_RenderFillRect(renderer, NULL);
}

void prepareForRendering(SDL_Renderer *renderer)
{
    SDL_SetRenderTarget(renderer, NULL);
    SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_BLEND);
    SDL_SetRenderDrawColor(renderer, 128, 128, 128, 255);
}

void checkSdlError()
{
    const char *sdlError = SDL_GetError();
    if(sdlError && *sdlError)
    {
        ::std::cout << "SDL ERROR: " << sdlError << ::std::endl;
    }
}

int main(int argc, char *argv[])
{
    SDL_Init(SDL_INIT_VIDEO | SDL_INIT_HAPTIC);

    SDL_Window *window = SDL_CreateWindow("SDL test",
        SDL_WINDOWPOS_CENTERED,
        SDL_WINDOWPOS_CENTERED,
        320, 240,
        SDL_WINDOW_OPENGL);
    SDL_Renderer *renderer = SDL_CreateRenderer(
        window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_TARGETTEXTURE);

    const int width = 50;
    const int height = 50;

    ::std::vector<SDL_Texture*> textures;

    SDL_Texture *redTexture = SDL_CreateTexture(renderer,
        SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, width, height);
    textures.push_back(redTexture);

    SDL_Texture *greenTexture = SDL_CreateTexture(renderer,
        SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, width, height);
    textures.push_back(greenTexture);

    SDL_Texture *purpleTexture = SDL_CreateTexture(renderer,
        SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, width, height);
    textures.push_back(purpleTexture);

    // Here is setting the blend mode for each and every used texture:
    for(int i = 0; i < textures.size(); ++i)
    {
        SDL_SetTextureBlendMode(textures[i], SDL_BLENDMODE_BLEND);
    }

    int purpleAlpha = 0;

    fillTexture(renderer, redTexture, 255, 0, 0, 255);
    fillTexture(renderer, greenTexture, 0, 255, 0, 128);
    fillTexture(renderer, purpleTexture, 255, 0, 255, purpleAlpha);

    prepareForRendering(renderer);

    bool running = true;
    while(running)
    {
        SDL_Rect rect;
        rect.w = width;
        rect.h = height;

        SDL_RenderClear(renderer);

        rect.x = 50;
        rect.y = 50;
        SDL_RenderCopy(renderer, redTexture, NULL, &rect);

        rect.x = 75;
        rect.y = 70;
        SDL_RenderCopy(renderer, greenTexture, NULL, &rect);

        rect.x = 75;
        rect.y = 30;
        SDL_RenderCopy(renderer, purpleTexture, NULL, &rect);

        SDL_RenderPresent(renderer);

        // Process events
        {
            SDL_Event event;
            while(SDL_PollEvent(&event) == 1)
            {
                if(event.type == SDL_QUIT)
                {
                    running = false;
                }
                else if(event.type == SDL_KEYDOWN)
                {
                    switch(event.key.keysym.sym)
                    {
                    case SDLK_ESCAPE:
                        running = false;
                        break;
                    case SDLK_a:
                        purpleAlpha = ::std::max(purpleAlpha - 32, 0);
                        fillTexture(renderer, purpleTexture, 255, 0, 255, purpleAlpha);
                        prepareForRendering(renderer);
                        ::std::cout << "Alpha: " << purpleAlpha << ::std::endl;
                        break;
                    case SDLK_s:
                        purpleAlpha = ::std::min(purpleAlpha + 32, 255);
                        fillTexture(renderer, purpleTexture, 255, 0, 255, purpleAlpha);
                        prepareForRendering(renderer);
                        ::std::cout << "Alpha: " << purpleAlpha << ::std::endl;
                        break;
                    }
                }
            }

            checkSdlError();
        }
    }

    for(int i = 0; i < textures.size(); ++i)
    {
        SDL_DestroyTexture(textures[i]);
    }
    textures.clear();

    SDL_DestroyRenderer(renderer);
    renderer = NULL;
    SDL_DestroyWindow(window);
    window = NULL;

    SDL_Quit();

    checkSdlError();

    return 0;
}

注释:编辑:完全重写了答案,原始的答案基本上包含了渲染器的混合模式。

1
@Lemonek 我终于有点时间研究这个问题了,我发现你不仅需要为渲染器设置混合模式,还需要为纹理设置。我相应地更改了这个答案并添加了工作示例。 - user1476710
很好 - 它开始工作了 - 我不知道我需要额外设置混合模式纹理,独立于渲染器混合模式。这适用于使用标志SDL_TEXTUREACCESS_TARGET创建的所有纹理,有没有一种简单的方法可以在此标志和SDL_TEXTUREACCESS_STREAMING之间切换? 以便能够渲染到纹理,然后使用SDL_TextureLock访问它?顺便说一句 - 谢谢您的支持。 - Lemonek
@Lemonek 嘿,我之前也不知道,直到我写了那段代码,它没有工作并且我在文档中注意到了这一点 - http://wiki.libsdl.org/FrontPage ... 关于切换访问标志 - 首先,我想再次指出我的SDL2经验为零,目前您可以添加上面的程序。无论如何,从文档和谷歌上看,我认为这是不可能的。然而,有多种方法可以实现您想要的内容; - user1476710
@Lemonek 首先,我想建议使用SDL_CreateTextureFromSurface,但我发现您无法设置访问标志(这很愚蠢!)。 无论如何,第一种解决方案是使用SDL_TextureLock编写所需数据,以便首先创建为_STREAM。 另一个解决方案是直接使用渲染器,然后使用SDL_RenderReadPixels读取数据,然后清除它并呈现要显示的内容。 这个解决方案的另一个版本是为窗口创建两个渲染器-第一个将被隐藏,您可以将其用于屏幕外呈现(我建议使用之前的)。 - user1476710
@Lemonek,恐怕我暂时没有时间深入研究这个问题。所以我建议你在SDL论坛上提问(正如你所看到的,在stackoverflow上并没有太多有SDL经验的人)...顺便说一句,虽然我不知道为什么SDL_RenderReadPixels在渲染器重定向到纹理时不能工作,但我打算在没有重定向的情况下使用它。 - user1476710
显示剩余5条评论

1

你可以像@user1476710建议的那样,在纹理上方渲染一个半透明矩形。然而,更直接的方法是仅为纹理启用alpha混合,将纹理alpha设置为所需值,然后进行渲染。例如,要呈现alpha = 127的半透明纹理:

...
SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_BLEND);
texture_rect=(SDL_Rect){...};
...
SDL_SetTextureAlphaMod(texture, 127);
SDL_RenderCopy(renderer, texture, NULL, texture_rect);
SDL_RenderPresent(renderer);
...

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接