SDL_TTF绘制垃圾内容

5
我前几天提了一个问题,关于使用SDL渲染TTF字体,然后被指向了SDL_TTF库。我尝试使用SDL_TTF库,但是屏幕上只有垃圾。我已经包含了非常简单的着色器和代码段来将文本加载到表面,并将其绑定到纹理。我并没有尝试做任何疯狂的事情。你能看到我做错了什么吗?我不太确定如何调试着色器等。

片段着色器(frag.glsl):

#version 330
in vec2 texCoord;
in vec4 fragColor;

out vec3 finalColor;

uniform sampler2D myTextureSampler;
void main() {
    finalColor = texture( myTextureSampler, texCoord ).rgb;
}

顶点着色器(vert.glsl)

#version 330

in vec3 vert;
in vec4 color;
in vec2 texcoord;

out vec4 fragColor;
out vec2 texCoord;

void main() {
    fragColor = color;
    gl_Position = vec4(vert, 1);
    texCoord = texcoord;
}

Font Loading (loadFont.cpp)

//Initialise TTF
if( TTF_Init() == -1 )
    throw std::runtime_error("SDL_TTF failed to initialise.");

//Load the texture
font = TTF_OpenFont( filePath.c_str(), 12 );
if(!font)
    throw std::runtime_error("Couldn't load: "+ filePath);

TTF_SetFontStyle(font, TTF_STYLE_NORMAL);

surface = TTF_RenderUTF8_Blended(font, "Hello", this->textColor);
Uint8 colors = surface->format->BytesPerPixel;
int texture_format;
if (colors == 4) {   // alpha
    if (surface->format->Rmask == 0x000000ff)
        texture_format = GL_RGBA;
    else
        texture_format = GL_BGRA;
} else {             // no alpha
    if (surface->format->Rmask == 0x000000ff)
        texture_format = GL_RGB;
    else
        texture_format = GL_BGR;
}
glGenTextures(1, &texture);
glBindTexture(GL_TEXTURE_2D, texture);

glTexImage2D(GL_TEXTURE_2D, 0, colors, surface->w, surface->h, 0,
             texture_format, GL_UNSIGNED_BYTE, surface->pixels);
SDL_FreeSurface(surface);

顶点属性设置

GLfloat vertices[] = {
    //X    Y      Z     R     G     B     A     U    V
    -1.0f, -1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.5f, 0.f, 1.f,
     1.0f, -1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.5f, 1.f, 1.f,
    -1.0f, -0.4f, 0.0f, 1.0f, 0.0f, 0.0f, 0.5f, 0.f, 0.f,
     1.0f, -1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.5f, 1.f, 1.f,
     1.0f, -0.4f, 0.0f, 1.0f, 0.0f, 0.0f, 0.5f, 1.f, 0.f,
    -1.0f, -0.4f, 0.0f, 1.0f, 0.0f, 0.0f, 0.5f, 0.f, 0.f

};


glGenVertexArrays(1, &_vao);
glBindVertexArray(_vao);

glGenBuffers(1, &_vbo);
glBindBuffer(GL_ARRAY_BUFFER, _vbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

glEnableVertexAttribArray(program->attrib("vert"));
glVertexAttribPointer(program->attrib("vert"), 3, GL_FLOAT, GL_FALSE, 9*sizeof(GLfloat), NULL);

glEnableVertexAttribArray(program->attrib("color"));
glVertexAttribPointer(program->attrib("color"), 4, GL_FLOAT, GL_TRUE,  9*sizeof(GLfloat), (const GLvoid*)(3 * sizeof(GLfloat)));

glEnableVertexAttribArray(program->attrib("texcoord"));
glVertexAttribPointer(program->attrib("texcoord"), 2, GL_FLOAT, GL_TRUE,  9*sizeof(GLfloat), (const GLvoid*)(7 * sizeof(GLfloat)));

我已经根据下面的评论附上了我正在使用的顶点属性代码。
编辑: 在一个已被删除的回复中,有人问SDL_TTF是否返回3个或4个通道。它返回一个BGRA图像。我尝试将我的片段着色器更改为:

片段着色器

#version 330
in vec2 texCoord;
in vec4 fragColor;

out vec4 finalColor;

uniform sampler2D myTextureSampler;
void main() {
    finalColor = texture( myTextureSampler, texCoord ).rgba;
}

注意vec4,并使用rgba而不是rgb。这只会导致黑色矩形。 我还尝试使用SDL_LoadBMP()生成表面,结果完全相同。

@AndonM.Coleman,你这个家伙,我刚刚也给你做了同样的事情。 - vallentin
2
请将您的代码添加到问题本身而不是外部服务中,因为如果您的代码从外部服务中删除,那么这个问题对于遇到类似问题的人来说将来就无法使用了。 - vallentin
@Vallentin: 是的,我非常抱歉。我们基本上同时进行了相同的编辑。如果您早些时候建议了它们,我会批准它们而不是自己完成它们。 - Andon M. Coleman
抱歉!我试图让我的问题尽可能简洁。 - Will P
你能把设置顶点属性指针的代码加到问题中吗?我无法想象是什么原因导致了这种柏林噪声效果,但也许再多一点代码会有所帮助。目前我怀疑可能是glTexImage2D(...)调用中的像素传输问题或者无效的顶点属性指针设置。 - Andon M. Coleman
显示剩余8条评论
2个回答

3

您对以下代码的调用:

glTexImage2D(GL_TEXTURE_2D, 0, colors, surface->w, surface->h, 0, texture_format, GL_UNSIGNED_BYTE, surface->pixels);

存在问题。

第三个参数是错误的:

http://www.opengl.org/sdk/docs/man/xhtml/glTexImage2D.xml

internalFormat(内部格式)

                Specifies the number of color components in the texture.
                Must be one of base internal formats given in Table 1,
                one of the sized internal formats given in Table 2, or one
                of the compressed internal formats given in Table 3, below.

我猜测您希望您的格式为GL_RGBA(或者您希望OpenGL将纹理存储在哪种格式中)。
编辑:
我刚刚看到,但是您在片段着色器中仅使用了3个通道。混合函数要求您使用4个通道,否则alpha通道将会出现问题。
我认为您的“主要”问题在其他地方,因为这只会使颜色在整个表面上保持恒定。(而不是您看到的“垃圾”)
我快速编写了这个程序,它大多数情况下都可以做到您正在做的事情。我认为这比我的存储库更有帮助,因为它直截了当。
#include <GL/glew.h>

#include <SDL2/SDL.h>
#include <SDL2/SDL_opengl.h>
#include <SDL2/SDL_ttf.h>

#include <string>
#include <iostream>

using namespace std;

SDL_Window *window = NULL;
SDL_GLContext context = NULL;
TTF_Font* font = NULL;
SDL_Surface* surface = NULL;

//OpenGL Objects
GLuint vao;
GLuint vbo;
GLuint texture;

//Shader Objects
GLuint program;
GLuint vs;
GLuint fs;

//Sampler Object
GLuint uniformSampler;

//Callback Function
APIENTRY GLvoid debugMessageCallbackFunction( GLenum source, GLenum type, GLuint id, GLenum severity,
                                 GLsizei length, const GLchar* message, GLvoid* userParam)
{
  cerr << endl << "\t" << message << endl;
}

//The shaders are identical to yours
const string fragmentShaderString = 
                "#version 130\n" // My laptop can't do OpenGL 3.3 so 3.0 will have to do
                "in vec2 texCoord;\n"
                "in vec4 fragColor;\n"
                "\n"
                "out vec4 finalColor;\n"
                "\n"
                "uniform sampler2D myTextureSampler;\n"
                "void main() {\n"
                "  finalColor = texture( myTextureSampler, texCoord ) * fragColor;\n"
                "}";

const string vertexShaderString = 
                "#version 130\n"
                "\n"
                "in vec3 vert;\n"
                "in vec4 color;\n"
                "in vec2 texcoord;\n"
                "\n"
                "out vec4 fragColor;\n"
                "out vec2 texCoord;\n"

                "void main() {\n"
                "  fragColor = color;\n"
                "  gl_Position = vec4(vert, 1);\n"
                "  texCoord = texcoord;\n"
                "}\n";

//Your vertices, but I changed alpha to 1.0f
const GLfloat vertices[] = 
{
    //X    Y      Z     R     G     B     A     U    V
    -1.0f, -1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.f, 1.f,
     1.0f, -1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.f, 1.f,
    -1.0f, -0.4f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.f, 0.f,
     1.0f, -1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.f, 1.f,
     1.0f, -0.4f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.f, 0.f,
    -1.0f, -0.4f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.f, 0.f
};

int main(int argc, char* args[])
{
  //Create Window and Context
  window = SDL_CreateWindow("SDL Text with OpenGL", 0, 0, 640, 480, SDL_WINDOW_OPENGL);

  //Set Core Context
  SDL_GL_SetAttribute( SDL_GL_CONTEXT_MAJOR_VERSION, 3 );
  SDL_GL_SetAttribute( SDL_GL_CONTEXT_MINOR_VERSION, 1 );
  SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);

  context = SDL_GL_CreateContext(window);

  //Simple OpenGL State Settings
  glViewport( 0.f, 0.f, 640.f, 480.f);
  glClearColor( 0.f, 0.f, 0.f, 1.f);

  //Init Glew
  //Set glewExperimental for Core Context
  glewExperimental=true;
  glewInit();

  //Set Blending 
  //Required so that the alpha channels show up from the surface
  glEnable(GL_BLEND);
  glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

  //Simple callback function for GL errors
  glDebugMessageCallbackARB(debugMessageCallbackFunction, NULL);

  //Create Shaders
  vs = glCreateShader(GL_VERTEX_SHADER);
  fs = glCreateShader(GL_FRAGMENT_SHADER);

  //Source Pointers
  const GLchar* vsSource= &vertexShaderString[0];
  const GLchar* fsSource = &fragmentShaderString[0];

  //Set Source
  glShaderSource(vs, 1, &vsSource, NULL);
  glShaderSource(fs, 1, &fsSource, NULL);

  //Compile Shaders
  glCompileShader(fs);
  glCompileShader(vs);

  //Create Shader Program
  program = glCreateProgram();

  //Attach Shaders to Program
  glAttachShader(program, vs);
  glAttachShader(program, fs);

  //No need for shaders anymore
  glDeleteShader(vs);
  glDeleteShader(fs);

  //Set Attribute Locations
  glBindAttribLocation(program, 0, "vert");
  glBindAttribLocation(program, 1, "color");
  glBindAttribLocation(program, 2, "texcoord");

  //Link Program
  glLinkProgram(program);

  //Setup VAO and VBO
  glGenVertexArrays(1, &vao);
  glGenBuffers(1, &vbo);

  glBindVertexArray(vao);
  glBindBuffer(GL_ARRAY_BUFFER, vbo);

  glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * 9 * 6, vertices, GL_STATIC_DRAW);

  glEnableVertexAttribArray(0);
  glEnableVertexAttribArray(1);
  glEnableVertexAttribArray(2);

  glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 9 * sizeof(GLfloat), NULL);
  glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, 9 * sizeof(GLfloat),(GLvoid*)(3*sizeof(GLfloat)));
  glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 9 * sizeof(GLfloat),(GLvoid*)(7*sizeof(GLfloat)));

  //Init TTF
  TTF_Init();

  //Open Font
  font = TTF_OpenFont("DroidSansFallbackFull.ttf", 30);

  SDL_Color color = {255, 255, 255, 255};

  //Create Surface
  surface = TTF_RenderUTF8_Blended(font, "This is TEXT!", color);

  //Your format checker
  GLenum format = (surface->format->BytesPerPixel==3)?GL_RGB:GL_RGBA;

  //Create OpenGL Texture
  glGenTextures(1, &texture);
  glBindTexture(GL_TEXTURE_2D, texture);
  glTexImage2D( GL_TEXTURE_2D, 0, format, surface->w, surface->h, 0, 
              format, GL_UNSIGNED_BYTE, surface->pixels);

  //Set Some basic parameters
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);

  //Set up Sampler
  glActiveTexture(GL_TEXTURE0);
  glBindTexture(GL_TEXTURE_2D, texture);

  uniformSampler = glGetUniformLocation(program, "myTextureSampler");
  //It defaults to using GL_TEXTURE0, so it's not necessary to set it
  //in this program it's generally a good idea.

  //-------------------------------------------------------------------------------------- 
  // DRAW STAGE
  //-------------------------------------------------------------------------------------- 

  glUseProgram(program);

  //glBindVertexArray(vao); - still in use

  glClear(GL_COLOR_BUFFER_BIT);

  glDrawArrays(GL_TRIANGLES, 0, 6);

  SDL_GL_SwapWindow(window);

  //Sleep for 2s before closing
  SDL_Delay(2000);
}

我没有做任何错误检查或关闭任何资源,因为它只是一个参考,而不是用来实际使用的。

通常我不使用glew,但编写代码手动获取这样一个小程序的函数似乎毫无意义。

它可以编译。

g++ source.cpp -g -lSDL2 -lSDL2_ttf -lGL -GLEW -o demo

在Linux上,您可能需要进行一些调整以适应Windows(头文件可能会稍微改变,库也会更改),而我认为在Mac上不需要更改即可正常工作。
编辑2:
要在Windows上使用mingw进行编译,您需要将APIENTRY添加到回调函数中,并且main函数应该有参数。已更改代码以反映此更改。
已测试并且可以在Windows和Linux上正常工作。(前提是您的实现可以访问GL_ARB_debug_callback扩展,如果不能,请注释掉它)

据我所知,你那里的代码看起来是正确的。你还可以绑定一些纹理参数,但我怀疑这不是你问题的原因。我还发现,在着色器属性索引规范中,通常比不断询问哪个索引绑定到该属性更容易管理(至少对我来说是这样),但这只是个人偏好。你确定你正确地绑定了采样器吗?你可以通过将纹理坐标和位置绘制为颜色来检查发送到GPU的数据是否正确。 - Xonar
所以我看了你的示例代码,感觉相当简单,但是你说的关于检查我的采样器似乎给了我正确的方向。我尝试过了,得到了相同的噪声,但是颜色却不同。深入挖掘后,除了顶点之外,我从我的VBO中取出了所有数据,并尝试将它们涂成红色,但没有成功。我重新编写了我的着色器(只是一个基本的着色器,输入顶点并输出颜色),并且正在重建它。我不知道实际问题是什么,但我会继续向你报告。谢谢你的帮助! - Will P
@WillP 我在编辑中添加了一个示例程序。如果您仍然有困难,也许您可以放置更多的代码。 - Xonar
@maccard,只有在创建核心上下文时才需要glewExperimental 。至少在Windows和Linux下,我没有发现SDL_TTF中的任何错误。您还可以明确指定采样器应该使用GL_TEXTURE0。我仍然无法复制出错的乱码输出。 - Xonar
@WillP 我已经修改了代码,使其能够在核心上下文中运行。在我的Linux机器上它完美无缺地工作。你可以测试一下我的示例,并报告是否仍然存在乱码输出。我认为这与SDL_TTF无关,因为无论上下文是核心还是非核心,对于SDL_TTF来说都没有影响。(也许是glew的问题) - Xonar
显示剩余5条评论

0

工作得很好,只需要编辑const GLfloat vertices[]数组即可始终更改文本颜色。对于纯色文本,请将数组中的所有RGB分量都设置为1.0f,并在color中呈现纹理。对于多彩文本,请先用SDL_Color color = { 255, 255, 255, 255 };将纹理呈现为白色,然后按下面所示编辑数组。

float width = (float)surface->w;
float height = (float)surface->h;

// alpha to 1.0f
const GLfloat vertices[] = {
    // X         Y          Z     R     G     B     A     U     V
    -1.0, -height / width, 1.0f, 1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f,
    1.0f, -height / width, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f,
    -1.0f, height / width, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f,
    1.0f, -height / width, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f,
    1.0f,  height / width, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f,
    -1.0f, height / width, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f
};

Multi-colored text rendered with SDL as texture in OpenGL


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