CParticleSamplerTexture
! IMPORTANT ! This is a PopcornFX v1 page. PopcornFX v2 documentation can be found here |
---|
- Main page :
Particle samplers
The texture sampler allows you to sample a 2D texture (luminance or RGBA), it is useful to encode particle properties (for example, for particles spawned on a mesh shape).
The sampler is stable/consistent, meaning that if you author a texture with some channels at full intensity or at full zero, even if the texture is in a compressed format, you'll get exactly 1.0 or 0.0 when sampling them back, and not unprecise values like 1.0001, or 0.0001.
Contents
Node properties
|
![]() |
Script bindings
for each texture, the following are published to scripts:
"Sampler" value | published functions |
---|---|
"Regular " or "Both ": |
When 'ScriptOutputType' is set to 'float':
float sample(float2 uv, int filterMode, int addressingMode); When 'ScriptOutputType' is set to 'float4': float4 sample(float2 uv, int filterMode, int addressingMode); |
"Density " or "Both ":
|
float2 sampleDensity(int filterMode, int addressingMode); float2 remapDensity(float2 uv, int filterMode, int addressingMode); |
The filterMode parameter can be any of those two values:
textureFilter.Point
(default)textureFilter.Linear
The addressingMode parameter can be any of those two values:
textureAddr.Clamp
textureAddr.Wrap
(default)
The uv parameter is in standard normalized texture coordinates space:
float2(0,0)
is the top-left texture cornerfloat2(1,0)
is the top-right texture cornerfloat2(1,1)
is the bottom-right texture cornerfloat2(0,1)
is the bottom-left texture cornerfloat2(0.5,0.5)
is the center of the texture.
for example, let's consider a texture sampler named "MyCoolTexture", bound to an RGBA texture.
In order to sample it with bilinear filtering and tiling enabled at UV coordinates {0.3,0.78}, one would write in hh-script:
float2 texcoords = float2(0.3, 0.78); float4 colorRGBA = MyCoolTexture.sample(texcoords, textureFilter.Linear, textureAddr.Wrap);
as textureFilter.Point and textureAddr.Wrap are the default values for the filter and addressing parameters of the 'sample' function, if one were to write:
float4 colorRGBA = MyCoolTexture.sample(texcoords);
it would sample the texture with point filtering (that is, no filtering at all, returning the nearest texel), and wrap address mode (which means texture tiling is enabled). note that:
float4 colorRGBA = MyCoolTexture.sample(texcoords, textureFilter.Linear);
is therefore equivalent to:
float4 colorRGBA = MyCoolTexture.sample(texcoords, textureFilter.Linear, textureAddr.Wrap);
as textureAddr.Wrap is the default value.
Examples
To better show the effects of the filter and wrap parameters, here is a visual example with a flat square particle spawner, that spawns 300K Particles. We'll sample a texture in the spawn script, using the particle x/y position as UV texture coordinates, and set the particle coord to the color sampled in the texture at that location.
![]() |
![]() |
Base particle canvas | Base texture used |
The following images were generated by the following spawn script applied to the base particle canvas, and modifying the filter mode, addressing mode, and UV multipliers and offsets:
function void Eval() { float uvMultiplier = 1.0; float2 uvOffset = float2(0.5, 0.5); float2 uv = Position.xy * uvMultiplier * 0.125 + uvOffset; Size = 0.025; Color = Texture01.sample(uv, textureFilter.Linear, textureAddr.Clamp); }
Addressing modes
![]() |
![]() |
textureAddr.Wrap UV multiplier = 2.0 |
textureAddr.Clamp UV multiplier = 2.0 |
![]() |
![]() |
textureAddr.Wrap UV multiplier = 16.0 |
textureAddr.Clamp UV multiplier = 16.0 |
Filter modes
![]() |
![]() |
textureFilter.Point UV multiplier = 0.04 |
textureFilter.Linear UV multiplier = 0.04 |
Probability Density map
WARNING: Density sampling does not work with overridden attribute samplers.
When density-sampling an attribute-sampler image, the density map of the image originally defined in the .pkfx file will be used.
When setting the "Sampler
" field to either "Density
" or "Both
", you have access to the remapDensity()
and sampleDensity()
(v1.9.0) functions.
When given uniform random float2 texcoords, in the [0,1] range, the remapDensity() function will treat the image as a density function, and will remap the input texcoords to the image density space.
This will produce samples that are more likely to fall where the image is bright. dark areas of the image won't get sampled as much as lighter ones.
For example, the following script :
function void Eval() { Life = 0.5; Size = 0.0065; float2 uv = Image.remapDensity(rand(float2(0), float2(1)), textureFilter.Linear); Position = (uv * float2(1,-1) + float2(-0.5,0.5)).xy0; }
Given a texture sampler named "Image
" with "DensityPower
" set to 2.0
, will produce this:
![]() |
![]() |
Source image | Resulting particle distribution |
sampleDensity()
doesn't expect an UV, and returns a random UV on the texture, properly distributed based on the density function.
It is equivalent to calling remapDensity(rand(float2(0), float2(1)))
, but more efficient (around 3 times faster on most hardware). The previous example script using sampleDensity would look like this.
function void Eval() { Life = 0.5; Size = 0.0065; float2 uv = Image.sampleDensity(textureFilter.Linear); Position = (uv * float2(1,-1) + float2(-0.5,0.5)).xy0; }
Filter modes
![]() |
![]() |
![]() |
Point filtering Samples are taken on the pixel centers |
Linear filtering Samples are distributed uniformly on the pixel's surface |
Image used as the probability density map |
Performance & Optimizations
Images can typically eat-up a _lot_ of memory very quickly when they become large.
Popcorn's image samplers can gracefully handle and sample large images, as long as you've got enough memory.
You should be careful not to use images that are too large, especially on prev-gen consoles or mobile. 64*64 or 128*128 is already a size that should be good enough for most uses.
Smaller images will be good for both memory usage and performance.
There are a few things to consider to keep memory and performance overhead to a minimum when using the image sampler:
Regular sampling
For faster CPU sampling, popcorn only knows how to sample a few image formats.
When the original image is already in one of these known formats, there is no extra memory or performance overhead.
When it is not, popcorn has to convert it to the correct format, allocate a new buffer, and do the conversion (can mean decompressing the image at loading time)
You can remove this step by providing an image that's already in the correct format.
The natively supported formats that require no conversion are:
v1.8.0 and above
Changing the 'SampleGammaSpace' property might also produce extra computations at load time, depending on the gamma-space of the input image.
To ensure there is no extra computations done, you can check the 'SampleRawValues' property, it'll make the sampler return the raw values stored in the texture, without caring about gamma-spaces.
Natively supported formats | ||||||
---|---|---|---|---|---|---|
Format | Channels | Texel Size | Point sampling | Linear sampling | Description | |
BGRA8 | RGBA | 32-bits | YES | YES | 4 8-bits channels note: NOT RGBA8 ! | |
BGRA4 | RGBA | 16-bits | YES | YES | 4 4-bits channels note: NOT RGBA4 ! | |
DXT1 | RGBA | 4-bits | YES | NO | Compressed, 5.6.5 RGB + 1 bit alpha | |
R32F | single-channel | 32-bits | YES | NO | 1 32-bits float channel |
Note that 'BGRA' is also named 'ARGB' in some packages, like NVidia's photoshop DDS exporter
Unlike in previous versions, there is no additional precomputations and overhead for linear sampling.
So load times are faster, and image samplers take far less memory.
Formats not supporting linear sampling fallback to point sampling if you try to sample them with a linear filter.
Total memory used | ||||
---|---|---|---|---|
Resolution | BGRA8 | BGRA4 | DXT1 | R32F |
64*64 | 16 Kb | 8 Kb | 2 Kb | 16 Kb |
128*128 | 65 Kb | 32 Kb | 8 Kb | 65 Kb |
256*256 | 256 Kb | 128 Kb | 32 Kb | 256 Kb |
1024*1024 | 4 Mb | 2 Mb | 512 Kb | 4 Mb |
4096*4096 | 64 Mb (!) | 32 Mb (!) | 8 Mb (!) | 64 Mb (!) |
Regarding performance, BGRA4 is fastest for 4-channels, BGRA8 is only very slightly slower, and DXT1 is roughly twice slower than BGRA8 and BGRA4.
R32F is the fastest overall, from 1.5x to 2x faster than the fastest 4-channel sampler, but... you only have a single channel.
Eliminating load-time overhead
To remove ALL loading-time overhead related to format conversion or precomputations, make sure you do the following:
- Enable 'SampleRawValues'
- Set 'ScriptOutputType' so that it matches your texture's channel-count. Set it to 'float4' for RGBA textures, and to 'float' for R32F
- Make sure the texture you reference is a .dds that has been saved in one of the formats listed in the 'Natively supported formats' table above.
v1.7.0 and below
Natively supported formats | |||||
---|---|---|---|---|---|
Format | Channels | Texel Size | Point sampling | Linear sampling | Description |
BGRA8 | BGRA | 32-bits | YES | YES | 4 8-bits channels note: NOT RGBA8 ! |
R32F | single-channel | 32-bits | YES | NO | 1 32-bits float channel |
Note that 'BGRA' is also named 'ARGB' in some packages, like NVidia's photoshop DDS exporter
Formats not supporting linear sampling fallback to point sampling if you try to sample them with a linear filter.
Then, once the texels are in a known format, if the format is BGRA8, it will pre-swizzle them, and duplicate some of them to remove extra computations.
The extra memory used added by this step will be equal to ImageWidth * ImageHeight * 16
bytes
These pre-computations are used only for linear sampling, but they will be computed at load time even if you don't use linear sampling afterwards.
Taking all this into account, here is the total amount of memory popcorn will end up needing for various image sizes:
(that includes the memory used by the original image, as well as the memory used by the sampler's precomputed tables)
Total memory used | |
---|---|
Resolution | BGRA8 |
64*64 | 80 Kb |
128*128 | 320 Kb |
256*256 | 1.25 Mb |
1024*1024 | 20 Mb (!) |
4096*4096 | 320 Mb (!) |
Changing the 'SampleGammaSpace' property might also produce extra computations at load time, depending on the gamma-space of the input image.
Density sampling
The density map will take at most roughly ImageWidth * ImageHeight * 4
bytes, regardless of the original image format.
That is, for a 512*512 image, 1 Mb of memory will be used.