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

Вход на сайт

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

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

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

dobryj den, popytalas otkryt prikreplionnyj fail ctoby posmotret kak rabotaet, no mne ego ne pokazyvaet vydajet osibku. Pochemu?
Очень интересно! ии сайт крутой жалко что умирает(
У Вас число превысит максимальное число int. Можно использовать в Вашем случае uint, но лучше все переписать на double.
Добавление к программе строки glutReshapeFunc(changeSize); приводит к тому, что треугольник перестаёт совсем отрисовываться.
Выдаёт ошибку glut32.dll не найден! При том, что он лежит в System32! Всё решил) Нужно отправить не в System32, а в System.

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

Рейтинг@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 кб

Комментарии

NTRNO аватар
Опубликовано NTRNO в 14. Ноябрь 2018 - 10:21.

добрый день!
при попытке компиляции выдает

Source.obj : error LNK2001: неразрешенный внешний символ "__imp_glPointSize"
1>Source.obj : error LNK2001: неразрешенный внешний символ "__imp_glPopMatrix"
1>Source.obj : error LNK2001: неразрешенный внешний символ "__imp_glEnableClientState"
1>Source.obj : error LNK2001: неразрешенный внешний символ "__imp_glClear"
1>Source.obj : error LNK2001: неразрешенный внешний символ "__imp_glVertexPointer"
1>Source.obj : error LNK2001: неразрешенный внешний символ "__imp_glEnable"
1>Source.obj : error LNK2001: неразрешенный внешний символ "__imp_glDisableClientState"
1>Source.obj : error LNK2001: неразрешенный внешний символ "__imp_glBlendFunc"
1>Source.obj : error LNK2001: неразрешенный внешний символ "__imp_glDrawArrays"
1>Source.obj : error LNK2001: неразрешенный внешний символ "__imp_glFlush"
1>Source.obj : error LNK2001: неразрешенный внешний символ "__imp_glColorPointer"
1>Source.obj : error LNK2001: неразрешенный внешний символ "__imp_glPushMatrix"
1>Source.obj : error LNK2001: неразрешенный внешний символ "__imp_glOrtho"

Подскажите что не так.