Related
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 am currently working on a rtsp player on android using ffmpeg to connect and decode the video stream. I would like to use OpenGL es 2.0 to convert the YUV frame to RGB frame and display it but i am blocked (it's the first time i use opengl).
I will try to explain clearly what is my problem.
From the NDK android i initialize an opengl context (from the thread i want to use to display images) using this method :
//
EGLint attribs[] = {
EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
EGL_BLUE_SIZE, 8,
EGL_GREEN_SIZE, 8,
EGL_RED_SIZE, 8,
EGL_ALPHA_SIZE, 8,
EGL_NONE
};
EGLint contextAttrs[] = {
EGL_CONTEXT_CLIENT_VERSION, 2,
EGL_NONE
};
LOGI("Initializing context");
if((display = eglGetDisplay(EGL_DEFAULT_DISPLAY)) == EGL_NO_DISPLAY)
{
closeContext();
return;
}
if(!eglInitialize(display, 0, 0))
{
closeContext();
return;
}
if(!eglChooseConfig(display, attribs, &config, 1, &numConfigs))
{
closeContext();
return;
}
if(!eglGetConfigAttrib(display, config, EGL_NATIVE_VISUAL_ID, &format))
{
closeContext();
return;
}
ANativeWindow_setBuffersGeometry(window, 0, 0, format);
if(!(surface = eglCreateWindowSurface(display, config, window, 0)))
{
closeContext();
return;
}
if(!(context = eglCreateContext(display, config, 0, contextAttrs)))
{
closeContext();
return;
}
if(!eglMakeCurrent(display, surface, surface, context))
{
closeContext();
return;
}
if(!eglQuerySurface(display, surface, EGL_WIDTH, &width) || !eglQuerySurface(display, surface, EGL_HEIGHT, &height))
{
closeContext();
return;
}
LOGI("EGLWIDTH : %d EGLHEIGHT : %d ", (int)width, (int)height);
isInitEGLContext = 1;
Then i setup the graphics using this method :
//
//Load Vertex and Fragment Shader, attach shader and link program
programId = createProgram(kVertexShader, kFragmentShader);
LOGI("Program id : %d error : %d",(int) programId, glGetError());
if(!programId)
{
LOGI("Could not create program");
return;
}
// get index of the generic vertex attribute bound to vPosition
positionObject = (int) glGetAttribLocation(programId, "vPosition");
// get index of the generic vertex attribute bound to vTexCoord
texturePosition = (int) glGetAttribLocation(programId, "vTexCoord");
// get the location of yTexture within the program (corresponding to program id)
yuv_texture_object[0] = glGetUniformLocation(programId, "yTexture");
// get the location of uTexture within the program
yuv_texture_object[1] = glGetUniformLocation(programId, "uTexture");
// get the location of vTexture within the program
yuv_texture_object[2] = glGetUniformLocation(programId, "vTexture");
// Setup width of each planes (display size)
stream_yuv_width[0] = 800;
stream_yuv_width[1] = 400;
stream_yuv_width[2] = 400;
// Setup height of each planes (display size)
stream_yuv_height[0] = 600;
stream_yuv_height[1] = 300;
stream_yuv_height[2] = 300;
//set the view port
glViewport(0,0,stream_yuv_width[0],stream_yuv_height[0]);
LOGI("glViewPort() %d ", glGetError());
I have hardcoded the display size (for now) until i get something that work.
The createProgram method, load the shaders, create the program, compile and link the shaders successfully.
Here are my shaders :
const char kVertexShader[] =
"attribute vec4 vPosition;\n"
"attribute vec2 vTexCoord;\n"
"varying vec2 v_vTexCoord;\n"
"void main() {\n"
"gl_Position = vPosition;\n"
"v_vTexCoord = vTexCoord;\n"
"}\n";
const char kFragmentShader[] =
"precision mediump float; \n"
"varying vec2 v_vTexCoord;\n"
"uniform sampler2D yTexture;\n"
"uniform sampler2D uTexture;\n"
"uniform sampler2D vTexture;\n"
"void main() {\n"
"float nx, ny; \n"
"nx = v_vTexCoord.x; \n"
"ny = v_vTexCoord.y; \n"
"float y=texture2D(yTexture, v_vTexCoord).r;\n"
"float u=texture2D(uTexture, vec2(nx / 2.0, ny / 2.0)).r;\n"
"float v=texture2D(vTexture, vec2(nx / 2.0, ny / 2.0)).r;\n"
"y = 1.1643 * (y - 0.0625);\n"
"u = u - 0.5; \n"
"v = v - 0.5; \n"
"float r=y + 1.5958 * v;\n"
"float g=y - 0.39173 * u - 0.81290 * v;\n"
"float b=y + 2.017 * u;\n"
"gl_FragColor = vec4(r, g, b, 1.0);\n"
"}\n";
const GLfloat kVertexInformation[] = {
-1.0f, 1.0f, // TexCoord 0 top left
-1.0f,-1.0f, // TexCoord 1 bottom left
1.0f,-1.0f, // TexCoord 2 bottom right
1.0f, 1.0f // TexCoord 3 top right
};
const GLshort kTextureCoordinateInformation[] = {
0, 0, // TexCoord 0 top left
0, 1, // TexCoord 1 bottom left
1, 1, // TexCoord 2 bottom right
1, 0 // TexCoord 3 top right
};
const GLuint kStride = 0;//COORDS_PER_VERTEX * 4;
const GLshort kIndicesInformation[] = {
0, 1, 2,
0, 2, 3
};
Then i setup the yuv textures and the render to textures, at this moment yuv_width[i] and yuv_height[i] are set to correct value:
void setupYUVTexture()
{
//Setup the pixel alignement
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
LOGI("glPixelStorei() : %d ", glGetError());
int i = 0;
for(i = 0 ; i < 3 ; ++i)
{
//Check if the texture already setup
if(yuv_texture_id[i] != 0)
{
glDeleteTextures(1, &yuv_texture_id[i]);
yuv_texture_id[i] = 0;
}
// Active the i texture
glActiveTexture(GL_TEXTURE0 + i);
//Generate the texture name
glGenTextures(1, &yuv_texture_id[i]);
// Bind the texture
glBindTexture(GL_TEXTURE_2D, yuv_texture_id[i]);
// Setup the texture parameters
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
//Define the texture image
glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, yuv_width[i], yuv_height[i], 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, NULL);
LOGI("glTexImage2D() %d ", glGetError());
}
}
void renderToTexture()
{
// Generate framebuffer object name
glGenFramebuffers(1, &frameBufferObject);
//Bind the framebuffer
glBindFramebuffer(GL_FRAMEBUFFER, frameBufferObject);
//Generate render buffer object name
glGenRenderbuffers(1, &renderBufferObject);
//Bind render buffer
glBindRenderbuffer(GL_RENDERBUFFER, renderBufferObject);
//Create and initialize render buffer for display RGBA with the same size of the viewport
glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA4, 800, 600);
//Attach render buffer to frame buffer object
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, renderBufferObject);
//Attach y plane to frame buffer object
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, yuv_texture_id[0], 0);
//Attach u plane to frame buffer object
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, yuv_texture_id[1], 0);
//Attach v plane to frame buffer object
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, yuv_texture_id[2], 0);
// Bind the framebuffer
glBindFramebuffer(GL_FRAMEBUFFER, 0);
//Check if the framebuffer is correctly setup
GLint status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
if(status != GL_FRAMEBUFFER_COMPLETE)
{
LOGI(" FBO setting fault : %d ", status);
return;
}
}
To finish, my draw frame method :
void drawFrame()
{
LOGI("DrawFrame");
glBindFramebuffer(GL_FRAMEBUFFER, frameBufferObject);
printGLError("glBindFramebuffer");
glUseProgram(programId);
printGLError("glUseProgram");
int i = 0;
for(i = 0 ; i < 3 ; ++i)
{
glActiveTexture(GL_TEXTURE0 + i);
printGLError("glActiveTexture");
glBindTexture(GL_TEXTURE_2D, yuv_texture_object[i]);
printGLError("glBindTexture");
glUniform1i(yuv_texture_object[i], i);
printGLError("glUniform1i");
LOGI("Plan : %d Largeur : %d Hauteur : %d ", i, yuv_width[i], yuv_height[i]);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0,yuv_width[i], yuv_height[i], GL_LUMINANCE, GL_UNSIGNED_BYTE, yuv_planes[i]);
printGLError("glTexSubImage2D");
}
glVertexAttribPointer(positionObject, 2, GL_FLOAT, GL_FALSE, kStride, kVertexInformation);
printGLError("glVertexAttribPointer");
glVertexAttribPointer(texturePosition, 2, GL_SHORT, GL_FALSE, kStride, kTextureCoordinateInformation);
printGLError("glVertexAttribPointer");
glEnableVertexAttribArray(positionObject);
printGLError("glVertexAttribArray");
glEnableVertexAttribArray(texturePosition);
printGLError("glVertexAttribArray");
glBindFramebuffer(GL_FRAMEBUFFER, 0);
printGLError("glBindFramebuffer");
glDrawElements(GL_TRIANGLE_STRIP, 6, GL_UNSIGNED_SHORT, kIndicesInformation);
printGLError("glDrawElements");
eglSwapBuffers(display, surface);
printGLError("eglSwapBuffers");
}
I initialize once the opengl textures and other attributes which are necessary, then when a frame is decode i recopy y buffer into yuv_planes[0], u buffer in yuv_planes[ 1] and v buffer in yuv_planes[2].
Once a frame is correctly decoded using ffmpeg i call in this order :
- initContext()
- setupGraphics()
- setupYUVTexture()
- renderToTexture()
then i call drawFrame. Of course, when everything is initialize i call directly drawFrame after each decoded frame.
There is the output i have now.
The size of the image is correct, but now i am block here. I don't understand why the image display is green !
Any ideas
That's a lot of code to go through and a lot of things that can go wrong ;). To debug these kind of issues, I would go step by step.
just output red (gl_FragColor = vec4(1.0, 0.5, 0.5, 1.0)) to make sure your configuration is working properly.
try to output every texture in grayscale. (gl_FragColor = vec4(y, y, y, 1.0))
If all that is working then it most likely means your yuv => rgb conversion is wrong somewhere.
If that's not working, then I would suspect something in the texture mapping. Double check your glTexSubImage2D call. You might need to pass a different stride or use a different coordinate system.
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.
EDIT: Right, fixed it :D Issue was that I was trying to set the Projection matrix before calling glUseProgram()
I'm starting out with GL ES 2.0 on Android, and am trying to migrate some of my code over from 1.1 I've defined vertex and frag shaders as per the official docs, and after some googling I understand how the Model/Projection matrices work together, yet I can't seem to get anything but a blank screen.
I'm passing in a model view matrix to my vert shader, and am multiplying it with the ortho projection before multiplying the resulting mvp matrix with the vertex position. Here are my shaders to clarify:
Vertex Shader
attribute vec3 Position;
uniform mat4 Projection;
uniform mat4 ModelView;
void main() {
mat4 mvp = Projection * ModelView;
gl_Position = mvp * vec4(Position.xyz, 1);
}
Fragment Shader
precision mediump float;
uniform vec4 Color;
void main() {
gl_FragColor = Color;
}
I'm building the projection matrix in my renderer's onSurfaceChangedFunction():
int projectionHandle = GLES20.glGetUniformLocation(shaderProg, "Projection");
Matrix.orthoM(projection, 0, -width / 2, width / 2, -height / 2, height / 2, -10, 10);
GLES20.glUniformMatrix4fv(projectionHandle, 1, false, projection, 0);
Then in my onDrawFrame(), I call each actor's draw routine, which looks like
nt positionHandle = GLES20.glGetAttribLocation(Renderer.getShaderProg(), "Position");
nt colorHandle = GLES20.glGetAttribLocation(Renderer.getShaderProg(), "Color");
nt modelHandle = GLES20.glGetUniformLocation(Renderer.getShaderProg(), "ModelView");
float[] modelView = new float[16];
Matrix.setIdentityM(modelView, 0);
Matrix.rotateM(modelView, 0, rotation, 0, 0, 1.0f);
Matrix.translateM(modelView, 0, position.x, position.y, 1.0f);
GLES20.glUniformMatrix4fv(modelHandle, 1, false, modelView, 0);
GLES20.glUniform4fv(colorHandle, 1, color.toFloatArray(), 0);
GLES20.glVertexAttribPointer(positionHandle, 3, GLES20.GL_FLOAT, false, 0, vertBuffer);
GLES20.glEnableVertexAttribArray(positionHandle);
GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, vertices.length / 3);
GLES20.glDisableVertexAttribArray(positionHandle);
I realize that I can optimize this a bit, but I just want to get it to work first. The vertices are in a FloatBuffer, and centered around the origin. Any thoughts on what I am doing wrong? I've been checking my code against various tutorials and SO questions/answers, and can't see what I'm doing wrong.
Right, fixed it :D Issue was that I was trying to set the Projection matrix before calling glUseProgram()