Related
My fragment shader for a mobile game in cocos2dx works on iOS, but does not work as expected on Android and I'm out of ideas.
The idea: u_texture is the input texture of a sprite whose appearance I want to modify.
u_texture1 is a "mask" texture that has some transparency and depending on its alpha channel, I want to render the sprite's appearance with some changes or without.
The very minimal example of the problem I'm experiencing:
This is working "as expected", namely, colours the sprite red where the mask is transparent and yellow where the mask is opaque.
uniform sampler2D u_texture;
uniform sampler2D u_texture1;
varying vec2 v_texCoord;
varying vec2 v_resolution;
void main()
{
vec4 maskCol=texture2D(u_texture1, v_texCoord);
vec4 currentCol = texture2D(u_texture,v_texCoord);
vec4 newCol = currentCol;
if(maskCol.a == 0.0){
newCol.r = 1.0;
newCol.g = 0.0;
newCol.b = 0.0;
newCol.a = 1.0;
} else {
newCol.r = 1.0;
newCol.g = 1.0;
newCol.b = 0.0;
newCol.a = 1.0;
}
gl_FragColor = newCol;
}
This code I would expect to colour the sprite red where the mask is transparent and leave the rest alone. However, it does not do that. Instead, it colours the sprite red where the INPUT TEXTURE is transparent, and leaves the rest alone.
uniform sampler2D u_texture;
uniform sampler2D u_texture1;
varying vec2 v_texCoord;
varying vec2 v_resolution;
void main()
{
vec4 maskCol=texture2D(u_texture1, v_texCoord);
vec4 currentCol = texture2D(u_texture,v_texCoord);
vec4 newCol = currentCol;
if(maskCol.a == 0.0){
newCol.r = 1.0;
newCol.g = 0.0;
newCol.b = 0.0;
newCol.a = 1.0;
}
gl_FragColor = newCol;
}
Can anyone see anything wrong with this code?
Apart from equality comparing the alpha value - which is not the problem here, I have checked, but I’m leaving it for simplicity.
Does v_resolution do anything?
glsl is fine.
Two ways to check:
1.Make sure that u_texture1 is a mask texture. Is the texture passing reversed?
2.The first glsl is correct because each newcol has a color. But the second glsl is not, so you can try to output newcol directly and see if the result is expected?
2022-12-29
I copied this glsl.
right is:
unsigned int texture1, texture2;
glGenTextures(1, &texture1);
glBindTexture(GL_TEXTURE_2D, texture1);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glGenTextures(1, &texture2);
glBindTexture(GL_TEXTURE_2D, texture2);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
shader is:
vec4 maskCol=texture2D(texture2, TexCoord);
vec4 currentCol = texture2D(texture1,TexCoord);
vec4 newCol = currentCol;
if(maskCol.a == 0.0){
newCol.r = 1.0;
newCol.g = 0.0;
newCol.b = 0.0;
newCol.a = 1.0;
}
FragColor = newCol;
texture1 is:
texture2 is
error is:
glGenTextures(1, &texture1);
glBindTexture(GL_TEXTURE_2D, texture1);
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);
maybe try to change texture glTexParameteri or other Settings for textures!
I’m struggling with rendering a simple overlay image on top of an already rendered frame. The frame is rendered by a mapbox library which I modified by calling my rendering code just after mapbox renders the frame. I don't know whether the fact that I’m rendering on top of an already rendered frame by mapbox library is important, but maybe it is, so I’m mentioning it here.
The problem seems somewhat related to texturing. The image I try to render is a checkerboard with red and transparent squares. The image bitmap does not have any edges. The image is visible but the edges are also coloured red which is not what I’ve intended and I've already spent a couple of days trying to figure out the reason why these edges are being rendered. I’ll be more than grateful if someone could enlighten me on what I’m doing wrong.
checkerboard with edges. Notice that one edge fades out at the top
namespace mbgl {
void MapOverlayRenderer::arm() { // called when rendering surface has changed
releaseLog("MOR a");
program = createProgram();
vertexBuffer = createVertexBuffer();
indexBuffer = createIndexBuffer();
glGenTextures(1, &texture);
fillTexture();
}
void MapOverlayRenderer::render() { // called just after mapbox frame has rendered
if (!_image.valid()) {
return;
}
glUseProgram(program);
auto positionHandle = glGetAttribLocation(program, "vertexData");
glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
glEnableVertexAttribArray(positionHandle);
GLint numberOfValuesPerVertex = 4;
GLsizei stride = numberOfValuesPerVertex * sizeof(GLfloat);
glVertexAttribPointer(positionHandle, numberOfValuesPerVertex, GL_FLOAT, false, stride, nullptr);
auto imagePositionHandle = glGetUniformLocation(program, "imagePosition");
GLfloat imagePosition[] = {_leftPadding, _topPadding, _rightPadding, _bottomPadding};
glUniform4fv(imagePositionHandle, 1, imagePosition);
auto imageTextureHandle = glGetUniformLocation(program, "imageTexture");
glUniform1i(imageTextureHandle, 0);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_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);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer);
auto numberOfVertices = 6;
glDrawElements(GL_TRIANGLES, numberOfVertices, GL_UNSIGNED_INT, nullptr);
glDisableVertexAttribArray(positionHandle);
}
void MapOverlayRenderer::setImage(float leftPadding, float topPadding, float rightPadding,
float bottomPadding, PremultipliedImage &&image) { // is called when I pass new image to be rendered
this->_image = std::move(image);
this->_leftPadding = leftPadding;
this->_topPadding = topPadding;
this->_rightPadding = rightPadding;
this->_bottomPadding = bottomPadding;
fillTexture();
}
void MapOverlayRenderer::removeImage() {
this->_leftPadding = 0.0f;
this->_topPadding = 0.0f;
this->_rightPadding = 0.0f;
this->_bottomPadding = 0.0f;
this->_image = PremultipliedImage();
}
GLuint MapOverlayRenderer::createVertexBuffer() {
GLuint result;
glGenBuffers(1, &result);
std::array<GLfloat, 16u> vertices{
-1.0f, -1.0f, 0.0f, 0.0f,
1.0f, -1.0f, 1.0f, 0.0f,
-1.0f, 1.0f, 0.0f, 1.0f,
1.0f, 1.0f, 1.0f, 1.0f
};
glBindBuffer(GL_ARRAY_BUFFER, result);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), &vertices, GL_STATIC_DRAW);
return result;
}
GLuint MapOverlayRenderer::createIndexBuffer() {
GLuint result;
glGenBuffers(1, &result);
std::array<GLuint, 6u> indices{
0u, 1u, 2u,
2u, 1u, 3u
};
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, result);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), &indices, GL_STATIC_DRAW);
return result;
}
GLuint MapOverlayRenderer::createVertexShader() {
auto vertexShaderCode = R"(
precision highp float;
precision highp int;
precision highp sampler2D;
attribute vec4 vertexData;
varying vec2 fullscreenTextureCoordinates;
void main() {
fullscreenTextureCoordinates = vertexData.zw;
gl_Position = vec4(vertexData.xy, 0.0, 1.0);
})";
auto shader = loadShader(GL_VERTEX_SHADER, &vertexShaderCode);
return shader;
}
GLuint MapOverlayRenderer::createFragmentShader() {
auto fragmentShaderCode = R"(
precision highp float;
precision highp int;
precision highp sampler2D;
varying vec2 fullscreenTextureCoordinates;
uniform vec4 imagePosition;
uniform sampler2D imageTexture;
void main() {
float left = imagePosition[0];
float top = imagePosition[1];
float right = imagePosition[2];
float bottom = imagePosition[3];
if (fullscreenTextureCoordinates[0] < left ||
fullscreenTextureCoordinates[0] > right ||
fullscreenTextureCoordinates[1] > 1.0 - top ||
fullscreenTextureCoordinates[1] < 1.0 - bottom) {
gl_FragColor = vec4(0.0, 0.0, 0.0, 0.0);
} else {
float imageHorizontalCoordinate = (fullscreenTextureCoordinates[0] - left) / (right - left);
float imageVerticalCoordinate = 1.0 - (fullscreenTextureCoordinates[1] - top) / (bottom - top);
vec2 imageTextureCoordinates = vec2(imageHorizontalCoordinate, imageVerticalCoordinate);
gl_FragColor = texture2D(imageTexture, imageTextureCoordinates);
}
})";
auto shader = loadShader(GL_FRAGMENT_SHADER, &fragmentShaderCode);
return shader;
}
GLuint MapOverlayRenderer::createProgram() {
auto vertexShader = createVertexShader();
auto fragmentShader = createFragmentShader();
auto result = glCreateProgram();
glAttachShader(result, vertexShader);
glAttachShader(result, fragmentShader);
glLinkProgram(result);
return result;
}
GLuint MapOverlayRenderer::loadShader(GLenum type, GLchar const *const *shaderCode) {
auto shader = glCreateShader(type);
glShaderSource(shader, 1, shaderCode, nullptr);
glCompileShader(shader);
return shader;
}
void MapOverlayRenderer::fillTexture() {
if (!_image.valid()) {
return;
}
glBindTexture(GL_TEXTURE_2D, texture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, static_cast<GLsizei>(_image.size.width), static_cast<GLsizei>(_image.size.height), 0, GL_RGBA,
GL_UNSIGNED_BYTE, _image.data.get());
glGenerateMipmap(GL_TEXTURE_2D);
}
}
The default texture wrapping mode is set to wrap around to the other side of the texture. If any filtering is being done, this will end up sampling pixels from the opposite side of the texture, which explains the thin red border.
To change it to clamping, use this in fillTexture after binding the texture:
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
I am writing a game for Android devices which uses the Android NDK and OpenGL-ES. I am rendering an image to a framebuffer and then using that information in the CPU. More precision would be better, so I used:
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32UI, width, height, 0, GL_RGBA_INTEGER,
GL_UNSIGNED_INT, nullptr);
to create the surface for the (only) color attachment. I selected it because it was the only 32 bit per color surface type that was usable as a color attachment listed on the OpenGL-ES page for glTexImage2D.
This works fine on some devices, but on an Android 6 HTC phone, I get the following errors output from the phone:
E/Adreno-ES20: <core_glClear:62>: WARNING: glClear called on an integer buffer. Buffer contents will be undefined
<oxili_check_sp_rb_fmt_mismatch:86>: WARNING : Rendertarget does not match shader output type.
E/Adreno-ES20: <core_glClear:62>: WARNING: glClear called on an integer buffer. Buffer contents will be undefined
E/Adreno-ES20: <oxili_check_sp_rb_fmt_mismatch:86>: WARNING : Rendertarget does not match shader output type.
Note: These messages are in a log file, no OpenGL errors were returned with glGetError.
Am I getting this error just because it is a buggy ancient phone, or is there a problem with what I am doing?
The OpenGL-ES page on glTexImage2D states that the surface can be used as a color attachment:
Khronos glTexImage2D reference page
The output from the fragment shader is a mediump vec4 (gl_FragColor), but that cannot be changed, right?
Note: the result I get from the code is just the clear color on the phone with the error in the log file (and one other phone which is a later model of the same brand). There are no errors returned from glGetError. And glCheckFramebufferStatus returned that the framebuffer was complete.
Code for creating the framebuffer:
glGenTextures(1, &m_depthMap);
checkGraphicsError();
glBindTexture(GL_TEXTURE_2D, m_depthMap);
checkGraphicsError();
glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT32F, width, height, 0,
GL_DEPTH_COMPONENT, GL_FLOAT, nullptr);
checkGraphicsError();
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
checkGraphicsError();
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
checkGraphicsError();
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
checkGraphicsError();
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
checkGraphicsError();
glBindTexture(GL_TEXTURE_2D, 0);
checkGraphicsError();
glGenFramebuffers(1, &m_depthMapFBO);
checkGraphicsError();
glBindFramebuffer(GL_FRAMEBUFFER, m_depthMapFBO);
checkGraphicsError();
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, m_depthMap, 0);
checkGraphicsError();
glGenTextures(1, &m_colorImage);
checkGraphicsError();
glActiveTexture(activeTextureIndicator);
checkGraphicsError();
glBindTexture(GL_TEXTURE_2D, m_colorImage);
checkGraphicsError();
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32UI, width, height, 0, GL_RGBA_INTEGER,
GL_UNSIGNED_INT, nullptr);
checkGraphicsError();
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
checkGraphicsError();
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
checkGraphicsError();
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
checkGraphicsError();
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
checkGraphicsError();
glBindTexture(GL_TEXTURE_2D, 0);
checkGraphicsError();
glFramebufferTexture2D(GL_FRAMEBUFFER, attachmentIndicator, GL_TEXTURE_2D, m_colorImage, 0);
checkGraphicsError();
GLenum rc = glCheckFramebufferStatus(GL_FRAMEBUFFER);
if (rc != GL_FRAMEBUFFER_COMPLETE) {
std::string c;
switch (rc) {
case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT:
c = "GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT";
break;
case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT:
c = "GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT";
break;
case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS:
c = "GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS";
break;
case GL_FRAMEBUFFER_UNSUPPORTED:
c = "GL_FRAMEBUFFER_UNSUPPORTED";
break;
default:
c = "Unknown return code.";
}
throw std::runtime_error(std::string("Framebuffer is not complete, returned: ") + c);
}
glBindFramebuffer(GL_FRAMEBUFFER, 0);
checkGraphicsError();
Update: It turns out if you use OpenGL ES GLSL version 1.00, you cannot change the output types. I was using GLSL 1.00 to be able to support lower end phones and old phones. I changed the code so that it would use GLSL 3.00 if the call to eglCreateContext with EGL_CONTEXT_CLIENT_VERSION set to 3 succeeded, otherwise it uses GLSL version 1.00 and does not use the integer surface. I am reading the result of the render with glReadPixels:
glReadPixels(0, 0, imageWidth, imageHeight, GL_RGBA_INTEGER, GL_UNSIGNED_INT, data.data());
I changed from calling glClear to calling glClearBufferuiv/glClearBufferfv, if OpenGL ES 3.0 is being used:
if (m_surfaceDetails->useIntTexture) {
std::array<GLuint, 4> color = {0, 0, 0, 4294967295};
glClearBufferuiv(GL_COLOR, 0, color.data());
checkGraphicsError();
GLfloat depthBufferClearValue = 1.0f;
glClearBufferfv(GL_DEPTH, 0, &depthBufferClearValue);
checkGraphicsError();
} else {
glClearColor(0.0, 0.0, 0.0, 1.0);
checkGraphicsError();
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
checkGraphicsError();
}
This works well for all my test devices except for that old HTC phone with android 6.0. On that phone, I am getting no GL errors programmatically or printed out to the debug log (i.e. the previously stated errors about calling glClear on an integer surface are gone). However, I get rgb = 1073741824, a=4294967295. The result I was looking for my test was rgb=2147483647 and a=4294967295. I didn't get the clear color of rgb=0 and a=4294967295, I got a different (weird) value for rgb. Any other ideas, or is the phone just buggy?
Listed below are my new vertex and fragment shaders using OpenGL ES GLSL 3.00.
My vertex shader:
#version 300 es
precision highp float;
uniform mat4 model;
uniform mat4 view;
uniform mat4 proj;
uniform float nearestDepth;
uniform float farthestDepth;
layout(location = 0) in vec3 inPosition;
out vec3 fragColor;
void main() {
gl_Position = proj * view * model * vec4(inPosition, 1.0);
gl_Position.z = -gl_Position.z;
vec4 pos = model * vec4(inPosition, 1.0);
float z = (pos.z/pos.w - farthestDepth)/(nearestDepth - farthestDepth);
if (z > 1.0) {
z = 1.0;
} else if (z < 0.0) {
z = 0.0;
}
fragColor = vec3(z, z, z);
}
My fragment shader:
#version 300 es
precision highp float;
precision highp int;
in vec3 fragColor;
layout(location = 0) out uvec4 fragColorOut;
void main() {
float maxUint = 4294967295.0;
fragColorOut = uvec4(
uint(fragColor.r * maxUint),
uint(fragColor.g * maxUint),
uint(fragColor.b * maxUint),
uint(maxUint));
}
Update 2
Thanks for all the comments. I ran some tests and changed my shaders in response to the comments:
So I checked the precision of highp and mediump floats and ints with glGetShaderPrecisionFormat and here's what I got:
GLint range[2];
GLint precision;
glGetShaderPrecisionFormat(GL_FRAGMENT_SHADER, GL_HIGH_FLOAT, range, &precision);
// range[0] = 127
// range[1] = 127
// precision = 23
glGetShaderPrecisionFormat(GL_FRAGMENT_SHADER, GL_HIGH_INT, range, &precision);
// range[0] = 31
// range[1] = 31
// precision = 0
glGetShaderPrecisionFormat(GL_FRAGMENT_SHADER, GL_MEDIUM_FLOAT, range, &precision);
// range[0] = 15
// range[1] = 15
// precision = 10
glGetShaderPrecisionFormat(GL_FRAGMENT_SHADER, GL_MEDIUM_INT, range, &precision);
// range[0] = 15
// range[1] = 15
// precision = 0
A couple of things to note:
highp floats and ints are supported by this phone (or so it says).
most of these values match up to the values stated in the OpenGL ES 3 reference card: https://www.khronos.org/files/opengles3-quick-reference-card.pdf - except: the medium precision float which is supposed to have range 14, but claims range 15.
But using mediump in the fragment shader is more correct since all the phones are required to support it. So I switched to using mediump floats and ints and GL_RGBA16UI surface:
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16UI, width, height, 0, GL_RGBA_INTEGER,
GL_UNSIGNED_SHORT, nullptr);
The new shaders are below:
The vertex shader:
#version 300 es
precision highp float;
uniform mat4 model;
uniform mat4 view;
uniform mat4 proj;
uniform float nearestDepth;
uniform float farthestDepth;
layout(location = 0) in vec3 inPosition;
layout(location = 1) in vec3 inColor;
layout(location = 2) in vec2 inTexCoord;
layout(location = 3) in vec3 inNormal;
out mediump vec3 fragColor;
void main() {
gl_Position = proj * view * model * vec4(inPosition, 1.0);
gl_Position.z = -gl_Position.z;
vec4 pos = model * vec4(inPosition, 1.0);
float z = (pos.z/pos.w - farthestDepth)/(nearestDepth - farthestDepth);
if (z > 1.0) {
z = 1.0;
} else if (z < 0.0) {
z = 0.0;
}
fragColor = vec3(z, z, z);
}
The fragment shader:
#version 300 es
precision mediump int;
precision mediump float;
in vec3 fragColor;
layout(location = 0) out uvec4 fragColorOut;
void main() {
// 2^14 the highest value for mediump float. -1 because uint only goes to 2^16-1, see below
float maxUint = 16383.0;
fragColorOut = uvec4(
uint(fragColor.r * maxUint),
uint(fragColor.g * maxUint),
uint(fragColor.b * maxUint),
16383u);
// mediump uint goes from 0 to 2^16-1
fragColorOut = fragColorOut << 2;
}
This works for all devices except that android 6 HTC phone. It returns all 0's for this value. Again, if I clear the depth surface to 0.8f or so, then I get the clear color.
The reason I am using the integer surface is that GL_RGBA32F and GL_RGBA16F internal formats do not support color rendering in OpenGL ES 3.0. GL_RGBA8 is supported but is only 8 bits per channel.
Update 3
My clear code and read code is below. I have code to deal with testing this code to see if the integer surface works. If it does not, useIntTexture will be set to false and the float surface will be used. So the branch of code that should be examined is if useIntTexture is true.
The only difference in the clear the depth buffer to 0.8f and 1.0f is the value of the variable: depthBufferClearValue. The below code has it set to 1.0f (as it should be, 0.8f was just an experiment).
ref.renderDetails->overrideClearColor(clearColor);
if (m_surfaceDetails->useIntTexture) {
auto convert = [](float color) -> GLuint {
return static_cast<GLuint>(std::round(color * std::numeric_limits<uint16_t>::max()));
};
std::array<GLuint, 4> color = {convert(clearColor.r), convert(clearColor.g), convert(clearColor.b), convert(clearColor.a)};
glClearBufferuiv(GL_COLOR, 0, color.data());
checkGraphicsError();
// the only difference between the clear 0.8f case and the clear
// 1.0f case is the below line. Right now it is clearing 1.0f...
GLfloat depthBufferClearValue = 1.0f;
glClearBufferfv(GL_DEPTH, 0, &depthBufferClearValue);
checkGraphicsError();
} else {
glClearColor(clearColor.r, clearColor.g, clearColor.b, clearColor.a);
checkGraphicsError();
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
checkGraphicsError();
}
ref.renderDetails->draw(0, ref.commonObjectData, drawObjTable,
drawObjTable->zValueReferences().begin(), drawObjTable->zValueReferences().end());
glFinish();
checkGraphicsError();
renderDetails::PostprocessingDataInputGL dataVariant;
if (m_surfaceDetails->useIntTexture) {
/* width * height * 4 color values each a uint16_t in size. */
std::vector<uint16_t> data(static_cast<size_t>(imageWidth * imageHeight * 4), 0.0f);
glReadBuffer(GL_COLOR_ATTACHMENT0);
checkGraphicsError();
glReadPixels(0, 0, imageWidth, imageHeight, colorImageFormat.format, colorImageFormat.type, data.data());
checkGraphicsError();
dataVariant = std::move(data);
} else {
/* width * height * 4 color values each a char in size. */
std::vector<uint8_t> data(static_cast<size_t>(imageWidth * imageHeight * 4), 0);
glReadBuffer(GL_COLOR_ATTACHMENT0);
checkGraphicsError();
glReadPixels(0, 0, imageWidth, imageHeight, colorImageFormat.format, colorImageFormat.type, data.data());
checkGraphicsError();
dataVariant = std::move(data);
}
In case anyone else has the above problem, like I did, making two changes solved my problem:
I made the relevant "out" variable in the fragment shader into a uvec4, as opposed to just a vec4.
I made sure that the "format" argument for my glReadPixels() command was also "_Integer". E.g. in my case, using GL_RED_INTEGER/GL_UNSIGNED_BYTE in the glReadPixels command, instead of GL_RED/GL_UNSIGNED_BYTE. While the original question does not explicitly mention glReadPixels(), I imagine it might still be relevant, as getting the data to the CPU was mentioned.
Hopefully, if anyone else comes across this problem, one/both of the above changes will fix it.
I'm currently having some issues with creating a texture in OpenGL ES 2.0. For some reason, the texture on iOS is offset and the texture on Android is out of alignment. I'm using a .bmp image that I got the pixel data from to create the texture.
Errors::iOS:
Errors::Android:
This is how I render out my texture:
void PolygonRenderer::renderTextures() {
// Data read from the header of the BMP file
unsigned char header[54]; // Each BMP file begins by a 54-bytes header
unsigned int dataPos; // Position in the file where the actual data begins
unsigned int width, height;
unsigned int imageSize; // = width*height*3
// Actual RGB data
unsigned char * data;
// Open the file
FILE * file = fopen(iconPath,"rb");
if (!file) {
log("Image could not be opened\n");
return;
}
if (fread(header, 1, 54, file) != 54) {
log("Not a correct BMP file");
return;
}
if (header[0] != 'B' || header[1] != 'M') {
log("Not a correct BMP file");
return;
}
// Read ints from the byte array
dataPos = *(int*)&(header[0x0A]);
imageSize = *(int*)&(header[0x22]);
width = *(int*)&(header[0x12]);
height = *(int*)&(header[0x16]);
if (imageSize == 0) {
imageSize = width * height;
}
log("Image size: %d, %d, total: %d", width, height, imageSize);
if (dataPos == 0) {
dataPos = 54;
}
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glPixelStorei(GL_PACK_ALIGNMENT, 1);
data = new unsigned char[imageSize];
log("Elements read: %d", fread(data, 1, imageSize, file));
fclose(file);
glActiveTexture(GL_TEXTURE0);
GLuint textureId;
glGenTextures(1, &textureId);
glBindTexture(GL_TEXTURE_2D, textureId);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB,
GL_UNSIGNED_BYTE, data);
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);
checkForGLError("Add texture:");
log("Texture created: %d", textureId);
loadingTexture = textureId;
}
The reason I am using RGB, even though .bmp saves as BGR is because I use the R channel as opacity and set the color externally. (Also this is OpenGL ES 2.0, which only has GL_RGB and GL_RGBA.)
Rendering polygon excerpt:
glUseProgram(shader.get(1));
GLuint projectionLocation =
glGetUniformLocation(shader.get(1), "projection");
glUniformMatrix4fv(projectionLocation, 1, GL_FALSE, projectionMatrix);
GLuint viewLocation = glGetUniformLocation(shader.get(1), "view");
glBindTexture(GL_TEXTURE_2D, loadingTexture);
glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);
glEnableVertexAttribArray(2);
glUniformMatrix4fv(viewLocation, 1, GL_FALSE,
&frame.combinedMatrix[16 * loadingPolygons[1].ViewGroup]);
glBindBuffer(GL_ARRAY_BUFFER, loadingPolygons[1].Buffer);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(GLfloat),
0);
glBindBuffer(GL_ARRAY_BUFFER, loadingPolygons[1].Buffer);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(GLfloat),
(GLvoid*)(3 * sizeof(GLfloat)));
glBindBuffer(GL_ARRAY_BUFFER, loadingPolygons[1].Buffer);
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(GLfloat),
(GLvoid*)(6 * sizeof(GLfloat)));
checkForGLError("In Renderer");
glDrawArrays(GL_TRIANGLES, 0, 6);
glDisableVertexAttribArray(0);
glDisableVertexAttribArray(1);
glDisableVertexAttribArray(2);
glBindTexture(GL_TEXTURE_2D, 0);
Vertex Shader Source:
std::string textureVertex =
"attribute vec3 vertexloc; \n"
"attribute vec3 vertexcol; \n"
"attribute vec2 vertexuv; \n"
"varying vec2 TexCoords; \n"
"varying vec3 textColor; \n"
"uniform mat4 projection; \n"
"uniform mat4 view; \n"
"void main() \n"
"{ \n"
" gl_Position = projection * view * vec4(vertexloc, 1.0); \n"
" TexCoords = vertexuv; \n"
" textColor = vertexcol; \n"
"}";
Fragment Shader Source:
std::string textureFragment =
"precision mediump float; \n"
"varying vec2 TexCoords; \n"
"varying vec3 textColor; \n"
"uniform sampler2D text; \n"
"void main() \n"
"{ \n"
" vec4 sampled = vec4(1.0, 1.0, 1.0, texture2D(text, TexCoords).r); \n"
" gl_FragColor = vec4(textColor, 1.0) * sampled; \n"
"}";
Edit: Added the second loadingPolygon value:
w = 250;
h = 43;
x = sWindowWidth / 2 - w / 2;
y = - sWindowHeight / 4 - h / 2;
temp = {
x, y + h, 0.3,
textureColor.x, textureColor.y, textureColor.z,
0, 1,
x, y, 0.3,
textureColor.x, textureColor.y, textureColor.z,
0, 0,
x + w, y, 0.3,
textureColor.x, textureColor.y, textureColor.z,
1, 0,
x, y + h, 0.3,
textureColor.x, textureColor.y, textureColor.z,
0, 1,
x + w, y, 0.3,
textureColor.x, textureColor.y, textureColor.z,
1, 0,
x + w, y + h, 0.3,
textureColor.x, textureColor.y, textureColor.z,
1, 1
};
polygonRenderer.addLoadingPolygon(temp);
Edit 2: Android eglconfig:
setEGLConfigChooser(8, 8, 8, 8, 16, 0);
Edit 3: Creating a shader:
GLuint Shader::LoadShader(std::string vertexSource,
std::string fragmentSource, std::map<int, std::string> attribs) {
GLuint vertexShader = LoadSubShader(vertexSource, GL_VERTEX_SHADER);
GLuint fragmentShader = LoadSubShader(fragmentSource, GL_FRAGMENT_SHADER);
if (vertexShader == 0 || fragmentShader == 0)
return 0;
GLuint program = glCreateProgram();
if (program == 0) {
log("Error compiling shader");
return 0;
}
glAttachShader(program, vertexShader);
glAttachShader(program, fragmentShader);
std::map<int, string>::iterator it;
for(it = attribs.begin(); it != attribs.end(); it++) {
glBindAttribLocation(program, it->first, it->second.c_str());
}
glLinkProgram(program);
GLint linked;
glGetProgramiv(program, GL_LINK_STATUS, &linked);
if (!linked) {
log("ERROR");
GLint infoLen = 0;
glGetProgramiv(program, GL_INFO_LOG_LENGTH, &infoLen);
if (infoLen > 1) {
char infoLog[512];
glGetProgramInfoLog(program, infoLen, NULL, infoLog);
log("Error linking program: %s", infoLog);
}
glDeleteProgram(program);
return GL_FALSE;
}
// glDetachShader(program, vertexShader);
// glDetachShader(program, fragmentShader);
return program;
}
Riddle me this,
I recently published a game for Android (play.google.com/store/apps/details?id=com.quackers if you want to witness the problems first hand), and initial feedback suggests that the thing doesn't run properly on some devices. I've since got ahold of one of the offending tablets (a Samsung Galaxy Tab 2 7.0) and it turns out it does run, it just wasn't rendering things properly.
A few digs later and I've discovered that it's a texturing issue. Textures are being loaded okay, but they're not being rendered - not the usual black squares you often get with OpenGL when something goes wrong - nothing at all.
This is OpenGL ES 2.0, doing an SDL/C++/ndk thing. While there are similar problems on the net, much of it involves ES 1.0 and regards a different issue - texture dimensions not being powers of two (e.g. 64x64, 128x128, 256x256 etc.) or some wacky compression stuff which doesn't apply here.
I've stripped out all of my rendering code and have gone back to basics - rendering a textured square (in a not particularly optimised manner).
Pre-loop code:
SDL_Init(SDL_INIT_VIDEO);
SDL_LogSetAllPriority(SDL_LOG_PRIORITY_VERBOSE);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0);
SDL_DisplayMode mode;
SDL_GetDisplayMode(0,0, &mode);
_currentWidth = mode.w;
_currentHeight = mode.h;
SDL_GL_SetAttribute(SDL_GL_ACCELERATED_VISUAL, 1);
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
_screen = SDL_CreateWindow("window", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, _currentWidth, _currentHeight, SDL_WINDOW_OPENGL | SDL_WINDOW_FULLSCREEN | SDL_WINDOW_RESIZABLE);
SDL_GLContext context = SDL_GL_CreateContext(_screen);
SDL_GL_MakeCurrent(_screen, context);
glViewport(0, 0, _currentWidth, _currentHeight);
//---
GLuint vs = glCreateShader(GL_VERTEX_SHADER);
const char *vs_source = "attribute highp vec2 coord2d; "
"attribute highp vec2 texcoord;"
"varying highp vec2 f_texcoord;"
"void main(void) { "
"gl_Position = vec4(coord2d, 0.0, 1.0); "
"f_texcoord = texcoord;"
"}";
glShaderSource(vs, 1, &vs_source, NULL);
glCompileShader(vs);
GLuint fs = glCreateShader(GL_FRAGMENT_SHADER);
const char *fs_source = "varying highp vec2 f_texcoord;"
"uniform sampler2D texture;"
"void main(void) { "
"vec2 flipped_texcoord = vec2(f_texcoord.x, 1.0 - f_texcoord.y);"
"gl_FragColor = texture2D(texture, flipped_texcoord);"
"}";
glShaderSource(fs, 1, &fs_source, NULL);
glCompileShader(fs);
_program = glCreateProgram();
glAttachShader(_program, vs);
glAttachShader(_program, fs);
glLinkProgram(_program);
//---
GLuint vs2 = glCreateShader(GL_VERTEX_SHADER);
const char *vs_source2 = "attribute vec2 coord2d; "
"void main(void) { "
"gl_Position = vec4(coord2d, 0.0, 1.0); "
"}";
glShaderSource(vs2, 1, &vs_source2, NULL);
glCompileShader(vs2);
GLuint fs2 = glCreateShader(GL_FRAGMENT_SHADER);
const char *fs_source2 = "uniform lowp vec4 u_colour;"
"void main(void) { "
"gl_FragColor = u_colour;"
"}";
glShaderSource(fs2, 1, &fs_source2, NULL);
glCompileShader(fs2);
_flatProgram = glCreateProgram();
glAttachShader(_flatProgram, vs2);
glAttachShader(_flatProgram, fs2);
glLinkProgram(_flatProgram);
glEnable(GL_BLEND);
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
//---------------------------------------
_screenRect.x = -1.0;
_screenRect.y = -1.0;
_screenRect.w = 2.0;
_screenRect.h = 2.0;
_superDuperFrameBuffer = 0;
_depthRenderBuffer = 0;
glGenTextures(1, &_screenTexture);
glBindTexture(GL_TEXTURE_2D, _screenTexture);
if(_currentWidth < SCREENWIDTH*2 || _currentHeight < SCREENHEIGHT*2) {
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
}
else {
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
}
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, SCREENWIDTH, SCREENHEIGHT, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
glBindTexture(GL_TEXTURE_2D, 0);
glGenRenderbuffers(1, &_depthRenderBuffer);
glBindRenderbuffer(GL_RENDERBUFFER, _depthRenderBuffer);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8_OES, SCREENWIDTH, SCREENHEIGHT);
glBindRenderbuffer(GL_RENDERBUFFER, 0);
// create a framebuffer object
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glGenFramebuffers(1, &_superDuperFrameBuffer);
glBindFramebuffer(GL_FRAMEBUFFER, _superDuperFrameBuffer);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, _screenTexture, 0);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, _depthRenderBuffer);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, _depthRenderBuffer);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
_defaultFrameBuffer = 0;
glGetIntegerv(GL_FRAMEBUFFER_BINDING, &_defaultFrameBuffer);
glClearColor(0.0f, 0.0f, 1.0f, 1.0f);
glEnable(GL_BLEND);
glEnable(GL_TEXTURE_2D);
SDL_Surface* testSurface = IMG_Load("graphics/bg_01_0.png");
uint32_t rmask;
uint32_t gmask;
uint32_t bmask;
uint32_t amask;
#if SDL_BYTEORDER == SDL_BIG_ENDIAN
rmask = 0xff000000;
gmask = 0x00ff0000;
bmask = 0x0000ff00;
amask = 0x000000ff;
#else
rmask = 0x000000ff;
gmask = 0x0000ff00;
bmask = 0x00ff0000;
amask = 0xff000000;
#endif
SDL_Surface *tempSurface = SDL_CreateRGBSurface(0, testSurface->w, testSurface->h, 32, rmask, gmask, bmask, amask);
SDL_SetSurfaceBlendMode(tempSurface, SDL_BLENDMODE_BLEND);
SDL_BlitSurface(testSurface, NULL, tempSurface, NULL);
testSurface = tempSurface;
SDL_FreeSurface(tempSurface);
GLint uniformTexture = glGetUniformLocation(_program, "texture");
_testTexture = 0;
glGenTextures(1, &_testTexture);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, _testTexture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
//glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
//glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glUniform1i(uniformTexture, /*GL_TEXTURE*/0);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 256, 256, 0, GL_RGBA, GL_UNSIGNED_BYTE, testSurface->pixels);
_vboTest = 0;
_vbo_cube_texcoords = 0;
glGenBuffers(1, &_vboTest);
glGenBuffers(1, &_vbo_cube_texcoords);
loop:
...
_quadColour[0] = 0.0f;
_quadColour[1] = 255.0f;
_quadColour[2] = 0.0f;
_quadColour[3] = 1.0f;
drawSquare(0, 0, 20, 20);
glViewport(0, 0, SCREENWIDTH, SCREENHEIGHT);
GLfloat x1 = 0, x2 = 8, y1 = 0, y2 = 8;
glUseProgram(_program);
GLint attributeCoord2d = glGetAttribLocation(_program, "coord2d");
GLint attributeTexcoord = glGetAttribLocation(_program, "texcoord");
glEnableVertexAttribArray(attributeTexcoord);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, _testTexture);
GLfloat cube_texcoords[] = {
0.0, 1.0,
0.0, 0.0,
1.0, 0.0,
1.0, 1.0,
};
glBindBuffer(GL_ARRAY_BUFFER, _vbo_cube_texcoords);
glVertexAttribPointer(attributeTexcoord, 2, GL_FLOAT, GL_FALSE, 0, 0);
glBufferData(GL_ARRAY_BUFFER, sizeof(cube_texcoords), cube_texcoords, GL_STATIC_DRAW);
glEnableVertexAttribArray(attributeCoord2d);
GLfloat triangle_vertices[] = {
x1, y2,
x1, y1,
x2, y1,
x2, y2,
};
glBindBuffer(GL_ARRAY_BUFFER, _vboTest);
glVertexAttribPointer(attributeCoord2d, 2, GL_FLOAT, GL_FALSE, 0, 0);
glBufferData(GL_ARRAY_BUFFER, sizeof(triangle_vertices), triangle_vertices, GL_STATIC_DRAW);
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
SDL_GL_SwapWindow(_screen);
...
obviously there's some stuff I've only added for testing purposes, like converting textures to RGBA and whatever. It draws a little green square, then a textured square.
Code might be messy but the point is this - two different results:
Galaxy Tab 2 7.0 (bork)
http://i.imgur.com/ht6LvFV.png
Nexus 7 (correct)
http://i.imgur.com/p4acmIq.png
How do I fix this?
Power-of-two textures are still required on many GLES 2.0 devices. From section 3.8.2 of the GLES 2.0 spec (https://www.khronos.org/registry/gles/specs/2.0/es_full_spec_2.0.25.pdf):
"Calling a sampler from a fragment shader will return (R; G;B;A) =
(0; 0; 0; 1) if any of the following conditions are true: ... A two-dimensional sampler is called, the corresponding texture image is a non-power-of-two image (as described in the Mipmapping discussion of section 3.7.7), and either the texture wrap mode is not CLAMP_TO_EDGE, or the minification filter is neither NEAREST nor LINEAR."
Assuming SCREENHEIGHT/SCREENWIDTH are the dimensions of your device, you're violating this restriction. You can ignore this restriction if your device supports some NPOT extension, for instance GL_OES_texture_npot (https://www.khronos.org/registry/gles/extensions/OES/OES_texture_npot.txt), although in my experience, some devices that report this extension still sample textures as black when the npot texture is the color target of a framebuffer. The best course is to just always use POT render targets in ES 2.0.
There are a few errors and possible issues in this code:
Your fragment shader should not compile if the GLSL compiler is strict about error checking. Since you do not specify a default precision, and there is no default precision for float/vector/matrix types, you need an explicit precision for all declarations. It is missing for this variable:
vec2 flipped_texcoord = vec2(f_texcoord.x, 1.0 - f_texcoord.y);
If you want to stick with highp, this should be:
highp vec2 flipped_texcoord = vec2(f_texcoord.x, 1.0 - f_texcoord.y);
This call has a bad argument:
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, _screenTexture, 0);
Since you're attaching a texture, the 3rd argument must be GL_TEXTURE_2D (you would need to use glFramebufferRenderbuffer for attaching a renderbuffer):
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, screenTexture, 0);
Make sure that the OES_packed_depth_stencil extension is supported on the device, since you're using it here:
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8_OES, SCREENWIDTH, SCREENHEIGHT);
This code sequence does not make much sense:
glBindFramebuffer(GL_FRAMEBUFFER, 0);
_defaultFrameBuffer = 0;
glGetIntegerv(GL_FRAMEBUFFER_BINDING, &_defaultFrameBuffer);
You just bound framebuffer 0, so the current framebuffer binding will always be 0 here. If you're concerned that the default framebuffer might not be 0, you have to query the value before the first time you change the binding.
This is not a valid call in ES 2.0:
glEnable(GL_TEXTURE_2D);
Enabling textures was only needed in fixed function OpenGL. Once you use shader, it will just use textures anytime the shader... uses textures. No need to explicitly enable anything.