[工作積累] Tricks with UE4 PerInstanceRandom
阿新 • • 發佈:2017-07-15
ppr for ons fun tran code sample mil using
最近在用UE4的Instancing, 發現限制很多。
Unity有instancing的attribute array (uniform/constant buffer),通過InstanceID來訪問每個instance的數據,實現每個實例的不同的參數(通常的一種做法)。
然而Unreal沒有這樣的功能,只有一些instancing的vertex buffer。
shader:
1 #if USE_INSTANCING && !USE_INSTANCING_EMULATED 2 float4 InstanceOrigin : ATTRIBUTE8; // per-instance random in w3 half4 InstanceTransform1 : ATTRIBUTE9; // hitproxy.r + 256 * selected in .w 4 half4 InstanceTransform2 : ATTRIBUTE10; // hitproxy.g in .w 5 half4 InstanceTransform3 : ATTRIBUTE11; // hitproxy.b in .w 6 #endif // USE_INSTANCING && !USE_INSTANCING_EMULATED
C++:
1 template <classFloatType> 2 struct FInstanceStream 3 { 4 FVector4 InstanceOrigin; // per-instance random in w 5 FloatType InstanceTransform1[4]; // hitproxy.r + 256 * selected in .w 6 FloatType InstanceTransform2[4]; // hitproxy.g in .w 7 FloatType InstanceTransform3[4]; // hitproxy.b in .w8 int16 InstanceLightmapAndShadowMapUVBias[4]; 9 ... 10 };
在不改動引擎代碼的前提下(不熟悉,風險大), 總結了以下workaround:
其中第一種是簡單的顏色映射。
後面的幾種是為了解決多個參數的方式,因為只有一個隨機值,導致隨機化不理想。比如一個實例有兩個隨機顏色,用PerInstanceRandom去查兩個顏色表【藍,黃,綠】和【黑,白,灰】那麽第一個顏色是藍色的時候,第二個顏色一定是黑色,而實際需求的很可能是兩個顏色也要隨機搭配。
解決方法是把一個隨機值映射為多個。前兩種比較類似,一個用隨機函數,一個用紋理采樣。最後一種是hack, 取每一位的數字就不一樣。雖然hack,但是目前用起來還算可以。
- Simple senario
- map to color
- create an color LUT table (i.e. using photoshop, filter mode need to set as point/nearest if interpolation is not expected)
- make float2: use PerInstanceRandom as x
- link to TextureSample node‘s uv pin
- map to value
- PerInstanceRandom * (max-min) + min
- PerInstanceRandom * (max-min) + min
- could be done in blueprint
- map to color
- Multiple variation attributes:(i.e. speed/color/move range/angular speed/etc.)
- Simply map PerInstanceRandom to multiple attributes works but may not have expected results
- i.e. instances with 2 different color, using PerInstanceRandom to access 2 color tables, lets say [Blue, Yellow, Cyan] and [Black, White, Grey]
because using the same PerInstanceRandom as UV, when the first color is "Blue", the second "random" color will always be "Black".
while the expected result may need be any combinations of the color in the 2 tables, not fixed combinations. - Solution: remap PerInstanceRandom to multiple random data
- multiple custom random function
- random0 = F0(PerInstanceRandom)
- random1 = F1(PerInstanceRandom);
- ...
- randomn = Fn(PerInstanceRandom);
- get color using random0;
- get speed/color2 using random1;
- ...
- pros: can map PerInstanceRandom to any count of random numbers
- cons: custom math/random function need to be written for each attribute variation
- using random noise texture: similiar as above while using texture sampling
-
- random0 = sample(RandomNoiseTexture0, float2( PerInstanceRandom, 0));
- random1 = sample(RandomNoiseTexture1, float2(PerInstancerandom, 0));
- ...
- pros: easy & simple; blueprint friendly; can map PerInstanceRandom to any count of random number
- cons: one additional random noise texture for each attribute - extra workflow & runtime cost (texture uniform may exceed?)
- simple hack: most significant digit shift
- random0 = PerInstanceRandom;
- random1 = frac(random0*10);
- random2 = rac(random1*10);
- ...
- pros: simple & easy; blueprint friendly
-
- cons: precision lost on each iteration; max attribute variations limited to 5-6, due to floating point precision issue. last random number( i.e. random5 ) have lowest resolution: approximately 10 (probably 0.0~0.9 or even less), but still good to be used as index/uv to sampling color table if color count is small.
- if variation count is small, this method is personally recommended.
note: instance‘s AbsoluteWorldPosition could be used to generate attribute variations.
[工作積累] Tricks with UE4 PerInstanceRandom