How to apply shader to each shapes at their location? - c++

I am playing with SFML and wrote a minimal program that draws a line behind the cursor.
I would like to make this line glow so I wrote a minimal shader that I apply to each line vertex. However, the maximum number of uniforms is about 4096. Instead, I would like to "run" my shader at each vertex position. Is there a better way other than using a global fragment and passing each position as uniforms?
#include <SFML/Graphics.hpp>
#include <boost/circular_buffer.hpp>
#include <iostream>
int main()
{
const float winW = 1000;
const float winH = 1000;
const int tail_length = 100;
sf::RenderWindow window(sf::VideoMode(winW, winH), "SFML");
sf::VertexArray lines(sf::LinesStrip, tail_length);
boost::circular_buffer<sf::Vector2i> tail(tail_length);
sf::Clock clk;
clk.restart();
sf::Vector2i old_mouse_pos = sf::Vector2i(0, 0);
// std::fill(tail.begin(), tail.end(), sf::Mouse::getPosition(window));
sf::Texture tex;
tex.create(winW, winH);
sf::Sprite spr(tex);
sf::Shader shader;
shader.loadFromFile("glow.glsl", sf::Shader::Fragment);
while (window.isOpen())
{
sf::Event event;
while (window.pollEvent(event))
{
if (event.type == sf::Event::Closed)
window.close();
}
shader.setUniform("resolution", sf::Vector2f(40, 40));
sf::Vector2i mousePos = sf::Mouse::getPosition(window);
if (old_mouse_pos != mousePos)
tail.push_back(mousePos);
old_mouse_pos = sf::Vector2i(mousePos.x, mousePos.y);
sf::Vector2f v[100];
for (int i = 0; i < tail.size(); i++)
{
v[i] = sf::Vector2f(tail[i].x, tail[i].y- 2* winH / 2);
lines[i].position = sf::Vector2f(tail[i].x, tail[i].y);
lines[i].color = sf::Color(255, 0, 0, i * 255 / tail_length);
}
shader.setUniformArray("tail", v, tail.size());
window.clear();
window.draw(spr, &shader);
window.draw(lines);
window.display();
}
}
Here the shader:
uniform vec2 resolution;
uniform vec2 tail[100];
void main(void) {
vec2 p = gl_FragCoord.xy / resolution.xy;
float d = 0.0;
for (int i = 0; i < 100; i++) {
vec2 u = tail[i];
vec2 q;
q.x = p.x - u.x / resolution.x;
q.y = p.y + u.y / resolution.y;
float dist = pow(1.0/length(q) * 0.1, 1.5);
d += dist;
}
vec3 col = d * vec3(1.0, 0.5, 0.25);
gl_FragColor = vec4(1.0 - exp( -col ), 1.0);
}

Related

How to fill sf::VertexArray of sf::TriangleStrip with color?

I would like to increase the drawing performance by using a single sf::VertexArray to display thousands of circles. To make sure it works, I wrote this example:
#include <SFML/Graphics.hpp>
#include <iostream>
#define WIDTH 100
#define HEIGHT 100
int main() {
sf::RenderWindow window(sf::VideoMode(WIDTH, HEIGHT), "RTREE",
sf::Style::Close);
while (window.isOpen()) {
sf::Event event;
while (window.pollEvent(event)) {
if (event.type == sf::Event::Closed) window.close();
}
sf::CircleShape circle(50);
circle.setPosition(100,100);
size_t count = circle.getPointCount();
sf::VertexArray objects(sf::TriangleStrip, count);
for (int i = 0; i < count; i++) {
objects.append(circle.getPoint(i));
}
for (int i = 0; i < objects.getVertexCount(); i++) {
objects[i].color = sf::Color::Blue;
}
window.clear();
window.draw(objects);
window.display();
}
}
However, the color only applies to the line, not the fill:
Is it possible to fill the shape with a single color?
You are right, displaying more than 1000 circles with a window.draw between each of them result in poor performances. The vertexArray is indeed a good solution.
You used sf::TriangleStrip which does not work as you expected. Your verticles are indeed painted, but they are only on the edge of the circle. You need to use either sf::TriangleFan or sf::Triangles if you want more than one circle per array.
Here an example. I was able to display 513'000 verticles at 60 fps (each circle is about 30 verticles, which is about 10'000 circles.
#include <SFML/Graphics.hpp>
#include <cmath>
#include <iostream>
#include <sstream>
#define WIDTH 800
#define HEIGHT 800
using namespace std;
template <typename T>
std::string to_string_with_precision(const T a_value, const int n = 6)
{
std::ostringstream out;
out.precision(n);
out << std::fixed << a_value;
return out.str();
}
void addCircle(sf::VertexArray &array, sf::Vector2f position, float radius, sf::Color color) {
size_t count = 30;
for (int i = 0; i < count; i += 1) {
sf::Vertex v0;
v0.position = sf::Vector2f(position.x, position.y);
v0.color = color;
array.append(v0);
sf::Vertex v1;
float angle = i * 2 * M_PI / count;
v1.position = sf::Vector2f(position.x + cos(angle) * radius, position.y + sin(angle) * radius);
v1.color = color;
array.append(v1);
sf::Vertex v2;
angle = (i + 1) * 2 * M_PI / count;
v2.position = sf::Vector2f(position.x + cos(angle) * radius, position.y + sin(angle) * radius);
v2.color = color;
array.append(v2);
}
}
#define FRAMERATE 120
int main() {
sf::RenderWindow window(sf::VideoMode(WIDTH, HEIGHT), "RTREE", sf::Style::Close);
// Fps text
sf::Font font;
if (!font.loadFromFile("../assets/collegiate.ttf")) {
std::cout << "Error loading font" << std::endl;
}
sf::Text fps;
fps.setFont(font);
fps.setPosition(10, 10);
fps.setCharacterSize(16);
fps.setFillColor(sf::Color::White);
// Time management
sf::Clock clock;
sf::Time timeSinceLastUpdate = sf::Time::Zero;
sf::Time timePerFrame = sf::seconds(1.f / FRAMERATE);
sf::Time timeSinceLastDraw = sf::Time::Zero;
window.setFramerateLimit(FRAMERATE);
float vr = 0;
while (window.isOpen()) {
sf::Event event;
while (window.pollEvent(event)) {
if (event.type == sf::Event::Closed) window.close();
}
size_t count = 15;
sf::Color color = sf::Color::Red;
color.a = 50;
sf::Color color2 = sf::Color::Blue;
color2.a = 20;
sf::VertexArray objects(sf::Triangles);
for (int k = 0; k < 5; k++) {
sf::Color c = k % 2 ? color : color2;
for (int j = 20; j < 400; j += 5) {
for (int i = 0; i < count; i++) {
float angle = i * 2 * M_PI / count;
angle += (i % 2 ? vr : -vr) * k * (k % 2 ? 1 : -1);
addCircle(objects, sf::Vector2f(WIDTH / 2 + cos(angle) * j, HEIGHT / 2 + sin(angle) * j), j / 5, c);
}
}
}
timeSinceLastDraw = clock.restart();
timeSinceLastUpdate += timeSinceLastDraw;
double timeFps = 1.f / timeSinceLastDraw.asSeconds();
fps.setString("verticles: " + to_string_with_precision(objects.getVertexCount(), 0) + " fps: " + to_string_with_precision(timeFps, 0));
while (timeSinceLastUpdate > timePerFrame) {
timeSinceLastUpdate -= timePerFrame;
vr += 0.01;
}
window.clear();
window.draw(objects);
window.draw(fps);
window.display();
}
}

Why are my Ray march fragment shader refelction texture lookups slowing my frame rate?

I’ve written a Fragment shader in GLSL, using shader toy.
Link : https://www.shadertoy.com/view/wtGSzy
most of it works, but when I enable texture lookups in the reflection function, the performance drops from 60FPS to 5~FPS.
The code in question is on lines 173 - 176
if(SDFObjectToDraw.texChannelID == 0)
col = texture(iChannel0, uv);
if(SDFObjectToDraw.texChannelID == 1)
col = texture(iChannel1, uv);
This same code can bee seen in my rayMarch function (lines 274-277) and works fine for colouring my objects. It only causes issues in the reflection function.
My question is, why are my texture lookups, in the reflection code, dropping my performance this much and what can I do to improve it?
/**
* Return the normalized direction to march in from the eye point for a single pixel.
*
* fieldOfView: vertical field of view in degrees
* size: resolution of the output image
* fragCoord: the x,y coordinate of the pixel in the output image
*/
vec3 rayDirection(float fieldOfView, vec2 size, vec2 fragCoord) {
vec2 xy = fragCoord - size / 2.0;
float z = size.y / tan(radians(fieldOfView) / 2.0);
return normalize(vec3(xy, -z));
}
float start = 0.0;
vec3 eye = vec3(0,0,5);
int MAX_MARCHING_STEPS = 255;
float EPSILON = 0.00001;
float end = 10.0;
const uint Shpere = 1u;
const uint Box = 2u;
const uint Plane = 4u;
vec3 lightPos = vec3(-10,0,5);
#define M_PI 3.1415926535897932384626433832795
const int SDF_OBJECT_COUNT = 4;
struct SDFObject
{
uint Shape;
vec3 Position;
float Radius;
int texChannelID;
float Ambiant;
float Spec;
float Diff;
vec3 BoxSize;
bool isMirror; //quick hack to get refletions working
};
SDFObject SDFObjects[SDF_OBJECT_COUNT] = SDFObject[SDF_OBJECT_COUNT](
SDFObject(Shpere, vec3(2,0,-3),1.0,0,0.2,0.2,0.8, vec3(0,0,0),true)
,SDFObject(Shpere, vec3(-2,0,-3),1.0,0,0.1,1.0,1.0, vec3(0,0,0),false)
,SDFObject(Box, vec3(0,0,-6),0.2,1,0.2,0.2,0.8, vec3(1.0,0.5,0.5),false)
,SDFObject(Plane, vec3(0,0,0),1.0,1,0.2,0.2,0.8, vec3(0.0,1.0,0.0),false)
);
float shereSDF(vec3 p, SDFObject o)
{
return length(p-o.Position)-o.Radius;
}
float boxSDF(vec3 pointToTest, vec3 boxBoundery, float radius, vec3 boxPos)
{
vec3 q = abs(pointToTest - boxPos) - boxBoundery;
return length(max(q,0.0)) + min(max(q.x, max(q.y,q.z)) ,0.0) -radius;
}
float planeSDF(vec3 p, vec4 n, vec3 Pos)
{
return dot(p-Pos, n.xyz) + n.w;
}
bool IsShadow(vec3 LightPos, vec3 HitPos)
{
bool isShadow = false;
vec3 viewRayDirection = normalize(lightPos- HitPos) ;
float depth = start;
vec3 hitpoint;
for(int i=0; i<MAX_MARCHING_STEPS; i++)
{
hitpoint = (HitPos+ depth * viewRayDirection);
float dist = end;
for(int j =0; j<SDF_OBJECT_COUNT; j++)
{
float distToObjectBeingConsidered;
if(SDFObjects[j].Shape == Shpere)
distToObjectBeingConsidered = shereSDF(hitpoint, SDFObjects[j]);
if(SDFObjects[j].Shape == Box)
distToObjectBeingConsidered = boxSDF(hitpoint, SDFObjects[j].BoxSize , SDFObjects[j].Radius, SDFObjects[j].Position);
if(SDFObjects[j].Shape == Plane)
distToObjectBeingConsidered= planeSDF(hitpoint, vec4(SDFObjects[j].BoxSize, SDFObjects[j].Radius), SDFObjects[j].Position);
if( distToObjectBeingConsidered < dist)
{
dist = distToObjectBeingConsidered;
}
}
if(dist < EPSILON)
{
isShadow = true;
}
depth += dist;
if(depth >= end)
{
isShadow = false;
}
}
return isShadow;
}
vec3 MirrorReflection(vec3 inComingRay, vec3 surfNormal, vec3 HitPos, int objectIndexToIgnore)
{
vec3 returnCol;
vec3 reflectedRay = reflect(inComingRay, surfNormal);
vec3 RayDirection = normalize(reflectedRay) ;
float depth = start;
vec3 hitpoint;
int i;
for(i=0; i<MAX_MARCHING_STEPS; i++)
{
hitpoint = (HitPos+ depth * RayDirection);
SDFObject SDFObjectToDraw;
float dist = end;
for(int j =0; j<SDF_OBJECT_COUNT; j++)
{
float distToObjectBeingConsidered;
if(SDFObjects[j].Shape == Shpere)
distToObjectBeingConsidered = shereSDF(hitpoint, SDFObjects[j]);
if(SDFObjects[j].Shape == Box)
distToObjectBeingConsidered = boxSDF(hitpoint, SDFObjects[j].BoxSize , SDFObjects[j].Radius, SDFObjects[j].Position);
if(SDFObjects[j].Shape == Plane)
distToObjectBeingConsidered= planeSDF(hitpoint, vec4(SDFObjects[j].BoxSize, SDFObjects[j].Radius), SDFObjects[j].Position);
if( distToObjectBeingConsidered < dist && j!= objectIndexToIgnore )// D > 0.0)
{
dist = distToObjectBeingConsidered;
SDFObjectToDraw = SDFObjects[j];
}
}
if(dist < EPSILON)
{
vec3 normal =normalize(hitpoint-SDFObjectToDraw.Position);
float u = 0.5+ (atan(normal.z, normal.x)/(2.0*M_PI));
float v = 0.5+ (asin(normal.y)/(M_PI));
vec2 uv =vec2(u,v);
vec4 col = vec4(0,0.5,0.5,0);
///>>>>>>>>>>>> THESE LINES ARE broken, WHY?
//if(SDFObjectToDraw.texChannelID == 0)
//col = texture(iChannel0, uv);
//if(SDFObjectToDraw.texChannelID == 1)
//col = texture(iChannel1, uv);
vec3 NormalizedDirToLight = normalize(lightPos-SDFObjectToDraw.Position);
float theta = dot(normal,NormalizedDirToLight);
vec3 reflectionOfLight = reflect(NormalizedDirToLight, normal);
vec3 viewDir = normalize(SDFObjectToDraw.Position);
float Spec = dot(reflectionOfLight, viewDir);
if(IsShadow(lightPos, hitpoint))
{
returnCol= (col.xyz*SDFObjectToDraw.Ambiant);
}
else
{
returnCol= (col.xyz*SDFObjectToDraw.Ambiant)
+(col.xyz * max(theta *SDFObjectToDraw.Diff, SDFObjectToDraw.Ambiant));
}
break;
}
depth += dist;
if(depth >= end)
{
//should look up bg texture here but cant be assed right now
returnCol = vec3(1.0,0.0,0.0);
break;
}
}
return returnCol;//*= (vec3(i+1)/vec3(MAX_MARCHING_STEPS));
}
vec3 rayMarch(vec2 fragCoord)
{
vec3 viewRayDirection = rayDirection(45.0, iResolution.xy, fragCoord);
float depth = start;
vec3 hitpoint;
vec3 ReturnColour = vec3(0,0,0);
for(int i=0; i<MAX_MARCHING_STEPS; i++)
{
hitpoint = (eye+ depth * viewRayDirection);
float dist = end;
SDFObject SDFObjectToDraw;
int objectInDexToIgnore=-1;
//find closest objecct to current point
for(int j =0; j<SDF_OBJECT_COUNT; j++)
{
float distToObjectBeingConsidered;
if(SDFObjects[j].Shape == Shpere)
distToObjectBeingConsidered = shereSDF(hitpoint, SDFObjects[j]);
if(SDFObjects[j].Shape == Box)
distToObjectBeingConsidered = boxSDF(hitpoint, SDFObjects[j].BoxSize , SDFObjects[j].Radius, SDFObjects[j].Position);
if(SDFObjects[j].Shape == Plane)
distToObjectBeingConsidered= planeSDF(hitpoint, vec4(SDFObjects[j].BoxSize, SDFObjects[j].Radius), SDFObjects[j].Position);
if( distToObjectBeingConsidered < dist)
{
dist = distToObjectBeingConsidered;
SDFObjectToDraw = SDFObjects[j];
objectInDexToIgnore = j;
}
}
//if we are close enough to an objectoto hit it.
if(dist < EPSILON)
{
vec3 normal =normalize(hitpoint-SDFObjectToDraw.Position);
if(SDFObjectToDraw.isMirror)
{
ReturnColour = MirrorReflection( viewRayDirection, normal, hitpoint, objectInDexToIgnore);
}
else
{
float u = 0.5+ (atan(normal.z, normal.x)/(2.0*M_PI));
float v = 0.5+ (asin(normal.y)/(M_PI));
vec2 uv =vec2(u,v);
vec4 col;
if(SDFObjectToDraw.texChannelID == 0)
col = texture(iChannel0, uv);
if(SDFObjectToDraw.texChannelID == 1)
col = texture(iChannel1, uv);
vec3 NormalizedDirToLight = normalize(lightPos-SDFObjectToDraw.Position);
float theta = dot(normal,NormalizedDirToLight);
vec3 reflectionOfLight = reflect(NormalizedDirToLight, normal);
vec3 viewDir = normalize(SDFObjectToDraw.Position);
float Spec = dot(reflectionOfLight, viewDir);
if(IsShadow(lightPos, hitpoint))
{
ReturnColour= (col.xyz*SDFObjectToDraw.Ambiant);
}
else
{
ReturnColour= (col.xyz*SDFObjectToDraw.Ambiant)
+(col.xyz * max(theta *SDFObjectToDraw.Diff, SDFObjectToDraw.Ambiant));
//+(col.xyz* Spec * SDFObjectToDraw.Spec);
}
}
return ReturnColour;
}
depth += dist;
if(depth >= end)
{
float u = fragCoord.x/ iResolution.x;
float v = fragCoord.y/ iResolution.y;
vec4 col = texture(iChannel2, vec2(u,v));
ReturnColour =col.xyz;
}
}
return ReturnColour;
}
void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
// Normalized pixel coordinates (from 0 to 1)
//vec2 uv = fragCoord/iResolution.xy;
// Time varying pixel color
//vec3 col = 0.5 + 0.5*cos(iTime+uv.xyx+vec3(0,2,4));
// Output to screen
lightPos *= cos(iTime+vec3(1.5,2,2));
//lightPos= vec3(cos(iTime)*2.0,0,0);
vec3 SDFCol= rayMarch(fragCoord);
vec3 col = vec3(0);
//if(SDFVal <=1.0)
// col = vec3(1,0,0);
//col = vec3(SDFVal,0,0);
col = vec3(0.5,0,0);
col = SDFCol;
fragColor = vec4(col,1.0);
}
[...] This same code can bee seen in my rayMarch function (lines 274-277) and works fine for colouring my objects. [...]
The "working" texture lookup is executed in a loop in rayMarch. MAX_MARCHING_STEPS is 255, so the lookup is done at most 255 times.
vec3 rayMarch(vec2 fragCoord)
{
// [...]
for(int i=0; i<MAX_MARCHING_STEPS; i++)
{
// [...]
if(SDFObjectToDraw.texChannelID == 0)
col = texture(iChannel0, uv);
if(SDFObjectToDraw.texChannelID == 1)
col = texture(iChannel1, uv);
// [...]
}
// [...]
}
When you do the lookup in MirrorReflection then the performance breaks down, because it is done in a loop in MirrorReflection and MirrorReflection is called in a loop in rayMarch. In this case the lookup is done up to 255*255 = 65025 times.
~65000 texture lookups for a fragment is far to much and cause the break down of performance.
vec3 MirrorReflection(vec3 inComingRay, vec3 surfNormal, vec3 HitPos, int objectIndexToIgnore)
{
// [...]
for(i=0; i<MAX_MARCHING_STEPS; i++)
{
// [...]
if(SDFObjectToDraw.texChannelID == 0)
col = texture(iChannel0, uv);
if(SDFObjectToDraw.texChannelID == 1)
col = texture(iChannel1, uv);
// [...]
}
// [...]
}
vec3 rayMarch(vec2 fragCoord)
{
// [...]
for(int i=0; i<MAX_MARCHING_STEPS; i++)
{
// [...]
ReturnColour = MirrorReflection(viewRayDirection, normal, hitpoint, objectInDexToIgnore);
// [...]
}
// [...]
}

OpenGL: How do I render individual RGB values given a cross product that contains the total light?

I've been working on my own implementation of a Gouraud style shading model, and I've got the rest of it working pretty much how I want it, but the problem I've run into is it only shows white light. The calc_color function is where this operation is being performed. The Color variable represents the total light of the R, G and B values for that given location. I've been assigning Color to all three arrays just to get the shading implemented properly, but now that that is complete, I'd like to figure out a way to extract the R, G and B values from that total light value.
I've tried several different things, like taking the total light, and taking a percentage of the Light1r, etc. values but it always ends up looking strange or too bright.
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <iostream>
#ifdef MAC
#include <GLUT/glut.h>
#else
#include <GL/glut.h>
#endif
using namespace std;
//Camera variables
int xangle = -270;
int yangle = 0;
//Control Modes (Rotate mode by default)
int mode = 0;
int lightmode = 0;
//Player Position (Y offset so it would not be straddling the grid)
float cubeX = 0;
float cubeY = 0.5;
float cubeZ = 0;
//Vertex arrays for surface
float surfaceX [12][12];
float surfaceY [12][12];
float surfaceZ [12][12];
//Surface Normal arrays
float Nx[11][11];
float Ny[11][11];
float Nz[11][11];
//Color arrays
float R[11][11];
float G[11][11];
float B[11][11];
//Light position and color variables
float Light1x = 0;
float Light1y = 5;
float Light1z = 0;
float Light1r = 0;
float Light1g = 1;
float Light1b = 0;
float Light2x = -5;
float Light2y = 5;
float Light2z = -5;
float Light2r = 0;
float Light2g = 1;
float Light2b = 0;
//Random number generator
float RandomNumber(float Min, float Max)
{
return ((float(rand()) / float(RAND_MAX)) * (Max - Min)) + Min;
}
//---------------------------------------
// Initialize surface
//---------------------------------------
void init_surface()
{
//Initialize X, select column
for (int i = 0; i < 12; i++)
{
//Select row
//Surface is +1 so the far right normal will be generated correctly
for (int j = 0; j < 12; j++)
{
//-5 to compensate for negative coordinate values
surfaceX[i][j] = i-5;
//Generate random surface height
surfaceY[i][j] = RandomNumber(5, 7) - 5;
//surfaceY[i][j] = 0;
surfaceZ[i][j] = j-5;
}
}
}
void define_normals()
{
//Define surface normals
for (int i = 0; i < 11; i++)
{
for (int j = 0; j < 11; j++)
{
//Get two tangent vectors
float Ix = surfaceX[i+1][j] - surfaceX[i][j];
float Iy = surfaceY[i+1][j] - surfaceY[i][j];
float Iz = surfaceZ[i+1][j] - surfaceZ[i][j];
float Jx = surfaceX[i][j+1] - surfaceX[i][j];
float Jy = surfaceY[i][j+1] - surfaceY[i][j];
float Jz = surfaceZ[i][j+1] - surfaceZ[i][j];
//Do cross product, inverted for upward normals
Nx[i][j] = - Iy * Jz + Iz * Jy;
Ny[i][j] = - Iz * Jx + Ix * Jz;
Nz[i][j] = - Ix * Jy + Iy * Jx;
//Original vectors
//Nx[i][j] = Iy * Jz - Iz * Jy;
//Ny[i][j] = Iz * Jx - Ix * Jz;
//Nz[i][j] = Ix * Jy - Iy * Jx;
float length = sqrt(
Nx[i][j] * Nx[i][j] +
Ny[i][j] * Ny[i][j] +
Nz[i][j] * Nz[i][j]);
if (length > 0)
{
Nx[i][j] /= length;
Ny[j][j] /= length;
Nz[i][j] /= length;
}
}
}
}
void calc_color()
{
for (int i = 0; i < 10; i++)
{
for (int j = 0; j < 10; j++)
{
//Calculate light vector
//Light position, hardcoded for now 0,1,1
float Lx = Light1x - surfaceX[i][j];
float Ly = Light1y - surfaceY[i][j];
float Lz = Light1z - surfaceZ[i][j];
float length = sqrt(Lx * Lx + Ly * Ly + Lz * Lz);
if (length > 0)
{
Lx /= length;
Ly /= length;
Lz /= length;
}
//std::cout << "Lx: " << Lx << std::endl;
//std::cout << "Ly: " << Ly << std::endl;
//std::cout << "Lz: " << Lz << std::endl;
//Grab surface normals
//These are Nx,Ny,Nz due to compiler issues
float Na = Nx[i][j];
float Nb = Ny[i][j];
float Nc = Nz[i][j];
//std::cout << "Na: " << Na << std::endl;
//std::cout << "Nb: " << Nb << std::endl;
//std::cout << "Nc: " << Nc << std::endl;
//Do cross product
float Color = (Na * Lx) + (Nb * Ly) + (Nc * Lz);
std::cout << "Color: " << Color << std::endl;
//if (Color > 0)
//{
// Color = Color / 100;
//}
//Percent of light color
//float Ramt = (Light1r/2) / Color;
//float Gamt = (Light1g/2) / Color;
//float Bamt = (Light1b/2) / Color;
//R[i][j] = Ramt * Color;
//G[i][j] = Gamt * Color;
//B[i][j] = Bamt * Color;
R[i][j] = Color;
G[i][j] = Color;
B[i][j] = Color;
}
}
}
//---------------------------------------
// Init function for OpenGL
//---------------------------------------
void init()
{
glClearColor(0.0, 0.0, 0.0, 1.0);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
//Viewing Window Modified
glOrtho(-7.0, 7.0, -7.0, 7.0, -7.0, 7.0);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
//Rotates camera
//glRotatef(30.0, 1.0, 1.0, 1.0);
glEnable(GL_DEPTH_TEST);
//Project 3 code
init_surface();
define_normals();
//Shading code
// glShadeModel(GL_SMOOTH);
// glEnable(GL_NORMALIZE);
//X,Y,Z - R,G,B
// init_light(GL_LIGHT1, Light1x, Light1y, Light1z, Light1r, Light1g, Light1b);
// init_light(GL_LIGHT2, Light2x, Light2y, Light2z, Light2r, Light2g, Light2b);
//init_light(GL_LIGHT2, 0, 1, 0, 0.5, 0.5, 0.5);
}
void keyboard(unsigned char key, int x, int y)
{
///TODO: allow user to change color of light
//Controls
//Toggle Mode
if (key == 'q')
{
if(mode == 0)
{
mode = 1;
std::cout << "Switched to Light mode (" << mode << ")" << std::endl;
}
else if(mode == 1)
{
mode = 0;
std::cout << "Switched to Rotate mode (" << mode << ")" << std::endl;
}
}
//Toggle light control
else if (key == 'e' && mode == 1)
{
if(lightmode == 0)
{
lightmode = 1;
std::cout << "Switched to controlling light 2 (" << lightmode << ")" << std::endl;
}
else if(lightmode == 1)
{
lightmode = 0;
std::cout << "Switched to controlling light 1 (" << lightmode << ")" << std::endl;
}
}
////Rotate Camera (mode 0)
//Up & Down
else if (key == 's' && mode == 0)
xangle += 5;
else if (key == 'w' && mode == 0)
xangle -= 5;
//Left & Right
else if (key == 'a' && mode == 0)
yangle -= 5;
else if (key == 'd' && mode == 0)
yangle += 5;
////Move Light (mode 1)
//Forward & Back
else if (key == 'w' && mode == 1)
{
if (lightmode == 0)
{
Light1z = Light1z - 1;
//init_surface();
//define_normals();
//calc_color();
//glutPostRedisplay();
}
else if (lightmode == 1)
Light2z = Light2z - 1;
//init_surface();
}
else if (key == 's' && mode == 1)
{
if (lightmode == 0)
Light1z = Light1z + 1;
else if (lightmode == 1)
Light2z = Light2z + 1;
}
//Strafe
else if (key == 'd' && mode == 1)
{
if (lightmode == 0)
Light1x = Light1x + 1;
else if (lightmode == 1)
Light2x = Light2x + 1;
}
else if (key == 'a' && mode == 1)
{
if (lightmode == 0)
Light1x = Light1x - 1;
else if (lightmode == 1)
Light2x = Light2x - 1;
}
//Up & Down (Cube offset by +0.5 in Y)
else if (key == 'z' && mode == 1)
{
if (lightmode == 0)
Light1y = Light1y + 1;
else if (lightmode == 1)
Light2y = Light2y + 1;
}
else if (key == 'x' && mode == 1)
{
if (lightmode == 0)
Light1y = Light1y - 1;
else if (lightmode == 1)
Light2y = Light2y - 1;
}
//Redraw objects
glutPostRedisplay();
}
//---------------------------------------
// Display callback for OpenGL
//---------------------------------------
void display()
{
// Clear the screen
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
//Rotation Code
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glRotatef(xangle, 1.0, 0.0, 0.0);
glRotatef(yangle, 0.0, 1.0, 0.0);
//Light Code
// init_material(Ka, Kd, Ks, 100 * Kp, 0.8, 0.6, 0.4);
// init_light(GL_LIGHT1, Light1x, Light1y, Light1z, Light1r, Light1g, Light1b);
// init_light(GL_LIGHT2, Light2x, Light2y, Light2z, Light2r, Light2g, Light2b);
// glEnable(GL_LIGHTING);
//Color Code
calc_color();
//Draw the squares, select column
for (int i = 0; i <= 9; i++)
{
//Select row
for (int j = 0; j <= 9; j++)
{
glBegin(GL_POLYGON);
//Surface starts at top left
//Counter clockwise
glColor3f(R[i][j], G[i][j], B[i][j]);
std::cout << R[i][j] << " " << G[i][j] << " " << B[i][j] << endl;
// glNormal3f(Nx[i][j], Ny[i][j], Nz[i][j]);
glVertex3f(surfaceX[i][j], surfaceY[i][j], surfaceZ[i][j]);
glColor3f(R[i][j+1], G[i][j+1], B[i][j+1]);
// glNormal3f(Nx[i][j+1], Ny[i][j+1], Nz[i][j+1]);
glVertex3f(surfaceX[i][j+1], surfaceY[i][j+1], surfaceZ[i][j+1]);
glColor3f(R[i+1][j+1], G[i+1][j+1], B[i+1][j+1]);
// glNormal3f(Nx[i+1][j+1], Ny[i+1][j+1], Nz[i+1][j+1]);
glVertex3f(surfaceX[i+1][j+1], surfaceY[i+1][j+1], surfaceZ[i+1][j+1]);
glColor3f(R[i+1][j], G[i+1][j], B[i+1][j]);
// glNormal3f(Nx[i+1][j], Ny[i+1][j], Nz[i+1][j]);
glVertex3f(surfaceX[i+1][j], surfaceY[i+1][j], surfaceZ[i+1][j]);
glEnd();
}
}
// glDisable(GL_LIGHTING);
//Draw the normals
for (int i = 0; i <= 10; i++)
{
for (int j = 0; j <= 10; j++)
{
glBegin(GL_LINES);
glColor3f(0.0, 1.0, 1.0);
float length = 1;
glVertex3f(surfaceX[i][j], surfaceY[i][j], surfaceZ[i][j]);
glVertex3f(surfaceX[i][j]+length*Nx[i][j],
surfaceY[i][j]+length*Ny[i][j],
surfaceZ[i][j]+length*Nz[i][j]);
glEnd();
}
}
//Marking location of lights
glPointSize(10);
glBegin(GL_POINTS);
glColor3f(Light1r, Light1g, Light1b);
glVertex3f(Light1x, Light1y, Light1z);
glEnd();
glPointSize(10);
glBegin(GL_POINTS);
glColor3f(Light2r, Light2g, Light2b);
glVertex3f(Light2x, Light2y, Light2z);
glEnd();
//+Z = Moving TOWARD camera in opengl
//Origin point for reference
glPointSize(10);
glColor3f(1.0, 1.0, 0.0);
glBegin(GL_POINTS);
glVertex3f(0, 0, 0);
glEnd();
//Assign Color of Lines
float R = 1;
float G = 1;
float B = 1;
glBegin(GL_LINES);
glColor3f(R, G, B);
////Drawing the grid
//Vertical lines
for (int i = 0; i < 11; i++)
{
int b = -5 + i;
glVertex3f(b, 0, -5);
glVertex3f(b, 0, 5);
}
//Horizontal lines
for (int i = 0; i < 11; i++)
{
int b = -5 + i;
glVertex3f(-5,0,b);
glVertex3f(5,0,b);
}
glEnd();
glFlush();
}
//---------------------------------------
// Main program
//---------------------------------------
int main(int argc, char *argv[])
{
srand(time(NULL));
//Print Instructions
std::cout << "Project 3 Controls: " << std::endl;
std::cout << "q switches control mode" << std::endl;
std::cout << "w,a,s,d for camera rotation" << std::endl;
//Required
glutInit(&argc, argv);
//Window will default to a different size without
glutInitWindowSize(500, 500);
//Window will default to a different position without
glutInitWindowPosition(250, 250);
//
glutInitDisplayMode(GLUT_RGB | GLUT_SINGLE | GLUT_DEPTH);
//Required
glutCreateWindow("Project 3");
//Required, calls display function
glutDisplayFunc(display);
glutKeyboardFunc(keyboard);
//Required
init();
glutMainLoop();
return 0;
}
A common formula to calculate a a diffuse light is to calculate the Dot product of the normal vector of the surface and the vector to from the surface to the light source. See How does this faking the light work on aerotwist?.
kd = max(0, L dot N)
To get the color of the light, the RGB values are component wise multiplied by the diffuse coefficient:
(Cr, Cg, Cb) = (LCr, LCg, LCb) * kd
If there are multiple light sources, then the light colors are summed:
(Cr, Cg, Cb) = (LC1r, LC1g, LC1b) * max(0, L1 dot N) + (LC2r, LC2g, LC2b) * max(0, L2 dot N)
Note, if the surface (material) has an additional color, then ths color would have to be component wise multiplied to the final color:
(Cr, Cg, Cb) = (Cr, Cg, Cb) * (CMr, CMg, CMb)
Write a function which calculates the light for 1 single light source and add the light to the final color:
void add_light_color(int i, int j, float lpx, float lpy, float lpz, float lcr, float lcg, float lcb)
{
float Lx = lpx - surfaceX[i][j];
float Ly = lpy - surfaceY[i][j];
float Lz = lpz - surfaceZ[i][j];
float length = sqrt(Lx * Lx + Ly * Ly + Lz * Lz);
if (length <= 0.0)
return;
float kd = Lx/length * Nx[i][j] + Ly/length * Ny[i][j] + Ly/length * Ny[i][j];
if ( kd <= 0.0 )
return;
R[i][j] += kd * lcr;
G[i][j] += kd * lcg;
B[i][j] += kd * lcb;
}
Traverse the filed of attributes, set each color (0, 0, 0) and use the above function to add the color form each light source:
void calc_color()
{
float lp1[] = {Light1x, Light1y, Light1z};
float lp2[] = {Light2x, Light2y, Light2z};
float lc1[] = {Light1r, Light1g, Light1b};
float lc2[] = {Light2r, Light2g, Light2b};
for (int i = 0; i < 10; i++)
{
for (int j = 0; j < 10; j++)
{
R[i][j] = G[i][j] = B[i][j] = 0.0;
add_light_color(i, j, Light1x, Light1y, Light1z, Light1r, Light1g, Light1b);
add_light_color(i, j, Light2x, Light2y, Light2z, Light2r, Light2g, Light2b);
}
}
}
The result for the following light color settings:
float Light1r = 1;
float Light1g = 0;
float Light1b = 0;
float Light2r = 0;
float Light2g = 1;
float Light2b = 0;
I recommend to write your first Shader program, which does per fragment lighting and consists of a Vertex Shader and a Fragment Shader.
The program has to use GLSL version 2.00 (OpenGL Shading Language 1.20 Specification). This program can access the Fixed function attributes by the built in variables gl_Vertex, gl_Normal and gl_Color, as the fixed function matrices gl_NormalMatrix, gl_ModelViewMatrix and gl_ModelViewProjectionMatrix and the function ftransform().
See als Built in Vertex Attributes and Hello World in GLSL.
Further the program has to use Uniform Variables for the light colors and positions.
The Vertex shader transforms the model space coordinates and vectors to view space and pass the to the fragment shader by Varying Variables:
std::string vertex_shader = R"(
#version 120
uniform vec3 u_light_pos_1;
uniform vec3 u_light_pos_2;
varying vec3 v_pos;
varying vec3 v_nv;
varying vec4 v_color;
varying vec3 v_light_pos1;
varying vec3 v_light_pos2;
void main()
{
v_pos = (gl_ModelViewMatrix * gl_Vertex).xyz;
v_nv = gl_NormalMatrix * gl_Normal;
v_color = gl_Color;
v_light_pos1 = (gl_ModelViewMatrix * vec4(u_light_pos_1, 1.0)).xyz;
v_light_pos2 = (gl_ModelViewMatrix * vec4(u_light_pos_2, 1.0)).xyz;
gl_Position = ftransform();
}
)";
The fragment shader dose the per Fragment Light calculations in view space:
std::string fragment_shader = R"(
#version 120
varying vec3 v_pos;
varying vec3 v_nv;
varying vec4 v_color;
varying vec3 v_light_pos1;
varying vec3 v_light_pos2;
uniform vec3 u_light_col_1;
uniform vec3 u_light_col_2;
void main()
{
vec3 N = normalize(v_nv);
vec3 L1 = normalize(v_light_pos1 - v_pos);
vec3 L2 = normalize(v_light_pos2 - v_pos);
float kd_1 = max(0.0, dot(L1, N));
float kd_2 = max(0.0, dot(L2, N));
vec3 light_sum = kd_1 * u_light_col_1 + kd_2 * u_light_col_2;
gl_FragColor = vec4(v_color.rgb * light_sum, v_color.a);
}
)";
Compile the shader stages
GLuint generate_shader(GLenum stage, const std::string &source)
{
GLuint shader_obj = glCreateShader(stage);
const char *srcCodePtr = source.c_str();
glShaderSource(shader_obj, 1, &srcCodePtr, nullptr);
glCompileShader(shader_obj);
GLint status;
glGetShaderiv(shader_obj, GL_COMPILE_STATUS, &status);
if (status == GL_FALSE)
{
GLint maxLen;
glGetShaderiv(shader_obj, GL_INFO_LOG_LENGTH, &maxLen);
std::vector< char >log( maxLen );
GLsizei len;
glGetShaderInfoLog(shader_obj, maxLen, &len, log.data());
std::cout << "compile error:" << std::endl << log.data() << std::endl;
}
return shader_obj;
}
and link the program.
GLuint generate_program(const std::string &vert_sh, const std::string &frag_sh)
{
std::cout << "compile vertex shader" << std::endl;
GLuint vert_obj = generate_shader(GL_VERTEX_SHADER, vert_sh);
std::cout << "compile fragment shader" << std::endl;
GLuint frag_obj = generate_shader(GL_FRAGMENT_SHADER, frag_sh);
std::cout << "link shader program" << std::endl;
GLuint program_obj = glCreateProgram();
glAttachShader(program_obj, vert_obj);
glAttachShader(program_obj, frag_obj);
glLinkProgram(program_obj);
GLint status;
glGetProgramiv(program_obj, GL_LINK_STATUS, &status);
if (status == GL_FALSE)
{
GLint maxLen;
glGetProgramiv(program_obj, GL_INFO_LOG_LENGTH, &maxLen);
std::vector< char >log( maxLen );
GLsizei len;
glGetProgramInfoLog(program_obj, maxLen, &len, log.data());
std::cout << "link error:" << std::endl << log.data() << std::endl;
}
glDeleteShader(vert_obj);
glDeleteShader(frag_obj);
return program_obj;
}
Further get the uniform locations by glGetUniformLocation in the function init:
GLuint diffuse_prog_obj = 0;
GLint loc_l_pos[] = {-1, -1};
GLint loc_l_col[] = {-1, -1};
void init()
{
diffuse_prog_obj = generate_program(vertex_shader, fragment_shader);
loc_l_pos[0] = glGetUniformLocation(diffuse_prog_obj, "u_light_pos_1");
loc_l_pos[1] = glGetUniformLocation(diffuse_prog_obj, "u_light_pos_2");
loc_l_col[0] = glGetUniformLocation(diffuse_prog_obj, "u_light_col_1");
loc_l_col[1] = glGetUniformLocation(diffuse_prog_obj, "u_light_col_2");
// [...]
}
The shader program can be used by glUseProgram. The uniforms are set by glUniform*.
Beside the vertex coordinates, the normal vector attributes have to be set per vertex, to make the light calculations proper work. But it is sufficient to set a single color for the entire mesh:
void display()
{
// [...]
// install program
glUseProgram(diffuse_prog_obj);
// set light positions and colors
glUniform3f(loc_l_pos[0], Light1x, Light1y, Light1z);
glUniform3f(loc_l_pos[1], Light2x, Light2y, Light2z);
glUniform3f(loc_l_col[0], Light1r, Light1g, Light1b);
glUniform3f(loc_l_col[1], Light2r, Light2g, Light2b);
// set object color
glColor3f(1, 1, 0.5);
//Draw the squares, select column
for (int i = 0; i <= 9; i++)
{
//Select row
for (int j = 0; j <= 9; j++)
{
glBegin(GL_POLYGON);
std::cout << R[i][j] << " " << G[i][j] << " " << B[i][j] << endl;
glNormal3f(Nx[i][j], Ny[i][j], Nz[i][j]);
glVertex3f(surfaceX[i][j], surfaceY[i][j], surfaceZ[i][j]);
glNormal3f(Nx[i][j+1], Ny[i][j+1], Nz[i][j+1]);
glVertex3f(surfaceX[i][j+1], surfaceY[i][j+1], surfaceZ[i][j+1]);
glNormal3f(Nx[i+1][j+1], Ny[i+1][j+1], Nz[i+1][j+1]);
glVertex3f(surfaceX[i+1][j+1], surfaceY[i+1][j+1], surfaceZ[i+1][j+1]);
glNormal3f(Nx[i+1][j], Ny[i+1][j], Nz[i+1][j]);
glVertex3f(surfaceX[i+1][j], surfaceY[i+1][j], surfaceZ[i+1][j]);
glEnd();
}
}
// invalidate installed program
glUseProgram(0);
// [...]
}
See the preview of you program, with the applied suggestions:

Opengl sometimes works funnily

I want to draw 2 circles, and here are the necessary bits from three files of my project.
circle.h
#include "main.h"
#ifndef CIRCLE_H
#define CIRCLE_H
class Circle{
public:
Circle(){};
Circle(float x, float y, float radius, float fraction, color_t color);
void set_position(float x, float y);
void draw(glm::mat4 VP);
bounding_box_t bounding_box();
glm::vec3 position;
float rotation;
float radius;
float fraction;
private:
VAO *object;
};
#endif //CIRCLE_H
circle.cpp
#include "circle.h"
#include "main.h"
#include <iostream>
#define PI 3.14159265358979323846
typedef struct {
GLfloat x;
GLfloat y;
GLfloat z;
}point;
point rotate_by(double angle, point inp_point){
point new_point;
new_point.x = inp_point.x*cos(angle) - inp_point.y*sin(angle);
new_point.y = inp_point.x*sin(angle) + inp_point.y*cos(angle);
return new_point;
};
void fill_array(int sides, float radius, int fraction, GLfloat array[]){
double angle = 2 * PI / sides;
point vertex;
vertex.x = 0.0f;
vertex.y = 0.0f;
vertex.z = 0.0f;
point oldpoint;
oldpoint.x = 0.0f;
oldpoint.y = radius;
oldpoint.z = 0.0f;
int iterations = (int)(fraction*sides);
std::cout<< iterations <<std::endl;
//For each triangle
for(int i = 0; i < iterations; i++){
// setting the vertex
array[9*i + 0] = vertex.x;
array[9*i + 1] = vertex.y;
array[9*i + 2] = vertex.z;
// setting the oldpoint
array[9*i + 3] = oldpoint.x;
array[9*i + 4] = oldpoint.y;
array[9*i + 5] = oldpoint.z;
// making the new point
point newpoint = rotate_by(angle, oldpoint);
// setting the new point
array[9*i + 6] = newpoint.x;
array[9*i + 7] = newpoint.y;
array[9*i + 8] = newpoint.z;
// New point is now old
oldpoint = newpoint;
}
};
Circle::Circle(float x, float y, float radius, float fraction, color_t color) {
this->position = glm::vec3(x, y, 0);
this->rotation = 0;
int sides = 50;
this->radius = radius;
this->fraction = fraction;
static GLfloat vertex_buffer_data[1000];
fill_array(sides, radius, fraction, vertex_buffer_data);
int num_vertices = (int)(fraction*sides)*3;
std::cout << num_vertices <<std::endl;
this->object = create3DObject(GL_TRIANGLES, num_vertices, vertex_buffer_data, color, GL_FILL);
}
void Circle::draw(glm::mat4 VP) {
Matrices.model = glm::mat4(1.0f);
glm::mat4 translate = glm::translate (this->position); // glTranslatef
glm::mat4 rotate = glm::rotate((float) (this->rotation * M_PI / 180.0f), glm::vec3(0, 0, 1));
rotate = rotate * glm::translate(glm::vec3(0, -0.6, 0));
Matrices.model *= (translate * rotate);
glm::mat4 MVP = VP * Matrices.model;
glUniformMatrix4fv(Matrices.MatrixID, 1, GL_FALSE, &MVP[0][0]);
draw3DObject(this->object);
}
main.cpp
//-------------------Necessary tools----------------------------------------------
#include <vector>
#include <random>
#include "timer.h"
#include "main.h"
using namespace std;
//------------------Template Imports---------------------------------------------
#include "circle.h"
//---------------Important declarations-------------------------------------------
GLMatrices Matrices;
GLuint programID;
GLFWwindow *window;
float screen_zoom = 1, screen_center_x = 0, screen_center_y = 0;
Timer t60(1.0 / 60);
//--------------Object declarations-----------------------------------------------
Circle c1;
Circle c2;
// -------------Functions----------------------------------------------------------
void draw() {
//---------Don't touch---------------------------------------------------------
glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glUseProgram (programID);
Matrices.view = glm::lookAt(glm::vec3(0, 0, 3), glm::vec3(0, 0, 0), glm::vec3(0, 1, 0)); // Fixed camera for 2D (ortho) in XY plane
glm::mat4 VP = Matrices.projection * Matrices.view;
glm::mat4 MVP;
//---------Scene render--------------------------------------------------------
c1.draw(VP);
c2.draw(VP);
}
void initGL(GLFWwindow *window, int width, int height) {
//-----------Create objects----------------------------------------------
c1 = Circle(1,1,1.0f,1.0f,COLOR_BLACK);
c2 = Circle(2,2,1.0f,1.0f, COLOR_RED);
//-----------Don't touch-------------------------------------------------
programID = LoadShaders("Sample_GL.vert", "Sample_GL.frag");
Matrices.MatrixID = glGetUniformLocation(programID, "MVP");
reshapeWindow (window, width, height);
glClearColor (COLOR_BACKGROUND.r / 256.0, COLOR_BACKGROUND.g / 256.0, COLOR_BACKGROUND.b / 256.0, 0.0f); // R, G, B, A
glClearDepth (1.0f);
glEnable (GL_DEPTH_TEST);
glDepthFunc (GL_LEQUAL);
cout << "VENDOR: " << glGetString(GL_VENDOR) << endl;
cout << "RENDERER: " << glGetString(GL_RENDERER) << endl;
cout << "VERSION: " << glGetString(GL_VERSION) << endl;
cout << "GLSL: " << glGetString(GL_SHADING_LANGUAGE_VERSION) << endl;
}
int main(int argc, char **argv) {
srand(time(0));
int width = 600;
int height = 600;
window = initGLFW(width, height);
initGL (window, width, height);
/* Draw in loop */
while (!glfwWindowShouldClose(window)) {
// Process timers
if (t60.processTick()) {
// 60 fps
// OpenGL Draw commands
draw();
// Swap Frame Buffer in double buffering
glfwSwapBuffers(window);
tick_elements();
//tick_input(window);
}
// Poll for Keyboard and mouse events
glfwPollEvents();
}
quit(window);
}
void reset_screen() {
float top = screen_center_y + 4 / screen_zoom;
float bottom = screen_center_y - 4 / screen_zoom;
float left = screen_center_x - 4 / screen_zoom;
float right = screen_center_x + 4 / screen_zoom;
Matrices.projection = glm::ortho(left, right, bottom, top, 0.1f, 500.0f);
}
For some reason, sometimes, I get the right output, but sometimes, I get funny outputs.
This is the correct image that I want:
But several times nothing appears at all:
Sometimes really funny outputs appear, like this:
... And this:
Notice that I am not even running make again and again, these variations happen each time I am opening the executable itself.
How can I get the correct output every time?
Do you call reset_screen() function somewhere? Because in this function you are creating your projection matrix. It looks like this matrix is uninitialized (initialized with some trash).

Abysmal performance with vectors in c++

Disclaimer: i just started programming in c++.
So i made a little program in c++ using SFML where i have 300 particles stored in a vector that are attracted to each other.
The program runs at 17 FPS!! i tried doing the same using a standard array and it didn't run much faster.
The same code in java using the Array libgdx class runs at 5000 FPS.
Using the java.util.Vector class runs at 2000 FPS.
Debugging i found out that most of the delay comes from retrieving an element from the vector.
I know i'm doing something wrong in that regard, so i would be grateful if someone can help me out.
Here is the update loop.
void update(RenderWindow& window) {
window.clear();
for (size_t i = 0, size = particles.size(); i != size; ++i) {
Particle &part = particles[i];
glm::vec2 forceSum;
for (size_t j = 0, size = particles.size(); j != size; ++j) {
Particle &part2 = particles[j];
if (j == i)
continue;
glm::vec2 v1 = part.getPos() - part2.getPos();
glm::vec2 v2 = glm::normalize(v1);
if (glm::length(v1) < 50)
forceSum += (v2 * -0.1f);
}
part.applyForce(forceSum);
part.update();
shape.setPosition(part.getPos().x, part.getPos().y);
window.draw(shape);
}
window.display();
}
I can show more code if requested.
Thanks in advance!
main.cpp
#include "stdafx.h"
using namespace std;
using namespace sf;
void update(RenderWindow& window);
int t = 0;
CircleShape shape(4);
vector<Particle> particles;
int main() {
ContextSettings settings;
settings.antialiasingLevel = 16;
RenderWindow window(VideoMode(800, 800), "test", Style::Default, settings);
shape.setFillColor(Color(155, 155, 155, 124));
srand(4);
for (int i = 0; i < 300; i++) {
Particle particle = Particle(glm::vec2(rand() % 800, rand() % 800), rand() % 10);
particles.push_back(particle);
}
while (window.isOpen())
{
Event event;
while (window.pollEvent(event))
{
if (event.type == Event::Closed)
window.close();
}
update(window);
}
return 0;
}
void update(RenderWindow& window) {
window.clear();
for (size_t i = 0, size = particles.size(); i != size; ++i) {
Particle &part = particles[i];
glm::vec2 forceSum;
for (size_t j = 0, size = particles.size(); j != size; ++j) {
Particle &part2 = particles[j];
if (j == i)
continue;
glm::vec2 v1 = part.getPos() - part2.getPos();
glm::vec2 v2 = glm::normalize(v1);
if (glm::length(v1) < 50)
forceSum += (v2 * -0.1f);
}
part.applyForce(forceSum);
part.update();
shape.setPosition(part.getPos().x, part.getPos().y);
window.draw(shape);
}
window.display();
}
Particle.h
#pragma once
#include <SFML/Graphics.hpp>
class Particle {
public:
Particle(glm::vec2 pos, float m);
void update();
void applyForce(const glm::vec2 &fce);
void setPos(const glm::vec2 &pos);
void setV(const glm::vec2 &vel);
const glm::vec2 getPos();
const glm::vec2 getV();
private:
float mass;
glm::vec2 acceleration = glm::vec2(0, 0);
glm::vec2 position = glm::vec2(0, 0);
glm::vec2 velocity = glm::vec2(0, 0);
};
Particle.cpp
#include "stdafx.h"
#include "Particle.h"
#include <iostream>
using glm::vec2;
Particle::Particle(vec2 pos, float m) {
mass = m;
position = pos;
}
void Particle::update() {
velocity += acceleration;
if (position.x + velocity.x > 800 || position.x + velocity.x < 0 || position.y + velocity.y > 800 || position.y + velocity.y < 0) {
velocity.x = 0; velocity.y = 0;
}
position += velocity;
}
void Particle::applyForce(const vec2 &fce) {
acceleration = fce/mass;
}
void Particle::setPos(const vec2 &pos) {
position = pos;
}
void Particle::setV(const vec2 &vel) {
velocity = vel;
}
const vec2 Particle::getV() {
return velocity;
}
const vec2 Particle::getPos() {
return position;
}
I've ran a vs studio profiler session on your code.
The app spends 59% of its time calculating square roots. You should optimize your code as follow:
glm::vec2 v1 = part.getPos() - part2.getPos();
glm::vec2 v2 = glm::normalize(v1); // <- 1 square root computed here
if (glm::length(v1) < 50) // <- another one here
forceSum += (v2 * -0.1f);
Becomes:
glm::vec2 v1 = part.getPos() - part2.getPos();
float l = glm::dot(v1, v1);
if (l < 2500)
{
forceSum += (v1 / sqrt(l));
}
} // end inner loop
part.applyForce(forceSum * .1f);
This will divide the time spent calculating square roots by 2 or more.
With null vec2s this optimization made execution on my machine go from 38s/10000 frames to 7s/10000 frames.
Thanks to BorgLeader and Rabbid76, who helped optimize your code further.