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 --
Related
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.
I'm trying to draw a filled in circle, but when I draw this, it only shows in wireframe, here is the code I'm using to draw:
void render_circle(Vec2 position, float radius, Vec4 colour) {
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
glColor4f(colour.x, colour.y, colour.z, colour.w);
glBegin(GL_LINE_LOOP);
int num_segments = 30; //#todo: make this scale for larger radius
for(int i = 0; i < num_segments; i++) {
float theta = 2.0f * math_pi * (float)i / (float)num_segments;
float x = radius * cosf(theta);
float y = radius * sinf(theta);
glVertex2f(position.x + x, position.y + y);
}
glEnd();
}
GL_LINE_LOOP is a line primitive type. If you want to draw a filled polygon, then you have to use a polygon primitive type. For instance GL_TRINAGLE_FAN.
It is only possible to correctly draw convex geometry. Concave polygons may not be represented correctly, by a primitive. A possibility to deal with this, is to split concave polygons into convex parts.
I'm making a small game engine in which i want to draw stuff using OpenGL. I abstracted all the OpenGL objects into classes (Buffers, VertexArrays, Shaders, Programs...). Everything worked fine until i got to 3D rendering. I implemented my own matrices and vectors(i didn't use like glm), and when i multiply my vertex position in the shader with any matrix, the z coordinate flips (z = -z). I even tried with the identity matrix. Here is the vertex shader:
#version 330 core
layout(location = 0) in vec4 i_pos;
layout(location = 1) in vec4 i_color;
out vec4 p_color;
uniform mat4 u_MVP;
uniform vec4 u_pos;
void main()
{
gl_Position = u_MVP * (i_pos + u_pos);
p_color = i_color;
}
I used the u_Pos uniform just for debugging reasons. And here i set the uniforms:
void Frame() override
{
deltaTime = timer.Reset();
if (Input::GetKey(Key::W).value == KeyDown) pos.z += deltaTime;
if (Input::GetKey(Key::S).value == KeyDown) pos.z -= deltaTime;
//mat4f(1.0f) creates a identity matrix
shaderSelection.SetUniform("u_MVP", mat4f(1.0f));
shaderSelection.SetUniform("u_pos", vec4f(pos));
ren.DrawTriangles(vertexArray, indexBuffer, shaderSelection);
}
Although im sure there's nothing with the matrix struct, here it is:
template<typename T = float, int sizeX = 4, int sizeY = 4>
struct BLAZE_API mat
{
private:
T v[sizeY][sizeX];
public:
mat()
{
for (unsigned i = 0; i < sizeX * sizeY; i++)
((T*)v)[i] = 0;
}
mat(T* ptr, bool transpose = false)
{
if (transpose)
for (unsigned i = 0; i < sizeX * sizeY; i++)
((T*)v)[i] = ptr[i];
else
for (unsigned i = 0; i < sizeX * sizeY; i++)
((T*)v)[i] = ptr[i % sizeY * sizeX + i / sizeY];
}
mat(T n)
{
for (int x = 0; x < sizeX; x++)
for (int y = 0; y < sizeY; y++)
if (x == y)
operator[](x)[y] = n;
else
operator[](x)[y] = 0;
}
mat(const mat<T, sizeX, sizeY>& mat)
{
for (int x = 0; x < sizeX; x++)
for (int y = 0; y < sizeY; y++)
v[x][y] = mat[x][y];
}
inline T* operator[] (unsigned i) const { return (T*)(v[i]); }
inline void operator= (const mat<T, sizeX, sizeY>& mat)
{
for (int x = 0; x < sizeX; x++)
for (int y = 0; y < sizeY; y++)
v[x][y] = mat[x][y];
}
};
And the SetUniform does this:
glUniformMatrix4fv( ... , 1, GL_FALSE, m[0]);
I made the matrix struct such that i don't have to use GL_TRUE for transpose parameter in glUniformMatrix4fv. I am pretty sure it isnt my matrix implementation that is inverting the z coordinate.
It is like the camera is looking in the -Z direction, but when i move a object in the +X direction it moves also +X on the screen(also applies for Y direction), which it shouldn't if the camera is facing -Z.
Is this supposed to happen, if so can i change it?
If you do not transform the vertex coordinates (or transform it by the Identity matrix), then you directly set the coordinates in normalized device space. The NDC is a unique cube, with the left, bottom, near of (-1, -1, -1) and the right, top, far of (1, 1, 1). That means the X-axis is to the right, the Y-axis is upwards and the Z-axis points into the view.
In common the OpenGL coordinate system is a Right-handed system. In view space the X-axis points to the right and the Y-axis points up.
Since the Z-axis is the Cross product of the X-axis and the Y-axis, it points out of the viewport and appears to be inverted.
To compensate the difference in the direction of the Z-axis in view space in compare to normalized device space the Z-axis has to be inverted.
A typical OpenGL projection matrix (e.g. glm::ortho, glm::perspective or glm::frustum) turns the right handed system to a left handed system and mirrors the Z-axis.
That means, if you use a (typical) projection matrix (and no other transformations), then the vertex coordinates are equal to the view space coordinates. The X-axis is to the right, the Y-axis is upwards and the Z-axis points out of the view.
In simplified words, in normalized device space the camera points in +Z. In view space (before the transformation by a typical projection matrix) the camera points in -Z.
Note if you setup a Viewing frustum, then 0 < near and near < far. Both conditions have to be fulfilled. The geometry has to be between the near and the far plane, else it is clipped. In common a view matrix is used to look at the scene from a certain point of view. The near and far plane of the viewing frustum are chosen in that way, that the geometry is in between.
Since the depth is not linear (see How to render depth linearly in modern OpenGL with gl_FragCoord.z in fragment shader?), the near plane should be placed as close as possible to the geometry.
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).
I'm trying to draw Steiner's Roman Surface in OpenGL, and I'm having some trouble getting the right normals so that the surface lights up correctly. I used the parametric equation from Wikipedia : http://en.wikipedia.org/wiki/Roman_surface. For the normals, I did a partial differentiation with respect to theta, then phi, then crossed the partial differentials to get the normal.
This doesn't allow the surface to light up properly because the Roman Surface is a non-orientable surface. Hence, I was wondering if there's a way to get the right normals out so that the surface can light up correctly. I've tried negating the normals, for the whole surface, and part of the surface(negating for the 1st and last quarter of n), but it doesn't seem to work.
My current code is as follows:
double getRad(double deg, double n){
return deg * M_PI / n;
}
int n = 24;
for(int i = 0; i < n; i++){
for(int j = 0; j < 2*n; j++){
glBegin(GL_POLYGON);
double x = -pow(r,4) * cos(2*getRad(i+0.5,n)) * pow(cos(getRad(j+0.5,n)),2) * cos(2*getRad(j+0.5,n)) * sin(getRad(i+0.5,n)) - 2 * pow(r,4) * pow(cos(getRad(i+0.5,n)),2) * pow(cos(getRad(j+0.5,n)),2) * sin(getRad(i+0.5,n)) * pow(sin(getRad(j+0.5,n)),2);
double y = pow(r,4) * cos(getRad(i+0.5,n)) * cos(2*getRad(i+0.5,n)) * pow(cos(getRad(j+0.5,n)),2) * cos(2*getRad(j+0.5,n)) - 2 * pow(r,4) * cos(getRad(i+0.5,n)) * pow(cos(getRad(j+0.5,n)),2) * pow(sin(getRad(i+0.5,n)),2) * pow(sin(getRad(j+0.5,n)),2);
double z = -pow(r,4) * pow(cos(getRad(i+0.5,n)),2) * cos(getRad(j+0.5,n)) * cos(2*getRad(j+0.5,n)) * sin(getRad(j+0.5,n)) - pow(r,4) * cos(getRad(j+0.5,n)) * cos(2*getRad(j+0.5,n)) * pow(sin(getRad(i+0.5,n)),2) * sin(getRad(j+0.5,n));
glNormal3d(x, y, z);
glVertex3d(r*r*cos(getRad(i,n))*cos(getRad(j,n))*sin(getRad(j,n)),r*r*sin(getRad(i,n))*cos(getRad(j,n))*sin(getRad(j,n)),r*r*cos(getRad(i,n))*sin(getRad(i,n))*cos(getRad(j,n))*cos(getRad(j,n)));
glVertex3d(r*r*cos(getRad(i+1,n))*cos(getRad(j,n))*sin(getRad(j,n)),r*r*sin(getRad(i+1,n))*cos(getRad(j,n))*sin(getRad(j,n)),r*r*cos(getRad(i+1,n))*sin(getRad(i+1,n))*cos(getRad(j,n))*cos(getRad(j,n)));
glVertex3d(r*r*cos(getRad(i+1,n))*cos(getRad(j+1,n))*sin(getRad(j+1,n)),r*r*sin(getRad(i+1,n))*cos(getRad(j+1,n))*sin(getRad(j+1,n)),r*r*cos(getRad(i+1,n))*sin(getRad(i+1,n))*cos(getRad(j+1,n))*cos(getRad(j+1,n)));
glVertex3d(r*r*cos(getRad(i,n))*cos(getRad(j+1,n))*sin(getRad(j+1,n)),r*r*sin(getRad(i,n))*cos(getRad(j+1,n))*sin(getRad(j+1,n)),r*r*cos(getRad(i,n))*sin(getRad(i,n))*cos(getRad(j+1,n))*cos(getRad(j+1,n)));
glEnd();
glFlush();
}
}
In the case you're dealing with nonorientable surfaces (like Steiner's Romans, or the famous Möbius strip) you have to possiblilities: Enable double sided lighting
glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_TRUE);
or you enable face culling and render the surface with two passes (front facing and back facing) – you'll have to negate the normals for the backface pass.
glEnable(GL_CULL_FACE);
glCullFace(GL_BACK); // backside faces are NOT rendered
draw_with_positive_normals();
glCullFace(GL_FRONT);
draw_with_negative_normals();
You would probably get better results by splitting the polygon into two triangles - each would then be guaranteed to be planar. Further, you could can generate the normals from each triangle, or smooth them between neighboring triangles.
The other trick is to pre-generate your points into an array and then referencing the array in the glVertex call. That way you have more options about how to generate normals.
Also, you can render the normals themselves with a glBegin(GL_LINES) ... glEnd() sequence.
For every triangle you generate create one with the same coordinates/normals but wound/flipped the other way.