Opengl ES透明纹理不混合

Open GL ES transparent texture not blending

我正在尝试将具有透明度的纹理绘制到背景渐变上,该渐变由在顶点之间插值颜色的顶点着色器创建。但是,只绘制纹理的不透明部分。

我正在使用混合功能:

glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

渲染代码:

struct vertex {
    float position[3];
    float color[4];
    float texCoord[2];
};

typedef struct vertex vertex;

const vertex vertices[] = {
    {{1, -1, 0}, {0, 167.0/255.0, 253.0/255.0, 1}, {1, 0}}, // BR (0)
    {{1, 1, 0}, {0, 222.0/255.0, 1.0, 1}, {1, 1}}, // TR (1)
    {{-1, 1, 0}, {0, 222.0/255.0, 1.0, 1}, {0, 1}}, // TL (2)
    {{-1, -1, 0}, {0, 167.0/255.0, 253.0/255.0, 1}, {0, 0}}, // BL (3)
};

const GLubyte indicies[] = {
    3, 2, 0, 1
};

-(void) render {

    glViewport(0, 0, self.frame.size.width, self.frame.size.height);

    glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer);

    glVertexAttribPointer(positionSlot, 3, GL_FLOAT, GL_FALSE, sizeof(vertex), 0);
    glVertexAttribPointer(colorSlot, 4, GL_FLOAT, GL_FALSE, sizeof(vertex), (GLvoid*)(sizeof(float)*3));
    glVertexAttribPointer(textureCoordSlot, 2, GL_FLOAT, GL_FALSE, sizeof(vertex), (GLvoid*)(sizeof(float)*7));

    glDrawElements(GL_TRIANGLE_STRIP, sizeof(indicies)/sizeof(indicies[0]), GL_UNSIGNED_BYTE, 0);

    // Not sure if required for blending to work..
    glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer);

    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D, texture);
    glUniform1i(textureUniform, 0);

    glVertexAttribPointer(positionSlot, 3, GL_FLOAT, GL_FALSE, sizeof(vertex), 0);
    glVertexAttribPointer(colorSlot, 4, GL_FLOAT, GL_FALSE, sizeof(vertex), (GLvoid*)(sizeof(float)*3));
    glVertexAttribPointer(textureCoordSlot, 2, GL_FLOAT, GL_FALSE, sizeof(vertex), (GLvoid*)(sizeof(float)*7));


    glDrawElements(GL_TRIANGLE_STRIP, sizeof(indicies)/sizeof(indicies[0]), GL_UNSIGNED_BYTE, 0);


    [context presentRenderbuffer:GL_RENDERBUFFER];
}

我不确定是否需要对渲染缓冲区进行两次绘制才能使混合功能正常工作,所以目前我在未绑定纹理的情况下进行绘制,然后再进行绑定。

片段着色器:

varying lowp vec4 destinationColor;

varying lowp vec2 texCoordOut;
uniform sampler2D tex;

void main() {
    lowp vec4 tex2D = texture2D(tex, texCoordOut);
    gl_FragColor = vec4(tex2D.rgb+destinationColor.rgb, tex2D.a*destinationColor.a);
}

顶点着色器:

attribute vec4 position;

attribute vec4 sourceColor;
varying vec4 destinationColor;

attribute vec2 texCoordIn;
varying vec2 texCoordOut;


void main() {
    destinationColor = sourceColor;
    gl_Position = position;
    texCoordOut = texCoordIn;
}

纹理加载代码:

-(GLuint) loadTextureFromImage:(UIImage*)image {

    CGImageRef textureImage = image.CGImage;

    size_t width = CGImageGetWidth(textureImage);
    size_t height = CGImageGetHeight(textureImage);

    GLubyte* spriteData = (GLubyte*) malloc(width*height*4);

    CGColorSpaceRef cs = CGImageGetColorSpace(textureImage);
    CGContextRef c = CGBitmapContextCreate(spriteData, width, height, 8, width*4, cs, kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big);
    CGColorSpaceRelease(cs);

    CGContextScaleCTM(c, 1, -1);
    CGContextTranslateCTM(c, 0, -CGContextGetClipBoundingBox(c).size.height);

    CGContextDrawImage(c, (CGRect){CGPointZero, {width, height}}, textureImage);
    CGContextRelease(c);

    GLuint glTex;
    glGenTextures(1, &glTex);
    glBindTexture(GL_TEXTURE_2D, glTex);

    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);

    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (int)width, (int)height, 0, GL_RGBA, GL_UNSIGNED_BYTE, spriteData);

    glBindTexture(GL_TEXTURE_2D, 0);

    free(spriteData);

    return glTex;
}

知道我做错了什么吗?

好吧,我觉得很傻。尝试在片段着色器外部使用混合功能没有做任何事情,因为纹理已经绘制好了。我只需要在片段着色器中使用等效项:

varying lowp vec4 destinationColor;

varying lowp vec2 texCoordOut;
uniform sampler2D tex;

void main() {
    lowp vec4 tex2D = texture2D(tex, texCoordOut);    
    lowp vec4 result = tex2D + vec4(1.0 - tex2D.a) * destinationColor;

    gl_FragColor = result;
}

您给自己的答案可能足以满足您的特定情况,但我认为这不是一个很好的解决方案。当您想要渲染两个以上的对象时,您可能会 运行 遇到很多问题。

你画了同一个物体两次。首先没有纹理绑定,然后使用纹理绑定 - 在着色器中完成混合。但是你会如何处理第三个对象呢?

我建议对两个对象使用一组不同的顶点。像这样:

const vertex gradient_background[] = {
    {{1, -1, 0}, {0, 167.0/255.0, 253.0/255.0, 1}, {1, 0}},
    {{1, 1, 0}, {0, 222.0/255.0, 1.0, 1}, {1, 1}}, 
    {{-1, 1, 0}, {0, 222.0/255.0, 1.0, 1}, {0, 1}}, 
    {{-1, -1, 0}, {0, 167.0/255.0, 253.0/255.0, 1}, {0, 0}}
};

const vertex textured_object[] = {
    {{1, -1, 0}, {0, 0, 0, 0}, {1, 0}},
    {{1, 1, 0}, {0, 0, 0, 0}, {1, 1}},
    {{-1, 1, 0}, {0, 0, 0, 0}, {0, 1}}, 
    {{-1, -1, 0}, {0, 0, 0, 0}, {0, 0}}
};

并适当调整你的渲染函数,绘制后也将纹理解除绑定为0。

-(void) render {

    ...

    glVertexAttribPointer(positionSlot, 3, GL_FLOAT, GL_FALSE, sizeof(gradient_background), 0);
    glVertexAttribPointer(colorSlot, 4, GL_FLOAT, GL_FALSE, sizeof(gradient_background), (GLvoid*)(sizeof(float)*3));
    glVertexAttribPointer(textureCoordSlot, 2, GL_FLOAT, GL_FALSE, sizeof(gradient_background), (GLvoid*)(sizeof(float)*7));

    glDrawElements(GL_TRIANGLE_STRIP, sizeof(indicies)/sizeof(indicies[0]), GL_UNSIGNED_BYTE, 0);

    ...

    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D, texture);
    glUniform1i(textureUniform, 0);

    glVertexAttribPointer(positionSlot, 3, GL_FLOAT, GL_FALSE, sizeof(textured_object), 0);
    glVertexAttribPointer(colorSlot, 4, GL_FLOAT, GL_FALSE, sizeof(textured_object), (GLvoid*)(sizeof(float)*3));
    glVertexAttribPointer(textureCoordSlot, 2, GL_FLOAT, GL_FALSE, sizeof(textured_object), (GLvoid*)(sizeof(float)*7));


    glDrawElements(GL_TRIANGLE_STRIP, sizeof(indicies)/sizeof(indicies[0]), GL_UNSIGNED_BYTE, 0);

    // Don't forget to unbind texture for next draw
    glBindTexture(GL_TEXTURE_2D, 0);

    ...
}

片段着色器

varying lowp vec4 destinationColor;

varying lowp vec2 texCoordOut;
uniform sampler2D tex;

void main() {
    lowp vec4 tex2D = texture2D(tex, texCoordOut); // Returns (0, 0, 0, 1) when texture 0 is bound
    gl_FragColor = destinationColor + tex2D;
}

然后使用

glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

或您想要的任何其他混合功能。