Уроки, алгоритмы, программы, примеры

Вход на сайт

Материалы по разделам

Построения
на плоскости (2D)
Графика
в пространстве (3D)
Вычислительная
геометрия
Физическое
моделирование
Фрактальная
графика

Новые комментарии

torrvic, возможно, Вам нужно добавить -lGLU
Извините за тупой вопрос. У меня при сборке Вашего примера выходит ошибка: "undefined reference to gluLookAt". Не могу найти в какой библиотеке находится эта функция. У меня задано: -lGL -lglut ... Искал в /usr/lib таким образом: nm lib*so* | grep...
Здравствуйте. Спасибо за проект. У меня вопрос, по какой причине определение принадлежности точки многоугольнику работает некорректно, если координаты из больших чисел состоят, например: int[] vertex = new int[] {...
Сейчас проверила нашла причину не запускания // Создание контекста воспроизведения OpenGL и привязка его к панели на форме OpenGLControl1:=TOpenGLControl.Create(Self); with OpenGLControl1 do begin Name:='OpenGLControl1'; //вот тут...
Ну..кажется что то пошло не так http://pp.usera...

Счетчики и рейтинг

Рейтинг@Mail.ru
Скриншот к примеру
Среда программирования: 
Visual Studio 2015
Статья по теме: 

Итак, начнем реализовывать задачи, поставленные в статье. Я опишу только ключевые моменты, все остальное вы найдете в исходниках.
Я буду использовать графическую библиотеку SFML версии 2.3.2 и некоторые элементы библиотеки OpenGL (все необходимое уже включено в SFML).

Для моделирования частицы я создал класс Particle

#include <iostream>
#include <SFML\Graphics.hpp>
#include <math.h>
#include <vector>
#pragma once
using namespace std;
 
class Particle
{
private: vector <sf::Vector2f> forces; // Скорости, которые воздействуют на частицу
		 sf::Vector2f position; // Позиция частицы
		 sf::Vector2f main_force; // Результирующая сила
		 sf::Vector2f speed; // Скорость частицы
		 int mass; // Масса частицы
public:
	Particle(); 
	Particle(int m, sf::Vector2f pos); 
	sf::Vector2f getSpeed()  noexcept { return speed; };
	sf::Vector2f getPosition() const noexcept { return position; }
	sf::Vector2f getMainForce() noexcept; // Находит результирующую силу
	void cler_force () noexcept; // Очищает std::vector, когда силы перестают действовать
	void addForce(sf::Vector2f & n_force) noexcept; // Добавляет новую силу
	void update(float && t) noexcept; // Обновляет все характеристики нашей точки
	~Particle();
};

Теперь определимся, сколько точек мы будет отрисовывать и каким именно образом.
Я решил остановиться на 100`000 точек, хотя можно взять и намного больше, скажем 1`000`000.
Рисовать наши частицы (по факту - точки) можно несколькими способами. Однако мы сразу отбросим два из них - это рисование с помощью стандартных контейнеров SFML, таких как sf::Vertex и sf::VertexArray, они слишком неэффективны, да и предназначены несколько для другого.
Рисовать мы будем с помощью OpenGL, однако и тут есть несколько вариантов.
1. Рисовать каждую из 100`000 отдельно, используя функцию glVertex2d(x,y).
2. Рисовать сразу массив точек с помощью glVertexPointer() и glDrawArrays().
Второй вариант работает намного быстрее, так что выбираем его.

Для этого создаем двумерный динамический массив типа float (динамический - чтобы ненароком не наступить в переполненный стек)

float ** coords = new float * [NUMB_OF_BLOCK];
for (int i = 0; i < NUMB_OF_BLOCK; ++i)
		coords[i] = new float[PART_IN_BLOCK * 2];
//NUMB_OF_BLOCK и PART_IN_BLOCK  - константы из const.h равные 100 и 1000

Чтобы было веселее - раскрасим наши точки, причем не единожды. Будем менять цвет точек в зависимости от их скорости.
Создаем похожий массив, только теперь тип его элементов - unsigned char

unsigned char ** colors = new unsigned char *[NUMB_OF_BLOCK];
	for (int i = 0; i < NUMB_OF_BLOCK; ++i)
		colors[i] = new unsigned char[PART_IN_BLOCK * 3];

Комрад! Не забудь высвободить динамическую память!

Рассмотрим теперь, как именно добавлять силы к нашим частицам.

for (auto it = 0; it < all_part.size(); ++it) {
			if (LB_press) {
				sf::Vector2f mouse = sf::Vector2f(sf::Mouse::getPosition(window)); // Записываем координаты мышки относительно нашего окна
 
				all_part[it].addForce((mouse - all_part[it].getPosition()) * (G / pow(getDistance(move(mouse), all_part[it].getPosition()) + 5, 2)));
				all_part[it].addForce(-all_part[it].getSpeed()*100.0f); // Тормозящая сила (сила сопротивления), вместе 100*f можно подставить любую магическую константу, чем больше - тем меньшее ускорение у частиц.
			}
 
			all_part[it].update(move(frame_time)); // Передает в update информацию о том, сколько времени ушло для вычисления всей информации на предыдущем кадре
			if (LB_press) all_part[it].cler_force(); // Очищает вектор скоростей
 
		}

Отдельно рассмотрим эту строчку

all_part[it].addForce((mouse - all_part[it].getPosition()) * (G / pow(getDistance(move(mouse), all_part[it].getPosition()) + 5, 2)));

В статье есть формула F = (N*G)/D^2
Здесь N = (mouse - all_part[it].getPosition()).

getDistance(move(mouse), all_part[it].getPosition()) - расстояние между точкой нажатия мышки и частицей it. После вычисления расстояния к нему прибавляется некоторая константа. Это сделано для того, чтобы при расстоянии существенно меньше единицы, сила, которую мы добавляем, не возрастала.

В начале я упоминал, что частицы будут изменять свой цвет в зависимости от скорости.
Реализуется это следующим образом:

for (int i = 0; i < NUMB_OF_BLOCK; ++i) {
			for (int j = 0; j < PART_IN_BLOCK; ++j) { // Цвета подобраны почти случайно, каждый может менять как горазд
				colors[i][3 * j] = 152;
				colors[i][3 * j + 1] =  50 + static_cast <int> (modOfVector(all_part[j + i*PART_IN_BLOCK].getSpeed())); // Находим модуль вектора скорости
				colors[i][3 * j + 2] = 10 + static_cast <int> (modOfVector(all_part[j + i*PART_IN_BLOCK].getSpeed())) ;
			}
		}

Функция modOfVector() просто определяет модуль вектора. В нашем случае - модуль вектора скорости. По хорошему - получившееся значение надо округлить по модулю 256, однако реализация функции OpenGL, которая будет работать с этим массивом, самостоятельно займется этим.

Код программы: 


Source.cpp


#include <iostream>
#include <math.h>
#include <vector>
#include <SFML\Graphics.hpp>
#include "Particle.h"
#include "const.h"
#include <SFML\OpenGL.hpp>
 
 
using namespace std;
 
float modOfVector(sf::Vector2f & v) { // Находим модуль вектора
	return sqrt(v.x*v.x + v.y*v.y); 
}
float getDistance(sf::Vector2f && v1, sf::Vector2f && v2) noexcept { // Растояние между двумя точками
	return sqrt(pow((v2.x - v1.x), 2) + pow((v2.y - v1.y), 2));
}
 
void particleTransform(vector <Particle>const & part, float ** & i_vec){ // Функция переписывает координаты частиц из вектора, в двумерный массив
	for (int i = 0; i < NUMB_OF_BLOCK; ++i) {
		for (int j = 0; j < PART_IN_BLOCK; ++j) {
			i_vec[i][j * 2] = part[j + i*PART_IN_BLOCK].getPosition().x;
			i_vec[i][j * 2 + 1] = part[j + i*PART_IN_BLOCK].getPosition().y;
		}
	}
}
 
int main() {
	sf::ContextSettings set;
	set.antialiasingLevel = 8; // Устанавливаем уровень сглаживания, хотя можно и без него обойтись
 
	sf::RenderWindow window(sf::VideoMode(winW, winH), "PS", sf::Style::Default, set); // Создаем окно, в котором будем рисовать. winW & winH - константы из const.h
 
	vector <Particle> all_part; // Вектор частиц
 
	float ** coords = new float * [NUMB_OF_BLOCK]; // Двумерный динамический массив координат точек, используется 
 
	for (int i = 0; i < NUMB_OF_BLOCK; ++i)
		coords[i] = new float[PART_IN_BLOCK * 2];
 
	unsigned char ** colors = new unsigned char *[NUMB_OF_BLOCK]; // Аналогичный массив, хранящий информацию о цвете каждой точки
	for (int i = 0; i < NUMB_OF_BLOCK; ++i)
		colors[i] = new unsigned char[PART_IN_BLOCK * 3];
 
	for (int i = 0; i < NUMB_OF_BLOCK; i++) {
		for (int j = 0; j < PART_IN_BLOCK; j++) {// sf::Vector2f(200, 100) - смешение для точек 
			srand(time(0));
			int mass = 10 + rand() %  70; // Выбираем случайую массу в диапазоне от 10 до 79
			Particle tmp(mass, sf::Vector2f(20, 100) + sf::Vector2f(j*0.6, i*3)); // Создаем новую частицу
			all_part.push_back(tmp);
			coords[i][2*j] = tmp.getPosition().x; // Добавляем координаты частицы в массив
			coords[i][2*j +1] = tmp.getPosition().y;
		}
	}
 
	////////////////// OpenGL settings
	glEnable(GL_POINT_SMOOTH); //Три функции приводят нашу квадратную точку к круглой
	glEnable(GL_BLEND);
	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
 
	glPointSize(1); // Размер точки - 1 пиксель
	glOrtho(0, winW, winH, 0, 1, -1); // Ориентирование нашей сцены
	//////////////////////////////////
 
	bool LB_press = 0;
 
	sf::Clock clock; // Часы, чтобы замерять сколько времени уходит на обработку каждого кадра
	float frame_time = 0.1f;
 
	sf::Event event;
	while (window.isOpen()) {
		while (window.pollEvent(event)) {
			if (event.type == sf::Event::Closed)
				window.close();
		}
		if (sf::Mouse::isButtonPressed(sf::Mouse::Left))// Тригер для нажатия ЛКМ
			LB_press = 1;
		else
			LB_press = 0;
 
		glClear(GL_COLOR_BUFFER_BIT); // Очистка буфера
 
		for (auto it = 0; it < all_part.size(); ++it) {
			if (LB_press) { // Если нажата ЛКМ, то добавляем силы к частицам
				sf::Vector2f mouse = sf::Vector2f(sf::Mouse::getPosition(window)); // Записываем координаты мышки относительно нашего окна
				all_part[it].addForce((mouse - all_part[it].getPosition()) * (G / pow(getDistance(move(mouse), all_part[it].getPosition()) + 5, 2)));
				all_part[it].addForce(-all_part[it].getSpeed()*100.0f); // Тормозящая сила (сила сопротивления), вместе 100*f можно подставить любую магическую константу, чем больше - тем меньшее ускорение у частиц.
			}
			//Обновляем частицу
			all_part[it].update(move(frame_time)); // Передает в update информацию о том, сколько времени ушло для вычисления всей информации на предыдущем кадре
			if (LB_press) all_part[it].cler_force(); // Очищаем вектор скоростей
 
		}
 
		glPushMatrix();
 
		particleTransform(all_part, coords); // Переносим обновленные данные из вектора в массив
 
		glEnableClientState(GL_VERTEX_ARRAY);
		glEnableClientState(GL_COLOR_ARRAY);
 
 
		for (int i = 0; i < NUMB_OF_BLOCK; ++i) {
			for (int j = 0; j < PART_IN_BLOCK; ++j) { // Цвета подобраны почти случайно, каждый может менять как горазд
				colors[i][3 * j] = 152;
				colors[i][3 * j + 1] =  50 + static_cast <int> (modOfVector(all_part[j + i*PART_IN_BLOCK].getSpeed())); // Находим модуль вектора скорости
				colors[i][3 * j + 2] = 10 + static_cast <int> (modOfVector(all_part[j + i*PART_IN_BLOCK].getSpeed())) ;
			}
		}
 
		for (int i = 0; i < NUMB_OF_BLOCK; ++i) {
			glVertexPointer(2, GL_FLOAT, 0, coords[i]); // Передаем кол-во координат точки, формат координат и указатель на массив координат
			glColorPointer(3, GL_UNSIGNED_BYTE, 0, colors[i]); // Тоже самое, только для цвета.
			glDrawArrays(GL_POINTS, 0, PART_IN_BLOCK); // Функция отображения массива элементов. Передаем тип отображаемого объекта, смещение до следующего элемента (у нас 0, т.к. массив линейный) и размер массива.
 
		}
 
		glDisableClientState(GL_VERTEX_ARRAY);
		glDisableClientState(GL_COLOR_ARRAY);
 
		glPopMatrix();
 
		glFlush();
		window.display(); // Отображаем кадр.
		frame_time = clock.restart().asSeconds(); // Записываем прошедшее время и перезапускаем часы.
 
	}
	for (int i = 0; i < NUMB_OF_BLOCK; ++i) // Освобождаем динамическую память.
		delete[] coords[i];
	for (int i = 0; i < NUMB_OF_BLOCK; ++i) // Освобождаем динамическую память.
		delete[] colors[i];
	return 0;
}


Particle.h

#include <iostream>
#include <SFML\Graphics.hpp>
#include <math.h>
#include <vector>
#pragma once
using namespace std;
 
class Particle
{
private: vector <sf::Vector2f> forces;
		 sf::Vector2f position;
		 sf::Vector2f main_force;
		 sf::Vector2f speed;
		 int mass;
public:
	Particle();
	Particle(int m, sf::Vector2f pos);
	sf::Vector2f getSpeed()  noexcept { return speed; };
	sf::Vector2f getPosition() const noexcept { return position; }
	sf::Vector2f getMainForce() noexcept;
	void cler_force () noexcept;
	void addForce(sf::Vector2f & n_force) noexcept;
	void update(float && t) noexcept;
	~Particle();
};


Particle.cpp

#include "Particle.h"
 
 
 
Particle::Particle() // Стандартный конструктор не имеет реализации, т.к. не пригодился
{
}
Particle::Particle(int m, sf::Vector2f pos) { // Конструктор инициализирует массу и начальное положение
	mass = m;
	position = pos;
	speed = sf::Vector2f(0,0); // Начальная скорость - 0
}
 
void Particle::cler_force() noexcept { // Очищает std::vector от векторов силы. Т.е. убирает все силы, которые действуют на точку
	forces.clear();
}
 
void Particle::addForce(sf::Vector2f & n_force) noexcept { // Добавляет вектор силы в std::vector. На точку теперь воздейтсвует новая сила
	forces.push_back(n_force);
}
sf::Vector2f Particle::getMainForce() noexcept {// Подсчитывает результирующую силу. Т.к. силу можно однозначно характеризовать вектором, мы просто складываем все вектора в std::vector 
	sf::Vector2f res(0, 0);
	for (auto it = forces.begin(); it != forces.end(); ++it)
		res += *it;
	return res;
}
void Particle::update(float && t) noexcept {
	sf::Vector2f prevPos = position; // Сохраняем текущую позицию точку
	sf::Vector2f res_force = getMainForce(); // Находим результирующую силу
 
	position += 0.5f*res_force*t*t / static_cast <float>(mass) + speed*t; // Обновляем позицию на текущем кадре
 
	// Оба варианта определения скорости корректны - но второй работает несколько быстрее
	//speed = (res_force / static_cast <float>(mass)) * t + speed; // Обновляем скорость
	speed = (position - prevPos) / t; // Обновляем скорость
 
}
 
 
Particle::~Particle()
{
}


const.h

#pragma once
 
const int winW = 800; // Ширина окна
const int winH = 500; // Высота окна
#ifdef DEBUG
const int NUMB_OF_BLOCK = 10;
const int PART_IN_BLOCK = 1000;
#endif // DEBUG
 
#ifdef RELEASE
const int NUMB_OF_BLOCK = 100;
const int PART_IN_BLOCK = 1000;
#endif // RELEASE
 
const float G = 300000;

Прикрепленный файлРазмер
Particle_System.zip1020.95 кб