Fast way to rasterize a grid of points/pixels - opengl

I want to fill the screen with a grid of points. My desired performance would be about the same speed as drawing that many pixels as a contiguous quad (or equivalent triangle clipped with glViewport). Using GL_POINT primitives (positioned via gl_VertexID, not attribs) or glPolygonStipple are possibilities, but are still a little slower. Here's an example of what I want (though the black points drawn may be yet more sparse):
Are there any other methods to draw this grid? (in a similar time to a smaller quad of the same number of pixels)
Wouldn't it be great if the rasterizer was programmable!
The main point of this is to be able to write to both stencil and colour buffers in this grid pattern from a fragment shader.
EDIT
Some rendering times:
Full screen for me is 1680x1050, GTX670. Times are calculated drawing 10,000 times each frame, no depth test. I draw a quad with a big triangle and clip using glViewport.
Rendering a full screen quad and calling discard for coord%4>0: 0.112ms
Rendering a full screen quad, assigning const colour: 0.059ms
Rendering with glPolygonStipple creating %4 pattern: 0.009ms
Rendering quarter full screen quad: 0.003ms
Rendering a 1x1 quad: 0.002ms (binding VBO and shader, could prob be optimized)
The differences get larger with a more sparse grid, for example %16.
EDIT
OK, I've thrown together a small example. Requires glut and glew libraries:
#include <GL/glew.h>
#include <GL/gl.h>
#include <GL/glut.h>
#include <memory.h>
#include <assert.h>
#include <stdio.h>
#define RESOLUTION_X 1680
#define RESOLUTION_Y 1050
#define USE_32_BIT 0
#define TEST_LOOP 1000 //number of quads to draw per frame
#define WARMUP_MS 1000 //time between switching methods
#define TEST_MS 4000 //time to benchmark for
#define TESTS 6
#define DRAW_GRAPH 1
#define SCALE_MS 0.2f //for drawing the graph
GLuint fbo, colourTex, vbo, shader, shaderPoints, shaderDiscard;
int viewport[2];
int test = 0;
int results_time[TESTS];
int results_frames[TESTS];
float colours[TESTS][3] = {
{1,0,0},
{1,1,0},
{1,0,1},
{0,1,0},
{0,1,1},
{0,0,1},
};
const char* names[TESTS] = {
"full",
"full discard",
"full stipple",
"draw points",
"quarter",
"one"
};
float triangleVerts[9] = {-1,-1,0,-1,4,0,4,-1,0};
const char* vertexShaderSrc = "#version 150\nin vec4 v;\nvoid main() {gl_Position = v;}\n";
const char* vertexShaderPointsSrc = "#version 150\nuniform ivec2 s;\nvoid main() {ivec2 p = ivec2(gl_VertexID%(s.x/4),gl_VertexID/(s.x/4)); gl_Position = vec4(2.0*(p*4+0.5)/s-1.0, 0, 1);}\n";
const char* fragmentShaderSrc = "#version 150\nout vec4 c;\nvoid main() {c = vec4(1,0,0,1);}\n";
const char* fragmentShaderDiscardSrc = "#version 150\nout vec4 c;\nvoid main() {if (int(gl_FragCoord.x)%4>0||int(gl_FragCoord.y)%4>0) discard; c = vec4(1,0,0,1);}\n";
void setupDraw(GLuint program, int x, int y)
{
glUseProgram(program);
glViewport(0, 0, x, y);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
GLuint loc = glGetAttribLocation(program, "v");
glEnableVertexAttribArray(loc);
glVertexAttribPointer(loc, 3, GL_FLOAT, GL_FALSE, 0, 0);
}
void polygonStippleGrid(int x, int y)
{
unsigned char tilePattern[32*32];
memset(tilePattern, 0, sizeof(tilePattern));
for (int j = 0; j < 32; j += y)
{
for (int i = 0; i < 32; i += x)
{
int index = (j * 32 + i);
tilePattern[index / 8] |= 1 << (index % 8);
}
}
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glPolygonStipple(tilePattern);
}
void display()
{
static int lastTime = -1;
int elapsed = glutGet(GLUT_ELAPSED_TIME);
if (lastTime == -1) lastTime = elapsed;
int dt = elapsed - lastTime;
lastTime = elapsed;
static int warmup = WARMUP_MS + 2000;
static int running = TEST_MS;
warmup -= dt;
if (warmup <= 0 && test < TESTS)
{
running -= dt;
results_time[test] += dt;
results_frames[test] += 1;
if (running <= 0)
{
printf("%s %s %.6fms\n", names[test], USE_32_BIT?"rgba32":"rgba8", results_time[test]/(float)(results_frames[test] * TEST_LOOP));
test += 1;
warmup = WARMUP_MS;
running = TEST_MS;
}
}
#if DRAW_GRAPH
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glViewport(0, 0, viewport[0], viewport[1]);
glClear(GL_COLOR_BUFFER_BIT);
float s = 2.0f / TESTS;
glBegin(GL_QUADS);
for (int i = 0; i < TESTS; ++i)
{
if (!results_frames[i]) continue;
glColor3fv(colours[i]);
float x = -1.0f + 2.0f * i / (float)TESTS;
float y = -1.0f + 2.0f * (results_time[i]/(float)(results_frames[i] * TEST_LOOP)) / SCALE_MS;
glVertex2f(x, -1.0f); glVertex2f(x, y); glVertex2f(x + s, y); glVertex2f(x + s, -1.0f);
}
glEnd();
#endif
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
switch (test)
{
case 0: //straight full screen quad
setupDraw(shader, RESOLUTION_X, RESOLUTION_Y);
for (int i = 0; i < TEST_LOOP; ++i)
glDrawArrays(GL_TRIANGLES, 0, 3);
break;
case 1: //full screen quad, discarding pixels in the frag shader
setupDraw(shaderDiscard, RESOLUTION_X, RESOLUTION_Y);
for (int i = 0; i < TEST_LOOP; ++i)
glDrawArrays(GL_TRIANGLES, 0, 3);
break;
case 2: //using polygon stipple to mask out fragments
polygonStippleGrid(4, 4);
glEnable(GL_POLYGON_STIPPLE);
setupDraw(shader, RESOLUTION_X, RESOLUTION_Y);
for (int i = 0; i < TEST_LOOP; ++i)
glDrawArrays(GL_TRIANGLES, 0, 3);
glDisable(GL_POLYGON_STIPPLE);
break;
case 3: //drawing points, but computing the position in the vertex shader
glUseProgram(shaderPoints);
glUniform2i(glGetUniformLocation(shaderPoints, "s"), RESOLUTION_X, RESOLUTION_Y);
for (int i = 0; i < TEST_LOOP; ++i)
glDrawArrays(GL_POINTS, 0, (RESOLUTION_X/4)*(RESOLUTION_Y/4));
break;
case 4: //a quad one quarter of the screen (as a speed comparison)
setupDraw(shader, RESOLUTION_X / 4, RESOLUTION_Y / 4);
for (int i = 0; i < TEST_LOOP; ++i)
glDrawArrays(GL_TRIANGLES, 0, 3);
break;
case 5: //a 1x1 quad (as a speed comparison)
setupDraw(shader,1, 1);
for (int i = 0; i < TEST_LOOP; ++i)
glDrawArrays(GL_TRIANGLES, 0, 3);
break;
default: break;
}
glUseProgram(0);
glDisableVertexAttribArray(0); //HACK: assumes location is always zero
//printf("%i %i %i\n", test, warmup, running);
glFinish();
glutSwapBuffers();
glutPostRedisplay();
assert(glGetError() == GL_NO_ERROR);
}
void reshape(int x, int y)
{
viewport[0] = x;
viewport[1] = y;
}
int main(int argc, char **argv)
{
memset(results_time, 0, sizeof(results_time));
memset(results_frames, 0, sizeof(results_frames));
//init glut
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA);
glutCreateWindow("quadtest");
glutReshapeFunc(reshape);
glutDisplayFunc(display);
glewInit();
//init gl stuff
glGenTextures(1, &colourTex);
glBindTexture(GL_TEXTURE_2D, colourTex);
#if USE_32_BIT
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, RESOLUTION_X, RESOLUTION_Y, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
#else
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, RESOLUTION_X, RESOLUTION_Y, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
#endif
/*
GLuint stencilRB;
glGenRenderbuffers(1, &stencilRB);
glBindRenderbuffer(GL_RENDERBUFFER, stencilRB);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_STENCIL, RESOLUTION_X, RESOLUTION_Y);
*/
glGenFramebuffers(1, &fbo);
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, colourTex, 0);
//glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, stencilRB);
assert(glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE);
glGenBuffers(1, &vbo);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(triangleVerts), triangleVerts, GL_STATIC_DRAW);
GLuint v = glCreateShader(GL_VERTEX_SHADER);
GLuint vp = glCreateShader(GL_VERTEX_SHADER);
GLuint f = glCreateShader(GL_FRAGMENT_SHADER);
GLuint fd = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(v, 1, &vertexShaderSrc, NULL);
glShaderSource(vp, 1, &vertexShaderPointsSrc, NULL);
glShaderSource(f, 1, &fragmentShaderSrc, NULL);
glShaderSource(fd, 1, &fragmentShaderDiscardSrc, NULL);
GLint ok = GL_TRUE;
shader = glCreateProgram();
glAttachShader(shader, v);
glAttachShader(shader, f);
glLinkProgram(shader);
glGetProgramiv(shader, GL_LINK_STATUS, &ok);
assert(ok == GL_TRUE);
/*
char log[512];
int n;
glGetShaderInfoLog(v, 512, &n, log);
printf("%s\n", log);
glGetProgramInfoLog(shader, 512, &n, log);
printf("%s\n", log);
*/
shaderPoints = glCreateProgram();
glAttachShader(shaderPoints, vp);
glAttachShader(shaderPoints, f);
glLinkProgram(shaderPoints);
glGetProgramiv(shaderPoints, GL_LINK_STATUS, &ok);
assert(ok == GL_TRUE);
shaderDiscard = glCreateProgram();
glAttachShader(shaderDiscard, v);
glAttachShader(shaderDiscard, fd);
glLinkProgram(shaderDiscard);
glGetProgramiv(shaderDiscard, GL_LINK_STATUS, &ok);
assert(ok == GL_TRUE);
glDisable(GL_DEPTH_TEST);
assert(glGetError() == GL_NO_ERROR);
glutMainLoop();
return 0;
}
Interestingly, using GL_RGBA32F 32 bit colour impacts performance a fair bit, also bringing back the overhead of the discard method to approximately the same as a full screen quad. The glPolygonStipple method gives dramatic improvements in this case, more so than with 8 bit. There is a discrepancy with the previous glPolygonStipple result too, I can reproduce both and haven't narrowed down the difference yet.
output for GL_RGBA:
full rgba8 0.059ms
full discard rgba8 0.112ms
full stipple rgba8 0.050ms
draw points rgba8 0.079ms
quarter rgba8 0.004ms
one rgba8 <0.001ms
output for GL_RGBA32F:
full rgba32 0.240ms
full discard rgba32 0.241ms
full stipple rgba32 0.101ms
draw points rgba32 0.091ms
quarter rgba32 0.015ms
one rgba32 <0.001ms
Drawing points and positioning from gl_VertexID will beat glPolygonStipple for GL_RGBA32F. I'd assume this trend would carry on for more expensive shaders (or at least memory-intensive).

Are there any other methods to draw this grid?
Exactly this grid? Well in that case your grid has a periodicity of 4 and an offset of -1 in x and -2 in y direction. So the fragment shader to produce it (discarding the "black" pixels) would be
void main()
{
if( ((gl_FragPosition.x-1) % 4) == 0 && ((gl_FragPosition.y-2) % 4) == 0 )
discard;
gl_FragColor = vec4(1,1,1,1);
}
Setting the stencil op to always replace the stencil value, will set the stencil buffer to your ref value everywhere, where no pixels are discarded.
If you can't express your grid by some kind of formula, well, use a texture instead.

The scattered memory writes of a sparse grid may simply mean more overhead that can't be avoided.
Draw GL_POINTs
Use glPolygonStipple
Initialize the stencil buffer with the pattern for a masking a full screen quad
What ever you do do not use the discard method if the fragment shader is expensive[1]. This is really stupid because you clog the pipeline with many threads which don't do anything.
[1] Either takes a long time to execute or uses lots of registers or local memory

Related

Polygon tearing in OpenGL

500x500 grid with 1000 sub Divisions:
Just one question.
Why is this happening ?
#include <iostream>
#include <sstream>
#include <vector>
#define GLEW_STATIC
#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include "glm/glm.hpp"
#include "glm/gtc/matrix_transform.hpp"
#include "GameEngine.hpp"
#include "ShaderProgram.h"
#include "Camera.h"
#include "Mesh.h"
const char *title = "Terrain";
GameEngine engine;
OrbitCamera orbitCamera;
float gYaw = 0.0f;
float gPitch = 1.0f;
float gRadius = 200.0f;
const float MOUSE_SENSTIVITY = 0.25f;
bool gWireFrame = false;
void glfw_onKey(GLFWwindow *window, int key, int scancode, int action, int mode);
void glfw_onMouseMove(GLFWwindow *window, double posX, double posY);
void glfw_onMouseScroll(GLFWwindow *window, double deltaX, double deltaY);
int main()
{
if (!engine.init(1024, 768, title))
{
std::cerr << "OpenGL init failed" << std::endl;
std::cin.get();
return -1;
}
//set callbacks
glfwSetKeyCallback(engine.getWindow(), glfw_onKey);
glfwSetCursorPosCallback(engine.getWindow(), glfw_onMouseMove);
std::vector<Vertex> VER;
std::vector<glm::vec3> verts;
std::vector<unsigned int> indices;
std::vector<glm::vec3> norms;
int subDiv = 1000;
int width = 500;
int height = 500;
int size = 0;
for (int row = 0; row < subDiv; row++)
{
for (int col = 0; col < subDiv; col++)
{
float x = (float)((col * width) / subDiv - (width / 2.0));
float z = ((subDiv - row) * height) / subDiv - (height / 2.0);
glm::vec3 pos = glm::vec3(x, 0, z);
verts.push_back(pos);
}
}
size = subDiv * subDiv;
size = verts.size();
for (int row = 0; row < subDiv -1 ; row++)
{
for (int col = 0; col < subDiv -1; col++)
{
int row1 = row * (subDiv);
int row2 = (row+1) * (subDiv);
indices.push_back(row1+col);
indices.push_back(row1+col+1);
indices.push_back( row2+col+1);
indices.push_back(row1+col);
indices.push_back( row2+col+1);
indices.push_back(row2+col);
}
}
for (int i = 0; i < verts.size(); i++)
{
Vertex vertex;
vertex.position = verts[i];
vertex.normal = glm::vec3(0, 0, 0);
vertex.texCoords = glm::vec2(0, 0);
VER.push_back(vertex);
}
VER.begin();
for (int i = 0; i < indices.size(); i += 3)
{
Vertex a = VER[indices[i]];
Vertex b = VER[indices[i + 1]];
Vertex c = VER[indices[i + 2]];
glm::vec3 p = glm::cross(b.position - a.position, c.position - a.position);
VER[indices[i]].normal += p;
VER[indices[i + 1]].normal += p;
VER[indices[i + 2]].normal += p;
}
for (int i = 0; i < VER.size(); i++)
{
VER[i].normal = glm::normalize(VER[i].normal);
}
glm::vec3 cubePos = glm::vec3(0.0f, 0.0f, -5.0f);
GLuint vbo, vao, ibo;
glGenVertexArrays(1, &vao);
glGenBuffers(1, &vbo);
glBindVertexArray(vao);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER, VER.size() * sizeof(Vertex), &VER[0], GL_STATIC_DRAW);
// Vertex Positions
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (GLvoid*)0);
glEnableVertexAttribArray(0);
// Normals attribute
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (GLvoid*)(3 * sizeof(GLfloat)));
glEnableVertexAttribArray(1);
// Vertex Texture Coords
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), (GLvoid*)(6 * sizeof(GLfloat)));
glEnableVertexAttribArray(2);
int n = indices.size() * sizeof(unsigned int);
glGenBuffers(1, &ibo);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size() * sizeof(unsigned int), &indices[0], GL_STATIC_DRAW);
glBindVertexArray(0);
ShaderProgram shaderProgram;
shaderProgram.loadShaders("shaders/vert.glsl", "shaders/frag.glsl");
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
while (!glfwWindowShouldClose(engine.getWindow()))
{
glfwPollEvents();
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glm::mat4 model, view, projection;
model = glm::mat4(1.0f);
orbitCamera.setLookAt(glm::vec3(0, 0, 0));
orbitCamera.rotate(gYaw, gPitch);
orbitCamera.setRadius(gRadius);
model = glm::translate(model, glm::vec3(0, 0, 0));
//model = glm::scale(model, glm::vec3(1, 0, 1));
//model = scaleMat;
projection = glm::perspective(glm::radians(45.0f), (float)engine.getWidth() / (float)engine.getHeight(), 0.00001f, 100.0f);
shaderProgram.use();
glm::vec3 viewPos;
viewPos.x = orbitCamera.getPosition().x;
viewPos.y = orbitCamera.getPosition().y;
viewPos.z = orbitCamera.getPosition().z;
shaderProgram.setUniform("projection", projection);
shaderProgram.setUniform("view", orbitCamera.getViewMatrix());
shaderProgram.setUniform("model", model);
shaderProgram.setUniform("lightPos", glm::vec3(5, 10, 10));
shaderProgram.setUniform("viewPos", viewPos);
glBindVertexArray(vao);
glDrawElements(GL_TRIANGLES,indices.size(), GL_UNSIGNED_INT, 0);
//glDrawArrays(GL_TRIANGLES, 0, VER.size());
glBindVertexArray(0);
glfwSwapBuffers(engine.getWindow());
}
//cleanup
glDeleteVertexArrays(1, &vao);
glDeleteBuffers(1, &vbo);
glfwTerminate();
return 0;
}
void glfw_onKey(GLFWwindow *window, int key, int scancode, int action, int mode)
{
if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS)
{
glfwSetWindowShouldClose(window, GL_TRUE);
}
if (key == GLFW_KEY_E && action == GLFW_PRESS)
{
gWireFrame = !gWireFrame;
if (gWireFrame)
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
else
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
}
}
void glfw_onMouseMove(GLFWwindow *window, double posX, double posY)
{
static glm::vec2 lastMousePos = glm::vec2(0, 0);
if (glfwGetMouseButton(engine.getWindow(), GLFW_MOUSE_BUTTON_LEFT) == 1)
{
gYaw -= ((float)posX - lastMousePos.x) * MOUSE_SENSTIVITY;
gPitch += ((float)posY - lastMousePos.y) * MOUSE_SENSTIVITY;
}
if (glfwGetMouseButton(engine.getWindow(), GLFW_MOUSE_BUTTON_RIGHT) == 1)
{
float dx = 0.01f * ((float)posX - lastMousePos.x);
float dy = 0.01f * ((float)posY - lastMousePos.y);
gRadius += dx - dy;
}
lastMousePos.x = (float)posX;
lastMousePos.y = (float)posY;
}
This is the main code. Rest is just basic initializing code, nothing fancy.
I've tried changing the swapinterval but that doesn't seems to be the problem.
I can share code for the other classes if anyone wants to take a look. And I've also tried lowering the sub divisions.
Edit*
After increasing the value of far plane to 8000:
Still not crisp.
the edit with second image is telling you what is happening ... if tampering with znear/zfar changes output like that it means your depth buffer has low bitwidth to the range you want to use...
However increasing zfar should make things worse (you just for some reason don't see it maybe its cut off or some weird math accuracy singularity).
for me its usual to select the planes so:
zfar/znear < (2^depth_buffer_bitwidth)/2
check you depth_buffer_bitwidth
Try to use 24 bits (you might have 16 bits right now). That should work on all gfx cards these days. You can try 32 bits too but that will work only on newer cards. I am using this code to get the max I can:
What is the proper OpenGL initialisation on Intel HD 3000?
However you are using GLFW so you need to find how to do it in it ... probably there is some hint for this in it ...
increase znear as much as you can
tampering znear has much much more impact than zfar...
Use linear depth buffer
this is the best option for large depth range views like terrains that covers stuf in whole depth view range. See:
How to correctly linearize depth in OpenGL ES in iOS?
however you need shaders and new api for this... I do not think this is doable in old api but luckily you are on new api already ...
if none of above is enough
You can stack up more frustrums together at a cost of multiple rendering of the same geometry. for more info see:
Is it possible to make realistic n-body solar system simulation in matter of size and mass?
How do you initialize OpenGL?
Are you using GL_BLEND?
Using blending is nice to get anti-aliased polygon edges, however it also means your z-buffer gets updated even when a very translucent fragment is drawn. This prevents other opaque fragments with the same z-depth from being drawn, which might be what is causing those holes. You could try disabling GL_BLEND to see if the issue goes away.
What depth function are you using?
By default it is set to GL_LESS. You might want to try glDepthFunc(GL_LEQUAL); So fragments with the same z-depth will be drawn. However, due to rounding errors this might not solve your problem entirely.

OpenGL Problems with Rendering Multiple Objects

I'm brand new to OpenGL and am having some difficulty rendering multiple objects.
I have a vector each of which has its own VertexBuffer. Then, in the while loop I draw each shape on its own.
It's all well and good when I have many of the same objects (multiple cubes etc.) however, when I add a triangle mesh everything gets all out of whack.
I can have many cubes
I can have a single triangle mesh:
But, when I try to have a cube and then a triangle mesh I get:
I'm totally at a loss for what's going on. The code for my loop is provided below.
while (!glfwWindowShouldClose(window))
{
// Get the size of the window
int width, height;
glfwGetWindowSize(window, &width, &height);
float aspect_ratio = 1 * float(height)/float(width); // corresponds to the necessary width scaling
double xpos, ypos;
glfwGetCursorPos(window, &xpos, &ypos);
// Clear the framebuffer
glClearColor(0.5f, 0.5f, 0.5f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// Enable depth test
glEnable(GL_DEPTH_TEST);
glUniform3f(program.uniform("triangleColor"), 1.0f, 1.0f, 1.0f);
glUniformMatrix4fv(program.uniform("proj"), 1, GL_FALSE, projection.data());
glUniformMatrix4fv(program.uniform("view"), 1, GL_FALSE, view.data());
int tally = 0;
for (int i = 0; i < surfaces.size(); i++) {
Surface *s = surfaces[i];
Vector3f color = s->getColor();
int tempIndex = triangleIndex;
Matrix4f model = s->getModel();
// Convert screen position to world coordinates
double xworld = ((xpos/double(width))*2)-1;
double yworld = (((height-1-ypos)/double(height))*2)-1; // NOTE: y axis is flipped in glfw
if (isPressed && mode == "translate") {
if(tempIndex == i) {
Vector4f center = s->getCenter() + model.col(3);
Vector4f displacement = Vector4f(xworld, yworld, 0, 1) - center;
Matrix4f translation = translateMatrix(displacement(0), displacement(1), displacement(2));
model = translation * s->getModel();
s->setModel(model);
}
}
glUniform3f(program.uniform("triangleColor"), color(0), color(1), color(2));
glUniformMatrix4fv(program.uniform("model"), 1, GL_FALSE, model.data());
glDrawArrays(GL_TRIANGLES, 0, s->getVertices().size());
}
And I initialize each VBO when making the object as
VertexBufferObject VBO;
VBO.init();
VBO.update(Vertices);
program.bindVertexAttribArray("position", VBO);
Surface* s = new Surface(VBO, Vertices, percentScale, 0, transformedCenter, SmoothNormals, FlatNormals, color);
s->setModel(model);
surfaces.push_back(s);
And where Program::bindVertexAttribArray is defined as
GLint Program::bindVertexAttribArray(
const std::string &name, VertexBufferObject& VBO) const
{
GLint id = attrib(name);
if (id < 0)
return id;
if (VBO.id == 0)
{
glDisableVertexAttribArray(id);
return id;
}
VBO.bind();
glEnableVertexAttribArray(id);
glVertexAttribPointer(id, VBO.rows, GL_FLOAT, GL_FALSE, 0, 0);
check_gl_error();
return id;
}
You're not binding any buffers before the draw call. You're probably simply drawing whatever buffer you last bound when you initialised them. You'll need something like this at the end of your loop before glDrawArrays:
...
program.bindVertexAttribArray("position", VBO); // where VBO is the buffer of surface s
glUniform3f(program.uniform("triangleColor"), color(0), color(1), color(2));
glUniformMatrix4fv(program.uniform("model"), 1, GL_FALSE, model.data());
glDrawArrays(GL_TRIANGLES, 0, s->getVertices().size());

OpenGL 4.1: Triangle Flashes and Disappears Immediately after Being Drawn

I want to render a green triangle.
OpenGL Version: 4.1
Shading Language Version: 4.10
Problem
The code below, when executed, shows a green triangle that flashes for an instant and disappears.
I saw this post: A red rectangle drawn on 2D texture disappears right after being drawn, who has a similar issue of disappearing triangles, but his was because he called Swap Buffer multiple times, but I only have one instance of glutSwapBuffers() in the displayFunc().
C++ Code:
#include <iostream>
#include "OpenGLMatrix.h"
#include "BasicPipelineProgram.h"
using namespace std;
int windowWidth = 1280;
int windowHeight = 720;
char windowTitle[512] = "Simple Green Triangle";
// global variables
OpenGLMatrix *matrix;
GLuint buffer;
BasicPipelineProgram *pipelineProgram;
GLint program;
GLuint vao;
// objects to render
int numVertices = 3;
float positions[9] =
{ -1, -1, -1,
1, -1, -1,
-1, 1, -1 }; // 3 vertices of triangle to render
float colors[12] =
{ 0, 1, 0, 1,
0, 1, 0, 1,
0, 1, 0, 1 }; // all vertices green with alpha = 1
void bindProgram() {
// upload model view matrix to shader
float m[16];
matrix->SetMatrixMode(OpenGLMatrix::ModelView);
matrix->GetMatrix(m);
pipelineProgram->SetModelViewMatrix(m);
// upload projection matrix to shader
float p[16];
matrix->SetMatrixMode(OpenGLMatrix::Projection);
matrix->GetMatrix(p);
pipelineProgram->SetProjectionMatrix(p);
}
void displayFunc() {
// computing modelview matrix
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
matrix->LoadIdentity();
// camera at (0,0,1), looking in -z direction, up vector y
matrix->LookAt(0, 0, 1, 0, 0, -1, 0, 1, 0);
bindProgram();
// use the VAO
pipelineProgram->Bind();
glBindVertexArray(vao);
GLint first = 0;
GLsizei count = numVertices;
glDrawArrays(GL_TRIANGLES, first, count);
glBindVertexArray(0);
glutSwapBuffers();
}
void idleFunc() {
// make the screen update
glutPostRedisplay();
}
void reshapeFunc(int w, int h) {
GLfloat aspect = (GLfloat) w / (GLfloat) h;
glViewport(0, 0, w, h);
// setup perspective matrix
matrix->SetMatrixMode(OpenGLMatrix::Projection);
matrix->LoadIdentity();
matrix->Perspective(60.0, aspect, 0.01, 1000.0);
matrix->SetMatrixMode(OpenGLMatrix::ModelView);
}
void initPipelineProgram() {
// initialize shader pipeline program
pipelineProgram = new BasicPipelineProgram();
pipelineProgram->Init();
pipelineProgram->Bind();
program = pipelineProgram->GetProgramHandle();
// VAO (vertex array objects) to contain the VBOs
glGenVertexArrays(1, &vao);
glBindVertexArray(vao);
glBindBuffer(GL_ARRAY_BUFFER, buffer);
// get location index of the "position" shader variable
GLuint loc = glGetAttribLocation(program, "position");
glEnableVertexAttribArray(loc);
const void *offset = (const void*) 0;
GLsizei stride = 0;
GLboolean normalized = GL_FALSE;
glVertexAttribPointer(loc, 3, GL_FLOAT, normalized, stride, offset);
// get location index of the "color" shader variable
loc = glGetAttribLocation(program, "color");
glEnableVertexAttribArray(loc);
offset = (const void*) sizeOfPositions;
stride = 0;
normalized = GL_FALSE;
glVertexAttribPointer(loc, 4, GL_FLOAT, normalized, stride, offset);
glBindVertexArray(0);
}
void initVBO() {
glGenBuffers(1, &buffer);
glBindBuffer(GL_ARRAY_BUFFER, buffer);
glBufferData(GL_ARRAY_BUFFER, sizeOfPositions + sizeOfColors, NULL, GL_STATIC_DRAW);
// upload position data
glBufferSubData(GL_ARRAY_BUFFER, 0, sizeOfPositions, positions);
// upload color data
glBufferSubData(GL_ARRAY_BUFFER, sizeOfPositions, sizeOfColors, colors);
}
void initScene(int argc, char *argv[])
{
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
glEnable(GL_DEPTH_TEST);
matrix = new OpenGLMatrix();
initVBO();
initPipelineProgram();
}
int main(int argc, char *argv[])
{
cout << "Initializing GLUT..." << endl;
glutInit(&argc,argv);
cout << "Initializing OpenGL..." << endl;
#ifdef __APPLE__
glutInitDisplayMode(GLUT_3_2_CORE_PROFILE | GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH | GLUT_STENCIL);
#else
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH | GLUT_STENCIL);
#endif
glutInitWindowSize(windowWidth, windowHeight);
glutInitWindowPosition(0, 0);
glutCreateWindow(windowTitle);
// glut callback functions
glutDisplayFunc(displayFunc);
glutIdleFunc(idleFunc);
glutReshapeFunc(reshapeFunc);
initScene(argc, argv);
glutMainLoop();
}
In the code, OpenGLMatrix *matrix and BasicPipelineProgram *pipelineProgram are classes defined elsewhere, and whose code, unless requested, I don't think we need to worry about. "position" and "color" in initPipelineProgram() refer to variables in the GLSL shader specification:
#version 150
in vec3 position;
in vec4 color;
out vec4 col;
uniform mat4 modelViewMatrix;
uniform mat4 projectionMatrix;
void main()
{
// compute the transformed and projected vertex position (into gl_Position)
// compute the vertex color (into col)
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0f);
col = color;
}
I don't understand why the triangle flashes and then immediately disappears??
I'm a beginner in OpenGL and any help is greatly appreciated. Thank you!

OpenGL repeated calls to glTexImage2D and alpha blending

This is more out of curiosity than for any practical purpose: is there anything in the OpenGL specification that suggests that calling glTexImage2D many times (e.g., once per frame) is illegal? I mean illegal as in 'it could produce wrong results', not just inefficient (suppose I don't care about the performance impact of not using glTexSubImage2D instead).
The reason I'm asking is that I noticed some very odd artifacts when drawing overlapping, texture-mapped primitives that use a partly-transparent texture which is loaded once per every frame using glTexImage2D (see the attached picture): after a few seconds (i.e., a few hundred frames), small rectangular black patches appear on the screen (they're actually flipping between black and normal between consecutive frames).
I'm attaching below the simplest example code I could write that exhibits the problem.
#include <stdio.h>
#ifndef __APPLE__
# include <SDL/SDL.h>
# include <SDL/SDL_opengl.h>
#else
# include <SDL.h>
# include <SDL_opengl.h>
#endif
/* some constants and variables that several functions use */
const int width = 640;
const int height = 480;
#define texSize 64
GLuint vbo;
GLuint tex;
/* forward declaration, creates a random texture; uses glTexSubImage2D if
update is non-zero (otherwise glTexImage2D) */
void createTexture(GLuint label, int update);
int init()
{
/* SDL initialization */
if (SDL_Init(SDL_INIT_VIDEO) < 0)
return 0;
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
if (!SDL_SetVideoMode(width, height, 0, SDL_OPENGL)) {
fprintf(stderr, "Couldn't initialize OpenGL");
return 0;
}
/* OpenGL initialization */
glClearColor(0, 0, 0, 0);
glEnable(GL_TEXTURE_2D);
glEnable(GL_BLEND);
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
glViewport(0, 0, width, height);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0, width, height, 0, -1, 1);
glMatrixMode(GL_MODELVIEW);
/* creating the VBO and the textures */
glGenBuffers(1, &vbo);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER, 1024, 0, GL_DYNAMIC_DRAW);
glGenTextures(1, &tex);
createTexture(tex, 0);
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
return 1;
}
/* draw a triangle at the specified point */
void drawTriangle(GLfloat x, GLfloat y)
{
GLfloat coords1[12] = {0, 0, 0, 0, /**/200, 0, 1, 0, /**/200, 150, 1, 1};
glLoadIdentity();
glTranslatef(x, y, 0);
glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(coords1), coords1);
glVertexPointer(2, GL_FLOAT, 4*sizeof(GLfloat), (void*)0);
glTexCoordPointer(2, GL_FLOAT, 4*sizeof(GLfloat),
(char*)0 + 2*sizeof(GLfloat));
glDrawArrays(GL_TRIANGLES, 0, 3);
}
void render()
{
glClear(GL_COLOR_BUFFER_BIT);
drawTriangle(250, 50);
createTexture(tex, 0);
drawTriangle(260, 120);
SDL_GL_SwapBuffers();
}
void cleanup()
{
glDeleteTextures(1, &tex);
glDeleteBuffers(1, &vbo);
SDL_Quit();
}
int main(int argc, char* argv[])
{
SDL_Event event;
if (!init()) return 1;
while (1) {
while (SDL_PollEvent(&event))
if (event.type == SDL_QUIT)
return 0;
render();
}
cleanup();
return 0;
}
void createTexture(GLuint label, int update)
{
GLubyte data[texSize*texSize*4];
GLubyte* p;
int i, j;
glBindTexture(GL_TEXTURE_2D, label);
for (i = 0; i < texSize; ++i) {
for (j = 0; j < texSize; ++j) {
p = data + (i + j*texSize)*4;
p[0] = ((i % 8) > 4?255:0);
p[1] = ((j % 8) > 4?255:0);
p[2] = ((i % 8) > 4?255:0);
p[3] = 255 - i*3;
}
}
if (!update)
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, texSize, texSize, 0, GL_RGBA,
GL_UNSIGNED_BYTE, data);
else
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, texSize, texSize, GL_RGBA,
GL_UNSIGNED_BYTE, data);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
}
Notes:
I'm using SDL, but I've seen the same happening in wxWidgets, so it's not an SDL-related problem.
If I use glTexSubImage2D instead for every frame (use update = 1 in createTexture), the artifacts disappear.
If I disable blending, there are no more artifacts.
I've been testing this on a late 2010 MacBook Air, though I doubt that's particularly relevant.
This clearly an OpenGL implementation bug (just calling glTexImage2D in a loop should not cause this to happen).

OpenGL Mapping Textures to a Grid Stored Inside Vertex Array

I have code that uses indices and vertices to draw a set of triangles in the shape of a grid. All the vertices are drawn using glDrawElements(). Now for each vertex I will set its corresponding Texture Coordinates to 0 or 1 for each set of triangles that form a square in the grid. Basically I want to draw a collage of random textures in each one of the "squares" (consisting of two triangles). I can do this using the glBegin() and glEnd() method calls inside a for loop using the fixed functional pipeline, but I would like to know how to do this using Vertex Arrays. A code view of what I am trying to do can be seen below.
#include "glwidget.h"
GLWidget::GLWidget(QWidget *parent, QGLWidget *glparent) :
QGLWidget(parent, glparent),
texture_ids_(NULL),
col_(30),
row_(30),
step_(16.0)
{
texture_ids_ = new GLuint[row_ * col_];
}
GLWidget::~GLWidget()
{
if (texture_ids_) {
glDeleteTextures(row_ * col_, texture_ids_);
}
}
void GLWidget::resizeEvent(QResizeEvent * /*event*/) {
initGL();
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glViewport(0, 0, width(), height());
glOrtho(0, width(), 0, height(), -1, 1);
}
void GLWidget::initGL()
{
makeCurrent();
// Variables for vertices
vertices_.clear();
int32_t start_y = step_;
int32_t start_x = step_;
// Varaibles for indices
indices_.clear();
int32_t vertices_per_row = col_ + 1;
int32_t vertex_num = 0;
for (int32_t j = 0; j <= row_; ++j) {
// Generate Vertices on row j
for (int32_t i = 0; i <= col_; ++i) {
vertices_.push_back(Vertex<GLfloat>((start_x + (i * step_)),
(start_y + (j * step_)), 0.0f));
}
if (j == row_) {
break;
}
// Generate Indices to get right vertices for traingle
for (int32_t i = 0; i < col_; ++i) {
indices_.push_back(Indices<GLuint>(vertex_num, (vertex_num + 1),
(vertex_num + vertices_per_row)));
indices_.push_back(Indices<GLuint>((vertex_num + 1),
(vertex_num + vertices_per_row),
(vertex_num + vertices_per_row + 1)));
vertex_num++;
}
vertex_num++;
}
}
void GLWidget::textureInit()
{
makeCurrent();
for (int32_t i = 0; i < row_ * col_; ++i) {
QImage tmpQImage(step_, step_, QImage::Format_ARGB32);
tmpQImage = QGLWidget::convertToGLFormat(tmpQImage);
QPainter tmpQPainter;
tmpQPainter.begin(&tmpQImage);
tmpQPainter.fillRect(QRect(0, 0, width(), height()),
QColor(255, 0, 0));
tmpQPainter.setRenderHint(QPainter::Antialiasing, true);
tmpQPainter.end();
glGenTextures(1, &texture_ids_[i]);
glBindTexture(GL_TEXTURE_2D, texture_ids_[i]);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, tmpQImage.width(),
tmpQImage.height(), 0, GL_RGBA, GL_UNSIGNED_BYTE,
tmpQImage.bits());
}
}
void GLWidget::updateGL() {
if (first_render_) {
textureInit();
first_render_ = false;
}
glMatrixMode(GL_MODELVIEW);
glScissor(0, 0, width(), height());
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glClearColor(0.5f, 0.5f, 0.5f, 0.5f);
glLoadIdentity();
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
glEnableClientState(GL_VERTEX_ARRAY);
glVertexPointer(3, GL_FLOAT, 0, vertices_.data());
glDrawElements(GL_TRIANGLES, indices_.size() * 3, GL_UNSIGNED_INT,
indices_.data());
glDisableClientState(GL_VERTEX_ARRAY);
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
}
So, you want to draw using a lot of textures, but you obviously can't re-bind new textures as it is all drawn from one array. One solution to this is to use a texture atlas. It is one single bitmap with all your textures inside it. For example, if you have 16 different textures, you make a bitmap with 4x4 sections. Instead of using texture coordinates from 0 to 1, you will use 0 to 0.25, or 0.25 to 0.50, etc.
There are some disadvantages you need to be aware of:
If you want high resolution, the texture atlas will obviously be quite big.
Minifying and magnifying can play tricks with you. GL_NEAREST won't be any problem, but using GL_LINEAR or variants of mipmapping will average values around a pixel. This can lead to artifacts for pixels at the border of one sub image.
As the UV coordinates will vary more, fewer vertices will have common vertex data, leading to a increased number of indices.
I assume you have done profiling that shows that using multiple iterations of drawing, rebinding the texture for each, is not good enough. This obvious solution can be surprisingly effective.