二维纹理之间的黑线

发布于 2025-01-18 12:15:46 字数 7643 浏览 6 评论 0 原文

我刚刚开始学习opengl技术。 我的程序绘制 2d 等距图块并输出以下内容: 在此处输入图像描述未知原因,当两个纹理重叠或两个纹理接触时会出现黑线。

代码示例:

typedef unsigned int ID;

class GraphicEngine {
public:
    GraphicEngine();
    ~GraphicEngine();
    void initShaders(const char* vertexShaderSource, const char* fragmentShaderSource);
    void initRenderData(float vertices[], unsigned int size);
    std::vector<ID> initTextures(std::vector<std::string>& paths);
    void drawTextures(std::vector<ID> testuresIds);

private:
    GraphicEngine(GraphicEngine&) = delete;
    GraphicEngine(GraphicEngine&&) = delete;
    GraphicEngine& operator=(const GraphicEngine& other) = delete;
private:
    unsigned int VBO = 0; 
    unsigned int VAO = 0;
    unsigned int EBO = 0;
    unsigned int shaderProgram;
};

GraphicEngine::GraphicEngine() {

}

GraphicEngine::~GraphicEngine() {
    glDeleteVertexArrays(1, &VAO);
    glDeleteBuffers(1, &VBO);
    glDeleteBuffers(1, &EBO);
}


void GraphicEngine::initShaders(const char* vertexShaderSource, const char* fragmentShaderSource) {
    glEnable(GL_BLEND);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    unsigned int vertexShader = glCreateShader(GL_VERTEX_SHADER);
    unsigned int fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
    shaderProgram = glCreateProgram();
    glShaderSource(vertexShader, 1, &vertexShaderSource, NULL);
    glCompileShader(vertexShader);
    glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL);
    glCompileShader(fragmentShader);
    glAttachShader(shaderProgram, vertexShader);
    glAttachShader(shaderProgram, fragmentShader);
    glLinkProgram(shaderProgram);
}

void GraphicEngine::initRenderData(float vertices[], unsigned int size) {
    unsigned int indices[] = {
        0, 1, 3,
        1, 2, 3
    };
    glGenVertexArrays(1, &VAO);
    glGenBuffers(1, &VBO);
    glGenBuffers(1, &EBO);

    glBindVertexArray(VAO);
    glBindBuffer(GL_ARRAY_BUFFER, VBO);
    glBufferData(GL_ARRAY_BUFFER, size, vertices, GL_STATIC_DRAW);

    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);

    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)0);
    glEnableVertexAttribArray(0);
    glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)(3 * sizeof(float)));
    glEnableVertexAttribArray(1);
}

std::vector<ID> GraphicEngine::initTextures(std::vector<std::string>& paths) {
    std::vector<ID> ids(paths.size());
    stbi_set_flip_vertically_on_load(true);
    for (int i = 0; i < paths.size(); i++) {
        unsigned int texture;
        glGenTextures(1, &ids[i]);
        glBindTexture(GL_TEXTURE_2D, ids[i]);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
        int width, height, nrChannels;
        unsigned char* data = stbi_load(paths[i].c_str(), &width, &height, &nrChannels, STBI_rgb_alpha);
        if (data)
        {
            glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
            glGenerateMipmap(GL_TEXTURE_2D);
        }
        stbi_image_free(data);
    }
    return ids;
}

void GraphicEngine::drawTextures(std::vector<ID> testuresIds) {
    static bool ex = false;
    for (auto testureId : testuresIds) {
        for (int i = 0; i < 4; i++) {
            glBindTexture(GL_TEXTURE_2D, testureId);
            glm::mat4 transform = glm::mat4(1.0f);
            transform = glm::translate(transform, glm::vec3(i * 0.6f + 0.0f, 0.0f, 0.0f));
            glUseProgram(shaderProgram);
            unsigned int transformLoc = glGetUniformLocation(shaderProgram, "transform");
            glUniformMatrix4fv(transformLoc, 1, GL_FALSE, glm::value_ptr(transform));
            glBindVertexArray(VAO);
            glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
        }
        for (int i = 0; i < 4; i++) {
            glBindTexture(GL_TEXTURE_2D, testureId);
            glm::mat4 transform = glm::mat4(1.0f);
            transform = glm::translate(transform, glm::vec3(i * 0.6f - 0.3f, -0.16f, 0.0f));
            glUseProgram(shaderProgram);
            unsigned int transformLoc = glGetUniformLocation(shaderProgram, "transform");
            glUniformMatrix4fv(transformLoc, 1, GL_FALSE, glm::value_ptr(transform));
            glBindVertexArray(VAO);
            glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
        }
    }

const unsigned int SCR_WIDTH = 800;
const unsigned int SCR_HEIGHT = 600;

Window::Window():window(nullptr) {}

Window::~Window() {
    glfwTerminate();
}

bool Window::initWindowResources() {
    bool result = false;
    if (glfwInit() == GLFW_TRUE) {
        glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
        glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
        glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
        window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "LearnOpenGL", NULL, NULL);
        if (window != nullptr) {
            glfwMakeContextCurrent(window);
            if (glfwSetFramebufferSizeCallback(window, [](GLFWwindow* window, int width, int height) {
                glViewport(0, 0, width, height); }) == NULL) {
                if (gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) {
                    result = true;
                }
            }
        }
    }
    return result;
}

const char* vertexShaderSource =
"#version 330 core\n"
"layout(location = 0) in vec3 aPos;\n"
"layout(location = 1) in vec2 aTexCoord;\n"
"out vec2 TexCoord;\n"
"uniform mat4 transform;\n"
"void main()\n"
"{\n"
"    gl_Position = transform * vec4(aPos, 1.0);\n"
"    TexCoord = vec2(aTexCoord.x, aTexCoord.y);\n"
"}\n\0";

const char* fragmentShaderSource =
"#version 330 core\n"
"out vec4 FragColor;\n"
"in vec3 ourColor;\n"
"in vec2 TexCoord;\n"
"uniform sampler2D texture1;\n"
"void main()\n"
"{\n"
"    FragColor = texture(texture1, TexCoord);\n"
"}\n\0";

void Window::mainWindowLoop() {

    graphicEngine.initShaders(vertexShaderSource, fragmentShaderSource);
    std::vector<std::string> pathsTextures = { "C:\\Users\\Олег\\\Desktop\\sea1.png" };
    float vertices[] = {
        // positions          // colors           // texture coords
        -1.3f,  0.16f, 0.0f,  1.0f, 1.0f, // top right
        -1.3f, -0.16f, 0.0f,  1.0f, 0.0f, // bottom right
        -0.7f, -0.16f, 0.0f,  0.0f, 0.0f, // bottom left
        -0.7f,  0.16f, 0.0f,  0.0f, 1.0f  // top left 
    };
    graphicEngine.initRenderData(vertices, sizeof(vertices));
    std::vector<ID> idsTextures = graphicEngine.initTextures(pathsTextures);
    while (!glfwWindowShouldClose(window))
    {
        glClearColor(1.0f, 1.0f, 1.0f, 1.0f);

        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
        graphicEngine.drawTextures(idsTextures);
        glfwSwapBuffers(window);
        glfwPollEvents();
    }
}

int main()
{
    Window window;
    if (window.initWindowResources()) {
        window.mainWindowLoop();
    }

    return 0;
}

Png: 大小 png:62x34 像素,透明精灵,使用 prog 创建 png:piskelapp

请避免有关此问题的信息:有关此问题的原因以及如何解决的信息解决这个问题。

I just started learning opengl technology.
My program draw 2d isometric tiles and program output this:
enter image description hereBe unknown reasons black lines appear when two textures overlap or two textures touch.

Code example:

typedef unsigned int ID;

class GraphicEngine {
public:
    GraphicEngine();
    ~GraphicEngine();
    void initShaders(const char* vertexShaderSource, const char* fragmentShaderSource);
    void initRenderData(float vertices[], unsigned int size);
    std::vector<ID> initTextures(std::vector<std::string>& paths);
    void drawTextures(std::vector<ID> testuresIds);

private:
    GraphicEngine(GraphicEngine&) = delete;
    GraphicEngine(GraphicEngine&&) = delete;
    GraphicEngine& operator=(const GraphicEngine& other) = delete;
private:
    unsigned int VBO = 0; 
    unsigned int VAO = 0;
    unsigned int EBO = 0;
    unsigned int shaderProgram;
};

GraphicEngine::GraphicEngine() {

}

GraphicEngine::~GraphicEngine() {
    glDeleteVertexArrays(1, &VAO);
    glDeleteBuffers(1, &VBO);
    glDeleteBuffers(1, &EBO);
}


void GraphicEngine::initShaders(const char* vertexShaderSource, const char* fragmentShaderSource) {
    glEnable(GL_BLEND);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    unsigned int vertexShader = glCreateShader(GL_VERTEX_SHADER);
    unsigned int fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
    shaderProgram = glCreateProgram();
    glShaderSource(vertexShader, 1, &vertexShaderSource, NULL);
    glCompileShader(vertexShader);
    glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL);
    glCompileShader(fragmentShader);
    glAttachShader(shaderProgram, vertexShader);
    glAttachShader(shaderProgram, fragmentShader);
    glLinkProgram(shaderProgram);
}

void GraphicEngine::initRenderData(float vertices[], unsigned int size) {
    unsigned int indices[] = {
        0, 1, 3,
        1, 2, 3
    };
    glGenVertexArrays(1, &VAO);
    glGenBuffers(1, &VBO);
    glGenBuffers(1, &EBO);

    glBindVertexArray(VAO);
    glBindBuffer(GL_ARRAY_BUFFER, VBO);
    glBufferData(GL_ARRAY_BUFFER, size, vertices, GL_STATIC_DRAW);

    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);

    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)0);
    glEnableVertexAttribArray(0);
    glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)(3 * sizeof(float)));
    glEnableVertexAttribArray(1);
}

std::vector<ID> GraphicEngine::initTextures(std::vector<std::string>& paths) {
    std::vector<ID> ids(paths.size());
    stbi_set_flip_vertically_on_load(true);
    for (int i = 0; i < paths.size(); i++) {
        unsigned int texture;
        glGenTextures(1, &ids[i]);
        glBindTexture(GL_TEXTURE_2D, ids[i]);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
        int width, height, nrChannels;
        unsigned char* data = stbi_load(paths[i].c_str(), &width, &height, &nrChannels, STBI_rgb_alpha);
        if (data)
        {
            glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
            glGenerateMipmap(GL_TEXTURE_2D);
        }
        stbi_image_free(data);
    }
    return ids;
}

void GraphicEngine::drawTextures(std::vector<ID> testuresIds) {
    static bool ex = false;
    for (auto testureId : testuresIds) {
        for (int i = 0; i < 4; i++) {
            glBindTexture(GL_TEXTURE_2D, testureId);
            glm::mat4 transform = glm::mat4(1.0f);
            transform = glm::translate(transform, glm::vec3(i * 0.6f + 0.0f, 0.0f, 0.0f));
            glUseProgram(shaderProgram);
            unsigned int transformLoc = glGetUniformLocation(shaderProgram, "transform");
            glUniformMatrix4fv(transformLoc, 1, GL_FALSE, glm::value_ptr(transform));
            glBindVertexArray(VAO);
            glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
        }
        for (int i = 0; i < 4; i++) {
            glBindTexture(GL_TEXTURE_2D, testureId);
            glm::mat4 transform = glm::mat4(1.0f);
            transform = glm::translate(transform, glm::vec3(i * 0.6f - 0.3f, -0.16f, 0.0f));
            glUseProgram(shaderProgram);
            unsigned int transformLoc = glGetUniformLocation(shaderProgram, "transform");
            glUniformMatrix4fv(transformLoc, 1, GL_FALSE, glm::value_ptr(transform));
            glBindVertexArray(VAO);
            glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
        }
    }

const unsigned int SCR_WIDTH = 800;
const unsigned int SCR_HEIGHT = 600;

Window::Window():window(nullptr) {}

Window::~Window() {
    glfwTerminate();
}

bool Window::initWindowResources() {
    bool result = false;
    if (glfwInit() == GLFW_TRUE) {
        glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
        glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
        glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
        window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "LearnOpenGL", NULL, NULL);
        if (window != nullptr) {
            glfwMakeContextCurrent(window);
            if (glfwSetFramebufferSizeCallback(window, [](GLFWwindow* window, int width, int height) {
                glViewport(0, 0, width, height); }) == NULL) {
                if (gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) {
                    result = true;
                }
            }
        }
    }
    return result;
}

const char* vertexShaderSource =
"#version 330 core\n"
"layout(location = 0) in vec3 aPos;\n"
"layout(location = 1) in vec2 aTexCoord;\n"
"out vec2 TexCoord;\n"
"uniform mat4 transform;\n"
"void main()\n"
"{\n"
"    gl_Position = transform * vec4(aPos, 1.0);\n"
"    TexCoord = vec2(aTexCoord.x, aTexCoord.y);\n"
"}\n\0";

const char* fragmentShaderSource =
"#version 330 core\n"
"out vec4 FragColor;\n"
"in vec3 ourColor;\n"
"in vec2 TexCoord;\n"
"uniform sampler2D texture1;\n"
"void main()\n"
"{\n"
"    FragColor = texture(texture1, TexCoord);\n"
"}\n\0";

void Window::mainWindowLoop() {

    graphicEngine.initShaders(vertexShaderSource, fragmentShaderSource);
    std::vector<std::string> pathsTextures = { "C:\\Users\\Олег\\\Desktop\\sea1.png" };
    float vertices[] = {
        // positions          // colors           // texture coords
        -1.3f,  0.16f, 0.0f,  1.0f, 1.0f, // top right
        -1.3f, -0.16f, 0.0f,  1.0f, 0.0f, // bottom right
        -0.7f, -0.16f, 0.0f,  0.0f, 0.0f, // bottom left
        -0.7f,  0.16f, 0.0f,  0.0f, 1.0f  // top left 
    };
    graphicEngine.initRenderData(vertices, sizeof(vertices));
    std::vector<ID> idsTextures = graphicEngine.initTextures(pathsTextures);
    while (!glfwWindowShouldClose(window))
    {
        glClearColor(1.0f, 1.0f, 1.0f, 1.0f);

        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
        graphicEngine.drawTextures(idsTextures);
        glfwSwapBuffers(window);
        glfwPollEvents();
    }
}

int main()
{
    Window window;
    if (window.initWindowResources()) {
        window.mainWindowLoop();
    }

    return 0;
}

Png: Size png: 62x34 pixels, Transparent sprite, use prog to created png: piskelapp

Please, pvoide information about this issue: inforamtion about reasons of this issue and how to fix this issue.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

感情废物 2025-01-25 12:15:46

瓷砖边缘的深线是α混合和纹理过滤的结果。

链接的瓷砖图像(PNG)包含三个预制的颜色通道(红色,绿色,蓝色)和透明度信息(alpha通道),没有部分透明的像素(Alpha值为1.0或0.0,到处都是0.0,导致边缘尖锐):

< a href =“ https://i.sstatic.net/ilbtl.png” rel =“ nofollow noreferrer”>

​> gimp )。该图像使用了预制的Alpha,即颜色通道被Alpha通道掩盖,并且仅包含Alpha通道非零的颜色信息。

有效图像区域外部的区域都是黑色的,因此,当OpenGL使用线性纹理插值( gl_linear )时,它将与可见的彩色Texels混合在边缘的隐藏黑色Texels,这可能会导致深色,具体取决于使用的混合功能。

Alpha混合将已经存在的颜色混合在Framebuffer(清除背景或已写入的片段)中的颜色与传入的颜色混合。

使用的混合功能 glblendfunc(gl_src_alpha,gl_one_minus_src_alpha)指示硬件为每个像素执行此操作:

”

结果:结果:dark dark tile tile tile tile tile tile the unterpolocacts the unterpololated the unterpolocacts瓷砖边缘处的alpha值,它使源颜色变暗( srgb * sa )(修改了原始瓷砖图像,从原始帖子中复制了问题):

换句话说:

https:// https:// shawnhargreaves。 com/blog/texture-filtering-alpha-cutouts.html

纹理过滤:alpha cutouts

(...)

过滤同样适用于RGB和Alpha通道。当在Alpha上使用时
切口纹理的渠道将产生新的分数alpha
形状边缘周围的值,这使情况看起来不错,并且
抗脉。但是过滤还会产生新的RGB颜色,一部分
在纹理的固体和透明部分的RGB之间。
因此,所谓透明像素的RGB值可以流血
我们的最终图像。

这通常会导致α切口周围的黑暗边界,因为
透明像素的RGB通常是黑色的。取决于
质地,出血可能是白色,粉红色等。

要快速解决问题,混合功能可以简单地更改为 glblendfunc(gl_one,gl_one_minus_src_alpha),因为瓷砖已经过了tile image premultightiple image rgb频道,透明区域中的全黑(= 0):

​https://shawnhargreaves.com/blog/premultiplied-alpha.html :

预制alpha比几个传统混合均优于常规混合
原因:

  • 过滤alpha cutouts(...)
  • 时,它可以正常工作

  • 进行图像组成(...)
  • 时,它可以正常工作

  • 这是传统和添加剂混合物的超集。如果您将alpha设置为零,而RGB不为零,则会获得一个添加剂
    混合。这对于想要平稳的粒子系统可能很方便
    从添加剂发光火花过渡到黑暗的烟灰块
    颗粒年龄。

结果:更改混合功能后,几乎完全消失了 (用原始图像的修改示例,部分固定):

,完美的。

为了解决此问题,可以在瓷砖周围绘制一些像素以稍微扩大可见区域:

“ https://i.sstatic.net/cjfuy.png” alt =“放大瓷砖”>

让瓷砖重叠一点,这样:

”重叠的瓷砖

结果(带纹理过滤,和纹理过滤和重叠像素):

(此外,可以在工件的顶部绘制行/其他图形元素以掩盖它们。如果不想要像素化的锯齿状边缘可以在一个连续的网格中彼此彼此相邻的菱形,该连续网格可以在一个抽奖中呈现,不再需要α混合,但是我猜想不适合像素化的外观


。 :

  • openggl纹理参数 >

    要摆脱手工艺和模糊/过滤外观, gl_linear 可以用 gl_nearest ,它完全禁用了所选纹理的纹理插值,并让OpenGl renender启用opengl renender无需应用纹理过滤的原始像素( gl_clamp_to_edge 在这里避免在边缘避免工件):

    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
  • 两种纹理的力量:

    使用质地维度可以通过始终来提高开放性能
    是两个功率,例如64x32(您的情况下而不是60x32)。 - 可以修改瓷砖图像,例如:在每一侧添加了2个像素(标有边界):
    “

    附带注意:这种限制不再重要,但是过去,甚至有必要使用特殊

常规的OpenGL纹理仅限于图像
两个尺寸和可选的1特克尔边框。
arb_texture_non_power_of_of_two扩展放松尺寸限制
对于1D,2D,立方体地图和3D纹理目标。

  • 快照到像素:

    有多种方法可以使用OpenGL进行此操作。
    我建议扩展拼字图,以便1个OpenGL
    坐标单元恰好与1个Texel单位匹配。这样,瓷砖可以精确地放置在像素网格上(只需将瓷砖顶点的坐标换成64个像素/openGL单元左/右,以便在此示例中到达下一个)。现在可以将坐标表示为发动机中的整数。

修改后的代码示例:

void GraphicEngine::drawTextures(std::vector<ID> testuresIds, float wndRatio) {
  const int countx = 3, county = 3; /* number of tiles */
  const float scale = 100.0f; /* zoom */
  const glm::mat4 mvp = glm::ortho(-wndRatio * scale, wndRatio * scale, -scale, scale, 2.0f, -2.0f);
  const float offx = -((countx * TILE_WIDTH * 2.0f) * 0.5f - TILE_WIDTH);
  const float offy = -TILE_WIDTH * 0.5f;
  for (auto testureId : testuresIds) {
    for (int y = 0; y < county; y++) {
      for (int x = 0; x < countx - (y & 1 ? 1 : 0); x++) {
        const glm::mat4 transform = mvp * glm::translate(glm::mat4(1.0f), glm::vec3(
          offx + x * TILE_WIDTH * 2.0f + (y & 1 ? TILE_WIDTH : 0.0f),
          offy + y * TILE_HEIGHT, 0.0f));
        glBindTexture(GL_TEXTURE_2D, testureId);
        const GLint transformLoc = glGetUniformLocation(shaderProgram, "transform");
        glUniformMatrix4fv(transformLoc, 1, GL_FALSE, glm::value_ptr(transform));
        glUseProgram(shaderProgram);
        glBindVertexArray(VAO);
        glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
      }
    }
  }
}

修改后的屏幕截图:

,没有明显的边缘:


使用“直α纹理”的一些提示:

解决这可能是使用未掩盖/未置身/直α纹理的另一种方法。原始瓷砖图像的颜色通道可以像这样填充:

“修改图图像”

(注意:上面的链接的PNG图像无法直接使用。Imgur似乎可以转换透明的PNG图像并自动掩盖颜色频道...)

“修改图图像的通道”

当使用纹理过滤和常规的alpha混合功能时,此技术可以帮助减少工件(即 gl_src_alpha,gl_src_alpha,gl_one_minus_src_src_alpha )。但是,背景总是会通过一点点显示,因为某些像素总是在边缘上看到透明的(由纹理过滤引起):

“具有线性纹理插值的修改示例”

(结果应与第一个解决方案非常相似上面,在原始的体育图像中与修改的alpha混合一起使用( gl_one,gl_one_minus_src_alpha )。)。)。)。)

。向外延伸以避免文物。

显然,当精确的2D外观是目标时,这并不能完全解决原始问题。 ,当使用其他形式的透明/混合/合成时,隐藏像素也会产生不良结果,例如粒子,叶子的半透明边缘,文本等。


但是它在其他情况下可能很有用 解决问题:

gl_polygon_smooth_hint
指示抗抗氧化多边形的采样质量。提示
GL_NICEST可能会导致更多像素片段在
栅格化,如果应用了较大的滤波器功能。


原始问题中的代码带有某些问题,它不会像这样编译(类定义的某些部分缺少等)。需要一些努力来发行发行,这使得回答问题更加复杂。 - 对我而言,目的是否只是渲染无缝的,像素化的瓷砖(解决方案:使用 gl_nearest ),或者是否需要纹理过滤...

这是我的修改的代码示例

有关堆栈溢出的相关问题:

  • opengl正常混合与黑色Alpha Edges
  • a href =“ https://stackoverflow.com/questions/19674740/opengl-es2-premultiplied-vs-vs-straight-alpha-搅拌”> openggl es2 premultiped vs fromptiped vs fromptiald vs Alpha +

href =“ http://glprogramming.com/red/chapter06.html” rel =“ nofollow noreferrer”> alpha混合/“ premultiped alpha”:

The dark lines at the edge of the tiles are results of alpha blending and texture filtering.

The linked tile image (PNG) contains three premultipled color channels (red, green, blue) and transparency information (alpha channel) with no partially transparent pixels (the alpha value is either 1.0 or 0.0 everywhere, which results in sharp edges):

tile image from original post

channels of original tile image

This can be checked in an image editor (for example Gimp). The image uses premultiplied alpha, i.e. the color channels were masked by the alpha channel and only contain color information where the alpha channel is non-zero.

The area outside of the valid image region is all black, so when OpenGL uses linear texture interpolation (GL_LINEAR) it will mix the hidden black texels right at the edge with the visible colored texels, which can result in a dark color, depending on the used blending function.

Alpha blending mixes the already present color in the framebuffer (of the cleared background or the already written fragments) with the incoming ones.

The used blending function glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) instructs the hardware to do this for every pixel:

GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA illustrated

The result: dark artifacts at the edges of each tile caused by the interpolated alpha value at the edges of the tile, which darkens the source color (sRGB * sA) (modified example with original tile image, reproduced issue from the original post):

modified example program using the original tile image, with glitchy blending

In other words:

https://shawnhargreaves.com/blog/texture-filtering-alpha-cutouts.html:

Texture filtering: alpha cutouts

(...)

Filtering applies equally to the RGB and alpha channels. When used on the alpha
channel of a cutout texture it will produce new fractional alpha
values around the edge of the shape, which makes things look nice and
antialiased. But filtering also produces new RGB colors, part way in
between the RGB of the solid and transparent parts of the texture.
Thus the RGB values of supposedly transparent pixels can bleed into
our final image.

This most often results in dark borders around alpha cutouts, since
the RGB of transparent pixels is often black. Depending on the
texture, the bleeding could alternatively be white, pink, etc.

To quick-fix the problem, the blending function could simply by changed to glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA), since the tile image already has premultiplied RGB channels, which are all black (= 0) in transparent areas:

GL_ONE, GL_ONE_MINUS_SRC_ALPHA illustrated

https://shawnhargreaves.com/blog/premultiplied-alpha.html:

Premultiplied alpha is better than conventional blending for several
reasons:

  • It works properly when filtering alpha cutouts (...)
  • It works properly when doing image composition (...)
  • It is a superset of both conventional and additive blending. If you set alpha to zero while RGB is non zero, you get an additive
    blend. This can be handy for particle systems that want to smoothly
    transition from additive glowing sparks to dark pieces of soot as the
    particles age.

The result: dark artifacts disappear almost entirely after changing the blending function (modified example with original tile image, issue partially fixed):

modified example program using the original tile image, with improved blending

Not perfect.

To fix this, some pixels could be drawn around the tile to enlarge the visible area a bit:

enlarged tile

To let tiles overlap a bit, like that:

overlapped tiles

The result (with texture filtering, and overlapped pixels):
overlapped tiles, linear texture filtering

(Additionally, lines/other graphical elements could be drawn on top of the artifacts to cover them up. And if the pixelated jagged edges are not wanted, the actual textured polygons quads could be replaced by rhombuses that could be placed precisely next to each other in a continuous mesh that could be rendered in one draw call, no alpha blending required anymore, however sharp edges do not fit a pixelated look I guess.)


A possible solution using GL_NEAREST:

  • OpenGL texture parameters:

    To get rid of the artefacts and blurred/filtered look, GL_LINEAR can be replaced by GL_NEAREST, which disables texture interpolation altogether for the selected texture and lets OpenGL render the raw pixels without applying texture filtering (GL_CLAMP_TO_EDGE makes sense here to avoid artifacts at the edges):

    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
  • Power of Two Textures:

    OpenGL performance can be improved by always using texture dimensions that
    are a power of two, e.g. 64x32 (instead of 60x32 in your case). - The tile image could be modified, e.g.: 2 pixels added on each side (and borders marked):
    sea2.png

    Side note: This restriction is not that important anymore, but in the past it was even necessary to use a special extension to enable NPOT textures:

Conventional OpenGL texturing is limited to images with
power-of-two dimensions and an optional 1-texel border.
ARB_texture_non_power_of_two extension relaxes the size restrictions
for the 1D, 2D, cube map, and 3D texture targets.

  • Snap to pixel:

    There are multiple ways to do this with OpenGL.
    I would recommend to scale the orthographic projection, so that 1 OpenGL
    coordinate unit exactly matches 1 texel unit. That way, tiles can be precisely placed on the pixel grid (just shift coordinates of the tile vertices by 64 pixels/OpenGL units left/right, to get to the next one, in this example). Coordinates could be represented as integers in the engine now.

Modified code example:

void GraphicEngine::drawTextures(std::vector<ID> testuresIds, float wndRatio) {
  const int countx = 3, county = 3; /* number of tiles */
  const float scale = 100.0f; /* zoom */
  const glm::mat4 mvp = glm::ortho(-wndRatio * scale, wndRatio * scale, -scale, scale, 2.0f, -2.0f);
  const float offx = -((countx * TILE_WIDTH * 2.0f) * 0.5f - TILE_WIDTH);
  const float offy = -TILE_WIDTH * 0.5f;
  for (auto testureId : testuresIds) {
    for (int y = 0; y < county; y++) {
      for (int x = 0; x < countx - (y & 1 ? 1 : 0); x++) {
        const glm::mat4 transform = mvp * glm::translate(glm::mat4(1.0f), glm::vec3(
          offx + x * TILE_WIDTH * 2.0f + (y & 1 ? TILE_WIDTH : 0.0f),
          offy + y * TILE_HEIGHT, 0.0f));
        glBindTexture(GL_TEXTURE_2D, testureId);
        const GLint transformLoc = glGetUniformLocation(shaderProgram, "transform");
        glUniformMatrix4fv(transformLoc, 1, GL_FALSE, glm::value_ptr(transform));
        glUseProgram(shaderProgram);
        glBindVertexArray(VAO);
        glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
      }
    }
  }
}

Screenshot of modified example:
screenshot of modified example

And without marked edges:
screenshot of modified example without marked edges


Some hints on the use of "straight alpha textures":

Another approach to solve this might be the use of an unmasked/unpremultiplied/straight alpha texture. The color channels of the original tile image can be flood filled out like this:

modified tile image

(Note: The linked PNG image above can't be used directly. Imgur seems to convert transparent PNG images and automatically masks the color channels...)

channels of modified tile image

This technique could help to reduce the artifacts when texture filtering and the conventional alpha blending function is used (i.e. GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA). However, the background will always show through a tiny bit, because some pixels are always sightly transparent at the edges (caused by texture filtering):

modified example with linear texture interpolation

(The result should be very similar to the first solution above, where the original premultiplied image is used with modified alpha blending (GL_ONE, GL_ONE_MINUS_SRC_ALPHA).)

If the tile contains not just a plain color, the color information at the edges of the tile would need to be extended outwards to avoid artifacts.

Obviously this doesn't solve the original issue completely, when a precise 2D look is the goal. But it could be useful in other situations, where the hidden pixels also generate bad results when other forms of transparency/blending/compositing are used, e.g. for particles, semi-transparent edges of foliage, text etc.


Some general hints that could help to solve the issue:

GL_POLYGON_SMOOTH_HINT
Indicates the sampling quality of antialiased polygons. Hinting
GL_NICEST can result in more pixel fragments being generated during
rasterization, if a larger filter function is applied.


The code in the original question comes with some issues, it does not compile like that (some parts of the class definitions are missing etc.). It takes some effort to reproduce to issue, which makes it more complicated to answer the question. - And it wasn't completely clear to me whether the intention is to just render seamless, pixelated tiles (solution: use GL_NEAREST), or if texture filtering is required...

Here my modified code example.

Related questions on Stack Overflow:

Some links related to Alpha Blending / "premultiplied alpha":

韵柒 2025-01-25 12:15:46

我能够复制您的问题。您正在与非质化的alpha合作,这是在呈现半透明图像时产生不良结果而闻名的。
看这篇文章: http://www.realtimerendering.com/blog/ GPU-PREFER-PREMULTIPLICATION/

现在,要解决您的问题,首先将混合功能更改为glblendfunc(gl_one,gl_one_minus_src_alpha)。
其次,STBI不会在负载上预先添加Alpha,您必须手动进行。
每个像素在0-255范围内由4个字节,红色,绿色,蓝色和alpha组成。通过将每个值除以255.0f,乘r,g和b,然后将其乘乘以255.0F,将每个值转换为归一化范围(0.0F -1.0F);

I was able to reproduce your issue. You are working with non-premultiplied alpha, this is known for producing undesirable results when rendering translucent images.
Take a look at this article: http://www.realtimerendering.com/blog/gpus-prefer-premultiplication/

Now, to solve your problem, first change your blend function to glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA).
Second, stbi doesn't pre-multiply the alpha on load, you have to do it manually.
Each pixel is composed by 4 bytes, red, green, blue and alpha, on the 0-255 range. Convert each value to the normalized range (0.0f - 1.0f) by dividing by 255.0f, multiply r, g, and b by alpha, then multiply it back by 255.0f;

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文