Dx11 particles custom textures


#1

Hi guys,

is is possible to have one dx11 particle system with different textures on the particles? I want to emmit random textures from a folder. all select nodes affect the particles before being fed into the particle system node, so do I have to use multiple particle systems or is there another way?

thanks


#2

Most likely you need a texture array to make it work… also you can do an texture atlas with texture transforms… also drawing shader should be customized.

Can you specify what you want different sprites or color from different textures?


#3

I want to have different sprites. Any idea on how to approach the IDing the points, so that the textures won’t switch when I remove points?


#4

Well you need to add some sort of emitter index, and emmit with that, then they won’t change… if ill have time tommorow ill see if i can fix you something…

The questions is sprite different depending on emitter or thay just randomized?


#5

you probably have some id for the particles. if you don’t know how to get that id look at the helppatch from IndexedInstancer (DX11 geometry). input.ii is the id in that example.

and somewhere in the pixelshader you should find a place where the texture is loaded. this should look similar to this:
float4 c=tex0.SampleLevel(linearSampler,In.TexCd.xy,0);

when you now convert your spread of textures (from a filetexture or so) convert to an array you can rewrite that code to smth. like this:
float4 c=tex0.SampleLevel(linearSampler, float3(In.TexCd.xy, myParticleID % arraySize), 0);

arraySize is the amount of tslices in the texArray

A quick and easy way to convert a spread of textures to an array is this new node called
GetSlice (DX11.TextureArray To Array). it’s already part of the latest dx11 and another PR is pending which fixes some mipmap issues


#6

You have to write a custom emitter that introduces a new attribute to pick the correct texture.
You can find a tutorial for custom emitters in “girlpower/Tutorials/CustomEmitters/CustomEmitters.v4p”

The new attribute is then used in the drawing shader to pick the correct texture (as sebl explained). Just copy the Constant (DX11.Particles.Effect) shader and access your custom attribute there.


#7

@antokhio
I should be able to select textures while emitting

@sebl
The index on your node is not the starting index for the bin size, but selects the texture… that had me confused. maybe some colors in the helppatch would help to visualize this.

@tmp
you mean creating a new compositestruct that also holds the texture for each particle?


#8

yes, you set a textureId in the compositestruct of the emitter and in the end apply a texture depending on this textureId.


#9

I am modifying the sprite shader from the dx11.particles pack.

The texture2dArray of [4] works and I am able to select the textures within the shader code. I added the “int texid” in the composit struct of the emitter, according to other variables such as “float lifespan” or “float age”.

I feed the data of my dynamic buffer (integers between 0 and 3, spread size same as emitter) to the particle:

StructuredBuffer TextureIdBuffer <string uiname=“TextureID Buffer”;>;
[…]
TextureIdBuffer.GetDimensions(size,stride);
p.texid = TextureIdBuffer[emitterCounter % size];

now each emitted particle should have my texid variable within?
In the sprite.fx I define what variables I want to read from the compositestruct:

struct Particle {
#if defined(COMPOSITESTRUCT)
COMPOSITESTRUCT
#else
float3 position;
int texid;
#endif

the vertex shader reads the position from it (p.xyz = ParticleBuffer[particleIndex].position;) so I modify the VS out struct to stream the particleIndex into the PS:

struct VSOut
{
float4 PosWVP : SV_POSITION;
float2 TexCd : TEXCOORD0;
float4 PosW:TEXCOORD1;
float3 Size:TEXCOORD2;
// set streamout for VS particleindex
uint particleIndex : VID;
};

and in the pixelshader I read my texid from the particleBuffer:

uint particleIndex = In.particleIndex;
int textureId= ParticleBuffer[particleIndex].texid;
col *= texture2d.Sample( sL, float3(In.TexCd.xy, textureId), 0);

Something works, the particles are emitted with either two of the 4 textures. But my textureId seems to be mostly 0 or a integer higher than 5…


#10

can you upload a zip file with custom emitter&sprite shader?

you can btw use uint instead of int.


#11

thx, I appreciate your help :) I’m on page ~100 of Paul Varcholik’s book

shader.zip (357.2 KB)


#12

Hi,
How many images do you need on your particles?

As mentioned already there is the method of using texture array and you can use texture atlas.

If you want as much images as your texture memory can hold, the best is to use both methods.

A shader can have multiple texture array inputs and each texture array slice contains a texture atlas.

The lookup from this combination would look like this in the vertex shader (based on id)

int TexPerAtlas = AtlasCountWH.x * AtlasCountWH.y;

uint TexArraySlot;
Out.TexArraySlot = floor(id / (TexArrayStride -1 ) ); //the texture array input to sample from

uint TexArrayIndex;
Out.TexArrayIndex = floor(id / (TexPerAtlas - 1)); //the local index in a texture array
Out.TexCd = mul(In.TexCd.xy, TexScaleAtlas) + float2(id % AtlasCountWH.x/AtlasCountWH.x,floor(id/AtlasCountWH.y)/AtlasCountWH.y ); //the texture coordinates for atlas lookup

In the PS you can then sample the images like this:

if(round(In.TexArraySlot) == 0) c = tex0.Sample(s0,float3(In.TexCd.xy,In.TexArrayIndex));
if(round(In.TexArraySlot) == 1) c = tex1.Sample(s1,float3(In.TexCd.xy,In.TexArrayIndex));
if(round(In.TexArraySlot) == 2) c = tex2.Sample(s2,float3(In.TexCd.xy,In.TexArrayIndex));

… and so on

you can generate the texture atlases beforehand with a simple grid in a renderer temp target and upload them to your multiple texture arrays in a sequential way ( dont try to upload all at once, that can crash the driver)

also always make sure to calculate everything index related in your vertex shader because in pixel shader floating point rounding problems will make problems with that.


#13

ah… the code was correct, just fed data through an int buffer instead of uint. now everything is working :)

@tekcor but is there a performance gain when using bigger textures and UV transforms instead of more smaller textures? Originally I was planning on doing texture sheet animations.


#14

didn’t read that you already managed it… :)
just for completeness… here is my working solution:
TextureArrayExample.zip (331.8 KB)


#15

@schlonzo

it realy depends on how many textures you need to load.

you can only load a limited amount of textures to one texture array slot. How high this limit is depends on your graphics card and mainboard / CPU setup. At one point the texture upload will crash the driver. So if you want more texture you need a new texturearray input (which you are writing to one after each other, not all at once).
At one point you will also run out of texture inputs on your shader, or it get too laggy from it.
So then you need to start using atlased texture arrays…


#16

@tmp I had problems with sebl’s node - sometimes no texture would be displayed, even if “info” shows that there are textures within the array. your approach also works with the texture2dArray within the shader and is stable.

@tekcor I will try your suggestions if I start running into limitations.

thx guys!