How to permanently draw things in OpenGL's Immediate mode? - c++

I am having a small problem in essentially creating a path tracer.
In my project, I have an object which constantly moves around quite organically through an update function done in the while loop. I use immediate mode and represent the player as a square, I would like to make it so that every update the object is drawn in its current position, but also for it to draw it's previous position(s), so etching dots towards the path the object is going. I'm pretty sure we can do this by drawing the position as normal but not clearing everything up after this instance in the while loop, but I have no knowledge on how to do this.
Edit: For those who want the code, do understand that this code, in particular, is not adherent to the question and that I made a ton of generalizations (such as the particle(s) being referred to an object) so that the general gist of the question is understandable:
#include "PerlinNoise.hpp"
#include "Particle.hpp"
#include <iostream>
#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include <cmath>
#include <vector>
using namespace siv;
float map(float oValue, float oMin, float oMax, float nMin, float nMax)
{
float oRange = (oMax - oMin);
float nRange = (nMax - nMin);
return(((oValue - oMin) * nRange)/oRange) + nMin;
}
void drawRectangle(float x, float y, float xr, float yr, float R, float G, float B)
{
glBegin(GL_QUADS);
glColor3f(R,G,B);
glVertex2f(x,y);
glVertex2f(x+xr,y);
glVertex2f(x+xr,y+yr);
glVertex2f(x,y+yr);
glEnd();
}
void drawLine(float x, float y, float xr, float yr, float rotation)
{
float radius = sqrt(xr*xr + yr*yr);
float a0 = asin(yr/radius);
float tangle = a0+rotation;
//std::cout<<tangle*180/M_PI<<std::endl;
glBegin(GL_LINES);
glColor3f(.1,.1,.1);
glVertex2f(x,y);
glVertex2f(x + sin(tangle)*radius,y + cos(tangle)*radius);
glEnd();
}
int main()
{
float inc = 0.1;
int scl = 20;
int cols,rows;
Particle particles[100000];
//V2D flowfield[cols*rows];
GLFWwindow* window;
if (!glfwInit())
return 1;
int width = 800;
int height = 800;
window = glfwCreateWindow(width, height, "Window", NULL, NULL);
cols = floor(width/scl);
rows = floor(height/scl);
V2D flowfield[cols*rows];
float zoff = 0;
if (!window) {
glfwTerminate();
return 1;
}
glfwMakeContextCurrent(window);
if(glewInit()!=GLEW_OK)
std::cout<<"Error"<<std::endl;
glEnable(GL_DEPTH_TEST);
glMatrixMode(GL_PROJECTION);
glfwGetFramebufferSize(window, &width, &height);
glOrtho(0, width*(width/height), height, 0, -2, 2);
PerlinNoise png = PerlinNoise(1);
while(!glfwWindowShouldClose(window)) {
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
glClearColor(0.11, 0.14, 0.17, 1);
float yoff = 0;
for(int y = 0; y < rows; y++)
{
float xoff = 0;
for(int x = 0; x < cols; x++)
{
double noise = map(png.noise((double)xoff, (double)yoff, (double)zoff),-1,1,0,1);
double angle = noise * 8 *M_PI;
//std::cout<<angle/(2*M_PI)<<std::endl;
int index = x + y * cols;
V2D v = V2D(cos(angle), sin(angle));
v.normalize();
v = V2D(v.x*5,v.y*5);
flowfield[index] = v;
//drawLine(x*scl, y*scl, scl, 0, atan2(v.x, v.y));
//drawRectangle(x*scl,y*scl,scl,scl,noise,noise,noise);
xoff += inc;
}
yoff += inc;
zoff += 0.0001;
}
for(int i = 0; i < 100000; i++)
{
particles[i].follow(flowfield);
particles[i].update();
particles[i].show();
}
glfwSwapBuffers(window);
glfwPollEvents();
}
glfwTerminate();
}

When drawing directly to a window (be it double buffered or not doesn't make a difference) you must not make any assumptions about its contents being persistent between drawing. Heck, strictly speaking the contents may become damaged mid draw, before things even finished up; of course in practice this isn't very likely to happen and given modern compositing graphics systems it's practically eliminated.
Your application screams for drawing to an intermediary framebuffer object. FBOs are guaranteed to retain their contents no matter what happens; also you can add further drawing to the backing buffer of an FBO at any time.
The official OpenGL wiki describes FBOs at https://www.khronos.org/opengl/wiki/Framebuffer_Object
Also ages ago I wrote a simple codesample (using a lot of outdated, legacy OpenGL); drawing is legacy, but the FBO parts are done today as it was 10 years ago: https://github.com/datenwolf/codesamples/blob/master/samples/OpenGL/minimalfbo/minimalfbo.c (I implemented it using render to texture; a render to renderbuffer and buffer blit to main framebuffer would work for you, too).

Related

SDL2 function to draw a filled circle

I am looking for a function that draws a filled circle using SDL2 without using a renderer at all. I currently have this:
void Circle(int center_x, int center_y, int radius, SDL_Color color) {
eraseOldCircle();
uint32_t *pixels = (uint32_t *) windowSurface->pixels;
SDL_PixelFormat *windowFormat = windowSurface->format;
SDL_LockSurface(windowSurface); // Lock surface for direct pixel access capability
int radiussqrd = radius * radius;
for(int x=center_x-radius; x<=center_x+radius; x++) {
int dx = center_x - x;
for(int y=center_y-radius; y<=center_y+radius; y++) {
int dy = center_y - y;
if((dy * dy + dx * dx) <= radiussqrd) {
pixels[(y * WIDTH + x)] = SDL_MapRGB(windowFormat, color.r, color.g, color.b);
}
}
}
SDL_UnlockSurface(windowSurface);
SDL_UpdateWindowSurface(window);
}
which has been adapted from another function I found here, it draws the pixels directly to the windowSurface after calling eraseOldCircle (which puts the game's background image back to the previous position of the circle, effectively erasing it from there.) but it is still too slow for what I need (probably the maths?). What would be the fastest way to draw a circle using direct pixel access? I need it to be high speed so I can use it in a 2D game. I haven't been able to find anything until now, everything I see uses SDL_Renderer, but I should strictly never use it.
Here is eraseOldCircle() in case it helps:
void eraseOldCircle() {
//Calculate previous position of ball
SDL_Rect pos = {circlePosition.x-(radius+steps), circlePosition.y-(radius+steps), radius*radius, radius*2+steps};
SDL_BlitSurface(backgroundImage, &pos, windowSurface, &pos);
}
I'm not too sure how to do it with surfaces and memory management and all that, but if this helps, here is a version using an SDL_Renderer that runs pretty quickly:
void draw_circle(SDL_Renderer *renderer, int x, int y, int radius, SDL_Color color)
{
SDL_SetRenderDrawColor(renderer, color.r, color.g, color.b, color.a);
for (int w = 0; w < radius * 2; w++)
{
for (int h = 0; h < radius * 2; h++)
{
int dx = radius - w; // horizontal offset
int dy = radius - h; // vertical offset
if ((dx*dx + dy*dy) <= (radius * radius))
{
SDL_RenderDrawPoint(renderer, x + dx, y + dy);
}
}
}
}
If you draw many circles, I would guess SDL_UpdateWindowSurface is where you spend the most time. Try this instead
SDL_LockSurface
// erase and draw all circles (possibly >1000)
SDL_UnlockSurface
SDL_UpdateWindowSurface
You can optimize your circle drawing code a bit, but it is probably fast enough. I also think that SDL_Renderer is probably fast enough.
The documentation for SDL_UpdateWindowSurface says it will copy the surface to the screen. You only need to do this once per frame.

Merge color vertices at center rather than at one vertex?

I'm trying to make a color swatch tool, where you give it n colors and it makes a n-gon with those colors merging at the center.
So far it just makes an n-gon(without specifying colors, it randomly generates them for now).
However the colors don't merge at the center but rather a single vertex.
Is there any fix to this?
#include <GLFW/glfw3.h>
#include <iostream>
#include <cmath>
float randfloat(){
float r = ((float)(rand() % 10))/10;
return r;
}
int main() {
int side_count;
std::cout<<"Type the no. of sides: "<<std::endl;
std::cin>>side_count;
srand(time(NULL));
std::cout<<randfloat()<<std::endl;
std::cout<<randfloat()<<std::endl;
float rs[side_count];
float gs[side_count];
float bs[side_count];
for (int i=0;i<side_count;i++)
{
rs[i] = randfloat();
gs[i] = randfloat();
bs[i] = randfloat();
}
GLFWwindow* window;
if (!glfwInit())
return 1;
window = glfwCreateWindow(800, 800, "Window", NULL, NULL);
if (!window) {
glfwTerminate();
return 1;
}
glfwMakeContextCurrent(window);
if(glewInit()!=GLEW_OK)
std::cout<<"Error"<<std::endl;
while(!glfwWindowShouldClose(window)) {
glClear(GL_COLOR_BUFFER_BIT);
glClearColor(0.11f,0.15f,0.17f,1.0f);
glBegin(GL_POLYGON);
//glColor3f(1.0f,0.0f,0.0f);glVertex3f(-0.5f,0.0f,0.0f);
for(int i=0; i<side_count;i++)
{
float r = rs[i];
float g = gs[i];
float b = bs[i];
float x = 0.5f * sin(2.0*M_PI*i/side_count);
float y = 0.5f * cos(2.0*M_PI*i/side_count);
glColor3f(r,g,b);glVertex2f(x,y);
}
glEnd();
glfwSwapBuffers(window);
glfwPollEvents();
}
glfwTerminate();
return 0;
}
All you've to do is to add a new point to the GL_POLYGON primitive, in the center of the circular shape:
glBegin(GL_TRIANGLE_FAN);
glColor3f(0.5f, 0.5f, 0.5f);
glVertex2f(0, 0);
for(int i=0; i <= side_count; i++)
{
float r = rs[i % side_count];
float g = gs[i % side_count];
float b = bs[i % side_count];
float x = 0.5f * sin(2.0*M_PI*i/side_count);
float y = 0.5f * cos(2.0*M_PI*i/side_count);
glColor3f(r, g, b);
glVertex2f(x, y);
}
glEnd();
Note, you've to define the color for the center point. In the code snippet, I've chosen (0.5, 0.5, 0.5).
Instead of GL_POLYGON, GL_TRIANGLE_FAN can be used, too.

How to correctly translate objects independently of one another?

I am attempting to translate one circle independent of a separate, stationary circle, utilizing glTranslatef();. However, with my current, full code, each of my circles remains immobile. To investigate why this may be so, I have researched several answers, each comparable to those found here and here. Additionally, I read up on glLoadIdentity as well as the differences between GL_MODELVIEW and GL_PROJECTION, just to see if their details would offer any further clarification. I've also consulted the OpenGL API for the proper definitions of each of the above.
In the style of these solutions, I produced the following do...while loop:
do{
glClear(GL_COLOR_BUFFER_BIT);
glDisable(GL_DEPTH_TEST);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0, fb_width, fb_height, 0, 0, 1);
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glTranslatef(0,1,0);
drawCircle(1280 * 0.50, 720 * 0.25,e[2]);
glPopMatrix();
glPushMatrix();
glTranslatef(0,0,0);
drawTarget(1280 * 0.50, 720 * 0.75,50);
glPopMatrix();
glfwSwapBuffers(w);
glfwPollEvents();
}
while (!glfwWindowShouldClose(w));
In this snippet, the drawCircle drawing remains stationary, but I would like for it to follow the written glTranslatef(0,1,0) instead. Is the stationary nature of the circle due to misplaced a glMatrixMode or glLoadIdentity, or perhaps due to the fact that they are being called within the do...while loop and the proper matrix is never really being utilized? I would appreciate any guidance you may have as to why the aforementioned and accepted answers are not functioning quite as well within my program.
For the sake of full transparency, here is the entirety of my code:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <stdint.h>
#include <math.h>
#include <stddef.h>
#include <stdbool.h>
#include <GL/glew.h>
#include <GLFW/glfw3.h>
GLFWwindow *w;
int fb_width, fb_height;
static void error(int error, const char *desc)
{
fputs(desc, stderr);
}
static void key_callback(GLFWwindow *w, int key, int scancode, int action, int mods)
{
if ((key == GLFW_KEY_ESCAPE || key == GLFW_KEY_Q) && action == GLFW_PRESS)
glfwSetWindowShouldClose(w, GL_TRUE);
}
void drawCircle(float cx, float cy, float radius)
{
float num_segments = 360;
float theta = 2 * 3.1415926 / num_segments;
float c = cosf(theta);//precalculate the sine and cosine
float s = sinf(theta);
float t;
float x = radius;//we start at angle = 0
float y = 0;
glBegin(GL_TRIANGLE_FAN);
glColor3f(1, 0, 1);
for(int ii = 0; ii < num_segments; ii++)
{
glVertex2f(x + cx, y + cy);//output vertex
//apply the rotation matrix
t = x;
x = c * x - s * y;
y = s * t + c * y;
}
glEnd();
}
void drawTarget(float cx, float cy, float radius)
{
float num_segments = 360;
float theta = 2 * 3.1415926 / num_segments;
float c = cosf(theta);//precalculate the sine and cosine
float s = sinf(theta);
float t;
float x = radius;//we start at angle = 0
float y = 0;
glBegin(GL_LINE_LOOP);
glColor3f(1, 1, 1);
for(int ii = 0; ii < num_segments; ii++)
{
glVertex2f(x + cx, y + cy);//output vertex
//apply the rotation matrix
t = x;
x = c * x - s * y;
y = s * t + c * y;
}
glEnd();
}
int main(void)
{
int i;
float e[3] = {140,120,100};
float m[3] = {90,80,70};
float h[3] = {60,50,40};
glfwSetErrorCallback(error);
if (!glfwInit())
exit(EXIT_FAILURE);
w = glfwCreateWindow(1280, 720, "AxTest", NULL, NULL);
if (!w)
{
glfwTerminate();
return 1;
}
glfwMakeContextCurrent(w);
glfwSetKeyCallback(w, key_callback);
glfwGetFramebufferSize(w, &fb_width, &fb_height);
do{
glClear(GL_COLOR_BUFFER_BIT);
glDisable(GL_DEPTH_TEST);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0, fb_width, fb_height, 0, 0, 1);
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glTranslatef(0,1,0);
drawCircle(1280 * 0.50, 720 * 0.25,e[2]);
glPopMatrix();
glPushMatrix();
glTranslatef(0,0,0);
drawTarget(1280 * 0.50, 720 * 0.75,50);
glPopMatrix();
glfwSwapBuffers(w);
glfwPollEvents();
}
while (!glfwWindowShouldClose(w));
glfwDestroyWindow(w);
glfwTerminate();
exit(EXIT_SUCCESS);
return 0;
}
The values for the vertex positions with which you draw your circles are in the order of hundreds (likely, because you want to address pixels as indicated by the values for the projection matrix). But glTranslates sees only a small number, so the shift is miniscule (one pixel) and hence you think nothing did happen. If you rewrite your code so that you don't specify the circle/target center by explicit modification of the vertex position offset it'd be clearer.
void drawCircle(float radius)
{
/* ... */
for(int ii = 0; ii < num_segments; ii++)
{
glVertex2f(x, y); // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
/* ... */
}
void drawTarget(float radius)
{
/* ... */
for(int ii = 0; ii < num_segments; ii++)
{
glVertex2f(x, y); // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
/* ... */
}
int main(void)
{
/* ... */
glPushMatrix();
glTranslatef(1280*0.50, 720*0.25, 0);
drawCircle(e[2]);
glPopMatrix();
glPushMatrix();
glTranslatef(1280 * 0.50, 720 * 0.25, 0);
drawTarget(50);
glPopMatrix();
/* ... */
}
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0, fb_width, fb_height, 0, 0, 1);
You don't have to make the projection matrix at every loop, put it before the loop.
Then the error you have is surely due to :
glMatrixMode(GL_MODELVIEW);
// it miss glLoadIdentity() here
glPushMatrix();
glTranslatef(0,1,0);

Drawing a circle in c++ using openGL

I've been trying to draw a circle in c++ using openGL. So far i have a compresses circle and it just has a random line going across the screen.
This is the function I'm using to get this shape.
void Sprite::init(int x, int y, int width, int height, Type mode, float scale) {
_x = x;
_y = y;
_width = width;
_height = height;
//generate buffer if it hasn't been generated
if (_vboID == 0) {
glGenBuffers(1, &_vboID);
}
Vertex vertexData[360];
if (mode == Type::CIRCLE) {
float rad = 3.14159;
for (int i = 0; i < 359; i++) {
vertexData[i].setPosition((rad * scale) * cos(i), (rad * scale) * sin(i));
}
}
//Tell opengl to bind our vertex buffer object
glBindBuffer(GL_ARRAY_BUFFER, _vboID);
//Upload the data to the GPU
glBufferData(GL_ARRAY_BUFFER, sizeof(vertexData), vertexData, GL_STATIC_DRAW);
//Unbind the buffer
glBindBuffer(GL_ARRAY_BUFFER, 0);
}
What is causing the line? Why is my circle being compressed?
Sorry if this is a dumb question or if this question doesn't belong on this website I'm very new to both c++ as well as this website.
It is difficult to be sure without testing the code myself, but I'll guess anyway.
Your weird line is probably caused by the buffer not being fully initialized. This is wrong:
Vertex vertexData[360];
for (int i = 0; i < 359; i++) {
It should be:
for (int i = 0; i < 360; i++) {
or else the position at vertexData[359] is left uninitialized and contains some far away point.
About the ellipse instead of a circle, that is probably caused by your viewport not having the same scale horizontally and vertically. If you configure the viewport plus transformation matrices to have a viewing frustum of X=-10..10, Y=-10..10, but the actual viewport is X=0..800 and the Y=0..600, for example, then the scale would be different and you'll get your image distorted.
The solution would be one of:
Create a square viewport instead of rectangular. Check your arguments to glViewport().
Define a view matrix to consider the same ratio your viewport has. You don't show how you set the view/world matrix, maybe you are not even using matrices... If that is the case, you should probably use one.
I don't understand, exactly, what you want obtain but... cos() and sin() receive a radiant argument; so, instead of cos(i) and sin(i), I suppose you need cos((2*rad*i)/360.0)) and sin((2*rad*i)/360.0)) or, semplified, cos((rad*i)/180.0)) and cos((rad*i)/180.0))
And what about the center and the radious of the circle?
(x, y) should be the center of the circle?
scale is the radious?
In this case, I suppose you should write something like (caution: not tested)
Vertex vertexData[360];
float rad = 3.14159;
if (mode == Type::CIRCLE) {
for (int i = 0; i < 359; ++i) {
float angle = (rad / 180) * i; // (thanks Rodrigo)
vertexData[i].setPosition(x + scale * cos(angle), y + scale * sin(angle));
}
}
or, loosing precision but avoidind some moltiplication,
Vertex vertexData[360];
float rad = 3.14159;
float angIncr = rad / 180.0;
if (mode == Type::CIRCLE) {
for (int i = 0, float angle = 0.0; i < 359; ++i, angle += angIncr) {
vertexData[i].setPosition(x + scale * cos(angle), y + scale * sin(angle));
}
}
But what about width and heigth?
p.s.: sorry for my bad English.
--- modified with suggestion from Rodrigo --

OpenGL glDrawPixels on dynamic 3D arrays

How do you draw the following dynamic 3D array with OpenGL glDrawPixels()?
You can find the documentation here: http://opengl.org/documentation/specs/man_pages/hardcopy/GL/html/gl/drawpixels.html
float ***array3d;
void InitScreenArray()
{
int i, j;
int screenX = scene.camera.vres;
int screenY = scene.camera.hres;
array3d = (float ***)malloc(sizeof(float **) * screenX);
for (i = 0 ; i < screenX; i++) {
array3d[i] = (float **)malloc(sizeof(float *) * screenY);
for (j = 0; j < screenY; j++)
array3d[i][j] = (float *)malloc(sizeof(float) * /*Z_SIZE*/ 3);
}
}
I can use only the following header files:
#include <math.h>
#include <stdlib.h>
#include <windows.h>
#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glut.h>
Uh ... Since you're allocating each single pixel with a separate malloc(), you will have to draw each pixel with a separate call to glDrawPixels(), too. This is (obviously) insane; the idea of bitmapped graphics is that the pixels are stored in an adjacent, compact, format, so that it is quick and fast (O(1)) to move from one pixel to another. This looks very confused to me.
A more sensible approach would be to allocate the "3D array" (which is often referred to as a 2D array of pixels, where each pixel happens to consist of a red, green and blue component) with a single call to malloc(), like so (in C):
float *array3d;
array3d = malloc(scene.camera.hres * scene.camera.vres * 3 * sizeof *array3d);
Thanks unwind. I got the same advice on gamedev.net so I have implemented the following algorithm:
typedef struct
{
GLfloat R, G, B;
} color_t;
color_t *array1d;
void InitScreenArray()
{
long screenX = scene.camera.vres;
long screenY = scene.camera.hres;
array1d = (color_t *)malloc(screenX * screenY * sizeof(color_t));
}
void SetScreenColor(int x, int y, float red, float green, float blue)
{
int screenX = scene.camera.vres;
int screenY = scene.camera.hres;
array1d[x + y*screenY].R = red;
array1d[x + y*screenY].G = green;
array1d[x + y*screenY].B = blue;
}
void onDisplay( )
{
glClearColor(0.1f, 0.2f, 0.3f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glRasterPos2i(0,0);
glDrawPixels(scene.camera.hres, scene.camera.vres, GL_RGB, GL_FLOAT, array1d);
glFinish();
glutSwapBuffers();
}
My application doesn't work yet (nothing appears on screen), but I think it's my fault and this code will work.
wouldn't you want to use glTexImage2D() instead: see here