Как стать автором
Обновить

Комментарии 44

А какие аналоги этих типов шейдеров существуют в OpenGL?
Было бы неплохо увидеть туториал именно на OpenGL, а для исходных текстов есть GitHub.

нужна видеокарта с поддержкой DX10

Что-то мне подсказывает, что одной видеокартой с поддержкой DX тут не обойтись.
Работает 61фпс на интегрированном видео в процессоре i7.
ARB_compute_shader, к сожалению, доступен только для OpenGL 4.3.
Но в качестве альтернативы можно использовать «Transform feedback». Он доступен с версии 3.0.
Почему «к сожалению»? На десктопах OpenGL 4.3 сейчас не поддерживает только Intel под линуксом, потому что работает через Mesa.
В OpenGL ES 3 compute shaders тоже есть, хотя вряд ли кто захочет симулировать 1М частиц на мобильном устройстве.

А вот OpenCL как раз Nvidia не поддерживает дальше версии 1.1, что значительно ограничевает возможности.
Сейчас 3 основные десктоп платформы. Винда, Линукс, МакОС.
Под макам версия GL традиционно отстает. ref support.apple.com/en-us/HT202823
Текущий максимум под OSX — это GL 4.1

UPD: если верить табличке www.g-truc.net/post-0702.html#menu — то и 4.1 Apple поддерживает на 83%
Оу, прошу прощения. Не думал что на маках с этим проблемы.
«К сожалению» — потому что ни моя домашняя, ни офисная видеокарты OpenGL 4.3 не поддерживают.
И таких людей довольно много, чтобы сбрасывать их со счетов.
Да, ещё для вычислений вполне можно использовать OpenCL. Там есть возможность использовать контекст и буферы OpenGL.
Спасибо за статью.

Правда проект просто так нельзя взять и запустить, тем кто не работает с SharpDX:
Cannot process file '\GPUParticlesSources\GPUParticles\Content\ParticleSolver.fx': Could not load file or assembly 'SharpDX.D3DCompiler,

Было бы здорово иметь самодостаточный солюшен.

Поставьте с помощью NuGet (возможно сделать прямо в студии, обновите только сам NuGet — если им не пользуетесь) пакет SharpDX, пакеты тут.
Мой косяк, не доложил файлы, обновил исходники в статье, теперь должен быть самодостаточным.
Огромное спасибо. Теперь все работает из коробки.
Извините, но с какого перепугу GeForce 550 Ti вдруг стала бюджетной видеокартой. Начнем с того, что это карта из линейки GTX а не GT, и по умолчанию является игровой. Более того, на момент появления была топовой картой и мечтой каждого игромана. Да, сегодня она по номеру устарела но по прежнему выдает достаточную производительность в играх, так как по сути в новых моделях ничего толкового не появилось.
А в чём противоречие? 550 Ti — самая дешёвая игровая видеокарта из 500-й линейки. Ниже — офисные, выше — средне-бюджетная 560, более дорогие 570, 580-я и топовая 590-я.
По видимому у нас с вами разная шкала оценки, мне всегда казалось что бюджетная и оффисная это одно и тоже. Но это мое личное мнение, спасибо за ответ.
Офисные нынче интеграшки интелловские в процессорах. А бюджетными называют обычно игровые дешевые карты, способные тянуть игры ну хоть как-то приемлимо.
Спасибо за статью)
На GTX770 полет отличный (скачал исходник, сбилдил). Показывает 61 fps, хотя мне кажется циферка врет немного).
Она не врет, FPS ограничен — 60 кадров в секунду, отключите (поставьте false) в конструкторе класса Logic -> SynchronizeWithVerticalRetrace и IsFixedTimeStep, увидите максимальное кол-во FPS. На GTX 770, думаю, будет около 220.
Ну поэкспериментируйте еще и с кол-вом частиц: класс GPUParticlesHandler, константа — PARTICLES_COUNT, достаточно изменить значение — а уж исходя из этого построится нужный буфер на нужное кол-во частиц в этом буфере.
Это все очень неоднозначно, потому что в частицах тормозить начинает уже на филлрейте. Открыли окно на весь экран или подлетели камерой поближе, и минус 50% FPS.
Нужно делать динамический LOD-инг чтобы выжать еще больше попугаев.
Не могу скачать демо — пишет превышен лимит скачивания файлов.
Обновил, залил на хостинг.
Скорее всего такое происходит из-за того, что разрешение окна — 1200x600, а такого полноэкранного режима нет. Я не реализовывал опцию Alt+Enter.
Эхх, жаль :( А вообще — получилась бы отличнейшая заставка :)
В статье вовсю Compute шейдера, но в требованиях упоминается только DirectX 10.
В статью неплохо было бы добавить, что для тех кто хочет миллионы частиц без DirectX 10 и выше — все прекрасно (практически с идентичной производительностью) делается на DX9. Вместо геометрических шейдеров и structured буфера — инстансинг. Вместо Compute шейдеров — рендер в текстуру.

Ну и по поводу шейдеров. Поскольку у нас частицы ориентированы на нас, то data.Position = mul(data.Position, Projection); в геометрическом шейдере не нужен. Так же размер частицы можно посчитать в вертексном, а в геометрическом делать только сдвиг. Оптимизация.

p.s. Знаю, что о опечатках принято в личку, но не удержался:
const float size = 0.1f; // размер конченого квадрата
Вместо геометрических шейдеров и structured буфера — инстансинг.

… что для тех кто хочет миллионы частиц без DirectX 10 и выше — все прекрасно (практически с идентичной производительностью) делается на DX9

Проверьте (касаемо «практические идентичной производительности»)? Сделайте систему на один миллион частиц на DX9 через рендер в текстуру и VFetch (еще и с инстансингом), и потом сравните с CS, GS. Когда уже дадут умереть DX9? :)

Ну и по поводу шейдеров. Поскольку у нас частицы ориентированы на нас, то data.Position = mul(data.Position, Projection); в геометрическом шейдере не нужен.

Попробуйте сделать сдвиг вертекса во ViewSpace и ProjectionSpace, ну как?

Так же размер частицы можно посчитать в вертексном, а в геометрическом делать только сдвиг

Вы думаете в копилированном варианте шейдера есть такое понятие — как функции? В геометрическом не считается размер частиц и делается только сдвиг.
Проверьте (касаемо «практические идентичной производительности»)? Сделайте систему на один миллион частиц на DX9 через рендер в текстуру и VFetch (еще и с инстансингом), и потом сравните с CS, GS. Когда уже дадут умереть DX9? :)
Спасибо, проверял уже в те времена, когда производительность GF550 Ti была чем-то очень крутым: habrahabr.ru/post/151821/
Все свободно крутилось на очень дохлых встроенных видеокартах. А в моем случае частицы еще и создавались динамически, и кол-во частиц в сцене могло скакать очень сильно. CS тогда еще в зачатках не было, а GS только только анонсировали. Вон выше в комментариях уже обсуждали, что DX11 возможности сегодня далеко не везде есть. Если можно обойтись без них — почему бы это не сделать (особенно это касается OpenGL)?

Попробуйте сделать сдвиг вертекса во ViewSpace и ProjectionSpace, ну как?
Легко. Вам шейдер запилить в качестве доказательства? Мы можем такое делать, потому что частица всегда ориентирована на нас и перспективных искажений нет => интерполятор даст идентичные результаты.

Вы думаете в копилированном варианте шейдера есть такое понятие — как функции? В геометрическом не считается размер частиц и делается только сдвиг.
Вы size не считате, потому что вы 4 раза умножаете на матрицу проекции. Я же говорю, что лучше умножать на матрицу проекции в вершинном, там же считать Size и передавать его в геометрический.

upd. Ответ на комментарий выше habrahabr.ru/post/248755/#comment_8243109
С радостью увижу шейдер (а еще лучше скомпилированный экзешник) с инстансингом (quadbillboard) и vfetch миллиона партиклов с FPS на GeForce 550 Ti в 110 (при реализации с FL9.3 у меня вышло 59 FPS).
А так же, сделайте геометрический шейдер с вашей оптимизацией, я посмотрю как это будет работать (скачайте исходник и сделайте).

Потом:

Вон выше в комментариях уже обсуждали, что DX11 возможности сегодня далеко не везде есть. Если можно обойтись без них — почему бы это не сделать (особенно это касается OpenGL)?


Так и пишите на ассемблере, у всех будет работать, в чем проблема? У DX10+ есть режим совместимости с feature level 9.1-9.3. DX10 карта сейчас есть практически у всех, DX9 (и всякие мертвые WinXP) — поддерживать смысла нет.

И да, если вы пишите игры для «офисных» компьютеров — использовать DX смысла вообще нет, используйте OpenGL.
А так же, сделайте геометрический шейдер с вашей оптимизацией, я посмотрю как это будет работать (скачайте исходник и сделайте).
К сожалению вы не залили проект на git, сделал бы пуллреквест… так что шейдер под спойлером:
ParticleRender.fx
struct Particle
{
    float3 Position;
    float3 Velocity;
};

StructuredBuffer<Particle> Particles : register(t0);

Texture2D<float> ParticleTexture : register(t1);
SamplerState ParticleSampler : register(s1);

cbuffer Params : register(b0)
{
	float4x4 ViewProjection;
	float4x4 Projection;
};

struct VertexInput
{
    uint VertexID : SV_VertexID;
};

struct GeometryInput
{
	float4 Position : SV_POSITION;
	float3 PositionTWS : TEXCOORD0;
	float2 Size : SIZE;
};

struct PixelInput
{
	float4 Position : SV_POSITION;
	float2 UV : TEXCOORD0;
	float3 PositionTWS : TEXCOORD1;
};

struct PixelOutput
{
    float4 Color : SV_TARGET0;
};

GeometryInput TriangleVS(VertexInput input)
{
	GeometryInput output = (GeometryInput)0;

	Particle particle = Particles[input.VertexID];

	output.Position = mul(float4(particle.Position, 1), ViewProjection);
	float size = 0.1;
	output.Size = size * float2(Projection[0][0], Projection[1][1]);
	float speedLength = length(particle.Velocity);
	//float magnitude = saturate(length(worldPosition.xyz) * 0.1);

	output.PositionTWS = float3(lerp(float3(0.1, 0.5, 1.0), float3(1.0, 0.5, 0.1), speedLength * 0.1));
 
	return output;
}

void FillQuadVertex(inout PixelInput data, in float2 NewXY, in float2 UV)
{
	data.Position.xy = NewXY;
	data.UV = UV;
}

[maxvertexcount(4)]
void TriangleGS( point GeometryInput input[1], inout TriangleStream<PixelInput> stream )
{
	PixelInput Out;
	Out.Position.zw = input[0].Position.zw;
    Out.PositionTWS = input[0].PositionTWS;

	FillQuadVertex(Out, input[0].Position.xy + float2(-1,-1) * input[0].Size, float2(0, 0)); stream.Append(Out);
	FillQuadVertex(Out, input[0].Position.xy + float2(-1, 1) * input[0].Size, float2(0, 1)); stream.Append(Out);
	FillQuadVertex(Out, input[0].Position.xy + float2( 1,-1) * input[0].Size, float2(1, 0)); stream.Append(Out);
	FillQuadVertex(Out, input[0].Position.xy + float2( 1, 1) * input[0].Size, float2(1, 1)); stream.Append(Out);

	stream.RestartStrip();
}

PixelOutput TrianglePS(PixelInput input)
{
	PixelOutput output = (PixelOutput)0;
	float particle = ParticleTexture.Sample(ParticleSampler, input.UV).x * 0.3;
	float3 speedColor = input.PositionTWS;
	output.Color = float4(speedColor * particle, 1);
	return output;
}

technique ParticleRender
{
	pass DefaultPass
	{
		Profile = 10.0;
		VertexShader = TriangleVS;
		GeometryShader = TriangleGS;
		PixelShader = TrianglePS;
	}
}

Убрал умножение на матрицу проекции в геометрическом шейдере. Поскольку все умножения теперь в вертексном, то делаю одно умножение на ViewProjection. Поэтому не забудьте изменить в GPUParticlesHandler.cs установку матрицы вида на _particlesRender.Parameters[«ViewProjection»].SetValue(camera.ViewProjection);
Но как я и говорил, прирост фпс после данной «оптимизации» сложно заметить, потому что узко на филлрейте.

С радостью увижу шейдер (а еще лучше скомпилированный экзешник) с инстансингом (quadbillboard) и vfetch миллиона партиклов с FPS на GeForce 550 Ti в 110.
Может быть как-нибудь позже, но не обещаю. Времени требует много.

Так и пишите на ассемблере, у всех будет работать, в чем проблема? У DX10+ есть режим совместимости с feature level 9.1-9.3. DX10 карта сейчас есть практически у всех, DX9 (и всякие мертвые WinXP) — поддерживать смысла нет.
Я сейчас говорю не столько о самом DX9, сколько об аппаратных возможностях. Компьют и геометрические шейдера с feature level 9 не взлетят.

И да, если вы пишите игры для «офисных» компьютеров — использовать DX смысла вообще нет, используйте OpenGL.
Вот уж не могу согласится. «Офисные» компьютеры часто с дефолтными драйверами после установки Win, и с OGL там вообще печаль. Вон даже хром «эмулирует» OGL через DirectX.
С радостью увижу шейдер (а еще лучше скомпилированный экзешник) с инстансингом (quadbillboard) и vfetch миллиона партиклов с FPS на GeForce 550 Ti в 110 (при реализации с FL9.3 у меня вышло 59 FPS).

mrdoob.com/lab/javascript/webgl/particles/particles_zz85_2m.html

И т.п. — гугл в помощь. 59 фпс — очень похоже на vsync (в webgl скорее всего тоже около 60 фпс покажет).
Я знаю что такое vsynch (я же ведь не идиот, замерять производительность с включенным vsynch), только… что я должен был увидеть по вышей ссылке? Это обычный point-render с vfetch.
Убрал умножение на матрицу проекции в геометрическом шейдере. Поскольку все умножения теперь в вертексном, то делаю одно умножение на ViewProjection. Поэтому не забудьте изменить в GPUParticlesHandler.cs установку матрицы вида на _particlesRender.Parameters[«ViewProjection»].SetValue(camera.ViewProjection);
Но как я и говорил, прирост фпс после данной «оптимизации» сложно заметить, потому что узко на филлрейте.


10 000 000 частиц, фуллскрин, разрешение 1920x1080, камера на всю сцену:
с «оптимизацией» — FPS: 9, Draw time: прыгает в районе 0.02s с максимальным отклонением 0.0052s.
без «оптимизации» — FPS: 9, Draw time: прыгает в районе 0.02s с максимальным отклонением 0.0049s.

Оптимизация говорите?

Может быть как-нибудь позже, но не обещаю. Времени требует много.

Я же говорю, я делал сначала на Feature Level 9.3, через две текстуры R32, G32, B32 формата и временного буфера с таким же форматом, через RTсчитал частицы, с обычным (!) point-рендером, было 59 fps. В то же время, CS + GS (конечный продукт — quadbillboard) — 110 fps, а вы говорите такой же FPS: проиграл по FPS, качеству, еще и памяти больше нужно.

Я сейчас говорю не столько о самом DX9, сколько об аппаратных возможностях. Компьют и геометрические шейдера с feature level 9 не взлетят.


Так не используйте FL9.x, это же по сути эмулятор фиксированного мамонта. DX10 карта сейчас (кто интересует играми хотя-бы чуть-чуть) есть у всех, а Windows XP как таргет-платформу рассматривать вообще не стоит, через год вы о ней уже забудете.
10 000 000 частиц, фуллскрин, разрешение 1920x1080, камера на всю сцену:
с «оптимизацией» — FPS: 9, Draw time: прыгает в районе 0.02s с максимальным отклонением 0.0052s.
без «оптимизации» — FPS: 9, Draw time: прыгает в районе 0.02s с максимальным отклонением 0.0049s.

Оптимизация говорите?
Я же говорю, узко на филлрейте. Упирается в запись в память. Если оптимзировать запись в память, то 10кк частиц можно выжать с гораздо большим фпс, и эта оптимизация уже будет иметь смысл. А сейчас чтобы заметить эту оптимизацию — камеру отворачивайте, чтобы частицы в кадр не попадали, и отключите резолв.
30 000 000 частиц, отключенный солвер, камера отвернута, FPS 5.4 в обоих случаях, Draw time одинаковые (шумные отклонения). Не могу заметить оптимизацию.
Решил делать аналогичную партикловую систему и сразу же уперся в проблему — как убивать и добавлять частицы? Вроде ясно, что нужно неактивные частицы просто загонять в конец буфера, меняя их с живыми, но как это мапить на многопоточность не понятно… Может у вас уже есть готовый способ?
Сначала гуглить, потом спрашивать :)
http://twvideo01.ubm-us.net/o1/vault/GDC2014/Presentations/Gareth_Thomas_Compute-based_GPU_Particle.pdf
Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Истории