Smoothed-particle Hydrodynamics Using Niagara in Unreal Engine - unreal-engine4

This is going to be quite a long post, sorry, but I think it's worth it because it's quite complicated and I would imagine quite a lot of other people would really like to be able to achieve this effect. There are a few other questions on here about SPH but none of them relate to a Niagara implementation. I've also posted this question on Unreal Engine Answers.
I've been attempting to replicate the fluid simulation in Niagara as shown by Asher Zhu here: The Art of Illusion - Niagara Simulation Framework Overview. Skip to 20:25 for the effect I'm after.
Seeing as he shows none of the Niagara system at all part from some of the bits for rendering it (as far as which I've yet to get), I've followed the article here: link.
Now, I have it looking more or less like a fluid. However, it doesn't really look anything like Asher's. It's rather unstable and will tend to sit for a few seconds with a region of higher density before exploding and then settling down. It also never develops any depth. All the particles, unless they're flying about erratically, sit on the floor. The other problem is collision - I can't see how Asher has managed to get such clean collisions with the environment. My signed distance fields are big, round and uneven and the particles never get anywhere near the walls.
The fourth image below shows it exploding just after it got to the third image and the fifth image is what it looks like after it finally settles down (as well as how far away from the walls the particles end up). The last image shows that it's completely flat (this isn't an issue with the volume of the box; I've tested that).
It's difficult to show everything in the Niagara system on here but the crucial bit is the HLSL code:
OutVelocity = Velocity;
OutPosition = Position;
Density = 0;
float Pressure = 0;
float smoothingRadius = 1.0f;
float restDensity = 0.2f;
float viscosity = 0.018f;
float gas = 500.0f;
const float3 gravity = float3(0, 0, -98);
float pi = 3.141593;
int numParticles;
DirectReads.GetNumParticles(numParticles);
const float Poly6_constant = (315 / (64 * pi * pow(smoothingRadius, 9)));
const float Spiky_constant = (-45 / (pi * pow(smoothingRadius, 6)));
float3 forcePressure = float3(0,0,0);
float3 forceViscosity = float3(0,0,0);
#if GPU_SIMULATION
//Calculate the density of this particle based on the proximity of the other particles.
for (int i = 0; i < numParticles; ++i)
{
bool myBool; //Temporary bool used to catch valid/invalid results for direct reads.
float OtherMass;
DirectReads.GetFloatByIndex<Attribute="Mass">(i, myBool, OtherMass);
float3 OtherPosition;
DirectReads.GetVectorByIndex<Attribute="Position">(i, myBool, OtherPosition);
// Calculate the distance and direction between the target Particle and itself
float distanceBetween = distance(OtherPosition, OutPosition);
if (distanceBetween < smoothingRadius)
{
Density += OtherMass * Poly6_constant * pow(smoothingRadius - distanceBetween, 3);
}
}
//Avoid negative pressure by clamping density to reference value
Density = max(restDensity, Density);
//Calculate pressure
Pressure = gas * (Density - restDensity);
//Calculate the forces.
for (int i = 0; i < numParticles; ++i)
{
if (i != InstanceId) //Only calculate the pressure-based force and Laplacian smoothing function if the other particle is not the current particle.)
{
bool myBool; //Temporary bool used to catch valid/invalid results for direct reads.
float OtherMass;
DirectReads.GetFloatByIndex<Attribute="Mass">(i, myBool, OtherMass);
float OtherDensity;
DirectReads.GetFloatByIndex<Attribute="Density">(i, myBool, OtherDensity);
float3 OtherPosition;
DirectReads.GetVectorByIndex<Attribute="Position">(i, myBool, OtherPosition);
float3 OtherVelocity;
DirectReads.GetVectorByIndex<Attribute="Velocity">(i, myBool, OtherVelocity);
float3 direction = OutPosition - OtherPosition;
float3 normalisedVector = normalize(direction);
float distanceBetween = distance(OtherPosition, OutPosition);
if (distanceBetween > 0 && distanceBetween < smoothingRadius) //distanceBetween must be >0 to avoide a div0 error.
{
float OtherPressure = gas * (OtherDensity - restDensity);
//Calculate particle pressure.
forcePressure += -1 * Mass * normalisedVector * (Pressure + OtherPressure) / (2 * Density * OtherDensity) * Spiky_constant * pow(smoothingRadius - distanceBetween, 2);
//Viscosity-based force computation with Laplacian smoothing function (W).
const float W = -(pow(distanceBetween, 3) / (2 * pow(smoothingRadius, 3))) + (pow(distanceBetween, 2) / pow(smoothingRadius, 2)) + (smoothingRadius / (2 * distanceBetween)) - 1;
forceViscosity += viscosity * (OtherMass / Mass) * (1 / OtherDensity) * (OtherVelocity - Velocity) * W * normalisedVector;
//forceViscosity += viscosity * (OtherMass / Mass) * (1 / OtherDensity) * (OtherVelocity - Velocity) * (45 / (pi * pow(smoothingRadius, 6))) * (smoothingRadius - distanceBetween);
}
}
}
OutVelocity += DeltaTime * ((forcePressure + forceViscosity) / Density);
OutPosition += DeltaTime * OutVelocity;
#endif
This code does two loops through all the other particles in the system, one to calculate the pressure and one to calculate the forces. Then it outputs the velocity and position. Just like the article I linked to above and like some other things I've seen. Yet it simply doesn't behave as shown in those resources.
I haven't applied any grid-based optimisation. To do this I'll just apply the grid optimisation used in the PBD example in UE's Content Examples project. But for now it's an added complication that isn't really needed. It runs fine with a thousands of particles even without it.
I've looked at a few resources (articles, videos and academic research papers) and I've spent a fortnight experimenting, including trial and error on the values at the top of the code. I'm obviously missing something crucial. What can it be? I'm so frustrated now that any help would be much appreciated.

Related

Unity character controller preventing movement stacking with W + A/D keys

I've been trying to make a really basic fps character controller script, but I couldn't solve the movement stacking when I'm moving sideways. I'm sure it's really basic solutin, but as a beginner it's hard for me to solve it.
float forwardSpeed = Input.GetAxis("Vertical") * movementspeed;
float sideSpeed = Input.GetAxis("Horizontal") * movementspeed;
Vector3 VecForwardSpeed = new Vector3(sideSpeed, verticalVelocity, forwardSpeed);
VecForwardSpeed = transform.rotation * VecForwardSpeed;
characterController.Move(VecForwardSpeed * Time.deltaTime);
If I understand you correctly what you mean is if moving forward and sidewards at the same time these inputs/velocities "stack" or sum up allowing the user to move faster then actually allowed.
You could solve this by normalizing them meaning you make sure that combined they never exceed a magnitude value of 1 like e.g.
// Get a vector of the combined input
var combinedInput = new Vector2(Input.GetAxis("Horizontal"), Input.GetAxis("Vertical"));
// Check if the magnitude exceeds 1
// sqrMagnitude is more efficient here and for comparing to 1
// behaves the same as magnitude
if(combinedInput.sqrMagnitude > 1)
{
// If so normalize the input vector to force it again
// to have the maximum length/magnitude of 1
combinedInput.Normalize();
}
// Until then apply the movementspeed here
combinedInput *= movementspeed;
// Now use the components of this combined and evtl normalized input vector instead
var vecForwardSpeed = transform.rotation * new Vector3(combinedInput.x, verticalVelocity, combinedInput.y) * Time.deltaTime;
characterController.Move(vecForwardSpeed);
From your question it is not sure though how the verticalVelocity comes into play.

HLSL: Unitys Vector3.RotateTowards(...)

I need to rotate a direction vector towards another with a maximum angle in a compute shader, just like the Vector3.RotateTowards(from, to, maxAngle, 0) function does. This needs to happen inside the compute shader, since I cannot send the needed values from and to the GPU for performance reasons. Any suggestions on how to implement this?
This is adapted from a combination of this post on the Unity forums by vc1001 and this shadertoy entry by demofox. I haven't tested this and it has been a while since I've done HLSL/cg coding, sop lease let me know if there are bugs--especially syntax errors.
float3 slerp(float3 current, float3 target, float maxAngle)
{
// Dot product - the cosine of the angle between 2 vectors.
float dot = dot(current, target);
// Clamp it to be in the range of Acos()
// This may be unnecessary, but floating point
// precision can be a fickle mistress.
dot = clamp(dot, -1, 1);
// Acos(dot) returns the angle between start and end,
// And multiplying that by percent returns the angle between
// start and the final result.
float delta = acos(dot);
float theta = min(1.0f, maxAngle / delta);
float3 relativeVec = normalize(target - current*dot); // Orthonormal basis
float3 slerped = ((start*cos(theta)) + (relativeVec*sin(theta)));
}

Ball Mechanics - Is this the best approach?

Good day,
I'd like to program a constantly moving ball (object3) being passed between two stationary objects (object1, object2), with the ability to set the max height Y of the pass trajectory dynamically.
What would you argue is the best way to program the ball physics for this concept?
I've looked at using addForce on a default sphere w/ a rigidbody. It seems like there should be an equation that expresses the trajectory of a pass of object3 from object1's x to object2's x... at a known, given speed, with a known, set mass, and a known gravity environment.
However, currently I have a Vector3.Lerp interpolating the ball between the two objects on each FixedUpdate() with t expressed as:
`(Mathf.Sin(speed * Time.time) + 1.0f) / 2.0f;`
It works and all, but with this approach, it seems there's no clear way to add height to the trajectory of the ball path. I've considered adding the height to the Y value in object2 until the ball is half way there, and then setting it back to the original Y position... but it just feels wrong! Thoughts?
Thanks!
Okey so if I understand you correctly currently you are doing
privte void FixedUpdate()
{
var factor = (Mathf.Sin(speed * Time.time) + 1.0f) / 2.0f;
object1.MovePosition(Vector3.Lerp(object2.position, object3.position, factor));
}
which moves the ball pingpong between object1 and object2 position but only planar.
Assuming for now the objects will only be moving within the XZ plane and never have different Y position in order to rather get a curve with height you could treat the separatly:
- Interpolate between both positions as before
- Separately calculate the Y position with sinus or any other mathematical curve function - for realistic physics probably rather a parabola actually
Could look somhow like
public class Example : MonoBehaviour
{
public Rigidbody object1;
public Transform object2;
public Transform object3;
// adjust in the Inspector
public float speed = 1;
public float Amplitude = 0;
// Just for debug
[Range(0, 1)] [SerializeField] private float linearFactor;
[SerializeField] private float yPosition;
private void FixedUpdate()
{
// This always returns a value between 0 and 1
// and linearly pingpongs forth and back
linearFactor = Mathf.PingPong(Time.time * speed, 1);
// * Mathf.PI => gives now a value 0 - PI
// so sinus returns correctly 0 - 1 (no need for +1 and /2 anymore)
// then simply multiply by the desired amplitude
var sinus = Mathf.Sin(linearFactor * Mathf.PI);
yPosition = sinus * Amplitude;
// As before interpolate between the positions
// later we will ignore/replace the Y component
var position = Vector3.Lerp(object2.position, object3.position, linearFactor);
object1.MovePosition(new Vector3(position.x, yPosition, position.z));
}
}
You could optionally also try and add some dumping in the Y direction in order to make the vertical movement more realistic (slow down when reaching the peak). I tried a bit using inverted SmoothStep like
// just for debug
[Range(0, 1)] [SerializeField] private float dampedSinusFactor;
[Range(0, 1)] [SerializeField] private float linearFactor;
[SerializeField] private float yPosition;
private void FixedUpdate()
{
// Use two different factros:
// - a linear one for movement in XZ
// - a smoothed one for movement in Y (in order to slow down when reaching the peak ;) )
linearFactor = Mathf.PingPong(Time.time * speed, 1);
dampedSinusFactor = InvertSmoothStep(linearFactor);
// * Mathf.PI => gives now a value 0 - PI
// so sinus returns correctly 0 - 1 ()
// then simply multiply by the desired amplitude
var sinus = Mathf.Sin(dampedSinusFactor * Mathf.PI);
yPosition = sinus * Amplitude;
// later we will ignore/replace the Y component
var position = Vector3.Lerp(object2.position, object3.position, linearFactor);
object1.position = new Vector3(position.x, yPosition, position.z);
}
// source: https://stackoverflow.com/a/34576808/7111561
private float InvertSmoothStep(float x)
{
return x + (x - (x * x * (3.0f - 2.0f * x)));
}
However for slow movements this looks a bit strange yet. But you can come up with any other maths curve that results in the expected behavior for x=[0,1] ;)

Get orientation device in the iPhone for Opengl Es

I'm trying to convert the geomagnetic and accelerometer to rotate the camera in opengl ES1, I found some code from android and changed this code for iPhone, actually it is working more or less, but there are some mistakes, I´m not able to find this mistake, I put the code, also the call to Opengl Es1: glLoadMatrixf((GLfloat*)matrix);
- (void) GetAccelerometerMatrix:(GLfloat *) matrix headingX: (float)hx headingY:(float)hy headingZ:(float)hz;
{
_geomagnetic[0] = hx * (FILTERINGFACTOR-0.05) + _geomagnetic[0] * (1.0 - FILTERINGFACTOR-0.5)+ _geomagnetic[3] * (0.55);
_geomagnetic[1] = hy * (FILTERINGFACTOR-0.05) + _geomagnetic[1] * (1.0 - FILTERINGFACTOR-0.5)+ _geomagnetic[4] * (0.55);
_geomagnetic[2] = hz * (FILTERINGFACTOR-0.05) + _geomagnetic[2] * (1.0 - FILTERINGFACTOR-0.5)+ _geomagnetic[5] * (0.55);
_geomagnetic[3]=_geomagnetic[0] ;
_geomagnetic[4]=_geomagnetic[1];
_geomagnetic[5]=_geomagnetic[2];
//Clear matrix to be used to rotate from the current referential to one based on the gravity vector
bzero(matrix, sizeof(matrix));
//MAGNETIC
float Ex = -_geomagnetic[1];
float Ey =_geomagnetic[0];
float Ez =_geomagnetic[2];
//ACCELEROMETER
float Ax= -_accelerometer[0];
float Ay= _accelerometer[1] ;
float Az= _accelerometer[2] ;
float Hx = Ey*Az - Ez*Ay;
float Hy= Ez*Ax - Ex*Az;
float Hz = Ex*Ay - Ey*Ax;
float normH = (float)sqrt(Hx*Hx + Hy*Hy + Hz*Hz);
float invH = 1.0f / normH;
Hx *= invH;
Hy *= invH;
Hz *= invH;
float invA = 1.0f / (float)sqrt(Ax*Ax + Ay*Ay + Az*Az);
Ax *= invA;
Ay *= invA;
Az *= invA;
float Mx = Ay*Hz - Az*Hy;
float My = Az*Hx - Ax*Hz;
float Mz = Ax*Hy - Ay*Hx;
// if (mOut.f != null) {
matrix[0] = Hx; matrix[1] = Hy; matrix[2] = Hz; matrix[3] = 0;
matrix[4] = Mx; matrix[5] = My; matrix[6] = Mz; matrix[7] = 0;
matrix[8] = Ax; matrix[9] = Ay; matrix[10] = Az; matrix[11] = 0;
matrix[12] = 0; matrix[13] = 0; matrix[14] = 0; matrix[15] = 1;
}
Thank you very much for the help.
Edit: The iPhone it is permantly in landscape orientation and I know that something is wrong because the object painted in Opengl Es appears two times.
Have you looked at Apple's GLGravity sample code? It does something very similar to what you want here, by manipulating the model view matrix in response to changes in the accelerometer input.
I'm unable to find any problems with the code posted, and would suggest the problem is elsewhere. If it helps, my analysis of the code posted is that:
The first six lines, dealing with _geomagnetic 0–5, effect a very simple low frequency filter, which assumes you call the method at regular intervals. So you end up with a version of the magnetometer vector, hopefully with high frequency jitter removed.
The bzero zeroes the result, ready for accumulation.
The lines down to the declaration and assignment to Hz take the magnetometer and accelerometer vectors and perform the cross product. So H(x, y, z) is now a vector at right angles to both the accelerometer (which is presumed to be 'down') and the magnetometer (which will be forward + some up). Call that the side vector.
The invH and invA stuff, down to the multiplication of Az by invA ensure that the side and accelerometer/down vectors are of unit length.
M(x, y, z) is then created, as the cross product of the side and down vectors (ie, a vector at right angles to both of those). So it gives the front vector.
Finally, the three vectors are used to populate the matrix, taking advantage of the fact that the inverse of an orthonormal 3x3 matrix is its transpose (though that's sort of hidden by the way things are laid out — pay attention to the array indices). You actually set everything in the matrix directly, so the bzero wasn't necessary in pure outcome terms.
glLoadMatrixf is then the correct thing to use because that's how you multiply by an arbitrary column-major matrix in OpenGL ES 1.x.

How to get colliding effect or bouncy when ball hits the track

** STILL NOT WORKING **
I am using below formula to move the ball circular, where accelX and accelY are the values from accelerometer, it is working fine.
But the problem in this code is mRadius (I fixed its value to 50), i need to change mRadius according to accelerometer values and also i need bouncing effect when it touches the track. Currently i am developing code by assuming only one ball is on the board.
float degrees = -atan2(accelX, accelY);
int x = cCentrePoint.x + mRadius * cos(degrees);
int y = cCentrePoint.y + mRadius * sin(degrees);
Here is the snap of the game i want to develop:
Balls Game http://iphront.com/wp-content/uploads/2009/12/bdece528ea334033.jpg.jpg
Updated: I am sending the updated code...
mRadius = 5;
mRange = NSMakeRange(0,60);
-(void) updateBall: (UIAccelerationValue) accelX
withY:(UIAccelerationValue)accelY
{
float degrees = -atan2(accelX, accelY);
int x = cCentrePoint.x + mRadius * cos(degrees);
int y = cCentrePoint.y + mRadius * sin(degrees);
//self.targetRect is rect of ball Object
self.targetRect = CGRectMake(newX, newY, 8, 9);
self.currentRect = self.targetRect;
static NSDate *lastDrawTime;
if(lastDrawTime!=nil)
{
NSTimeInterval secondsSinceLastDraw =
-([lastDrawTime timeIntervalSinceNow]);
ballXVelocity = ballXVelocity + (accelX * secondsSinceLastDraw)
* [self isTouchedTrack:mRadius andRange:mRange];
ballYVelocity = ballYVelocity + -(accelY * secondsSinceLastDraw)
* [self isTouchedTrack:mRadius andRange:mRange];
distXTravelled = distXTravelled + secondsSinceLastDraw
* ballXVelocity * 50;
distYTravelled = distYTravelled + secondsSinceLastDraw
* ballYVelocity * 50;
//Updating the ball rect
CGRect temp = self.targetRect;
temp.origin.x += distXTravelled;
temp.origin.y += distYTravelled;
//calculating new radius after updating ball position
int radius = (temp.origin.x - cCentrePoint.x) /
cos(degreesToRadians(degrees));
if( !NSLocationInRange(abs(radius),mRange))
{
//Colided with the tracks...Need a better logic here
ballXVelocity = -ballXVelocity;
}
else
{
// Need a better logic here
self.targetRect = temp;
}
}
[lastDrawTime release];
lastDrawTime = [ [NSDate alloc] init];
}
In the above code i have initialized mRadius and mRange(indicate track) to some constant for testing, i am not getting the moving of the ball as i expected( bouncing effect when Collided with track ) with respect to accelerometer. Help me to recognize where i went wrong or send some code snippets or links which does the similar job.
I am searching for better logic than my code, if you found share with me.
If I understand your code correctly, then the ball's position is directly controlled by the iPhone's orientation (tilt). So, tilting the iPhone to the right will place the ball at the right side of the track (3 o'clock). I believe you may want the balls acceleration (or, at least, its velocity) to be controlled. Then, you integrate the acceleration to velocity and the velocity to place, taking into account the constraints (the track walls).
The way it is set now, I don't see how you'd control more than one ball (as per the image you posted).
Then, for the bouncing effect: if you mean bouncing by the track's wall, then this will be a small modulation of the mRadius. If you mean bounce by other ball, then you'd modulate the angular position (by means of angular velocity) of the two balls to reflect the reaction.
EDIT: for integration of acceleration to velocity and then to position, for the purpose of this game, you can do with 1st order rectangular integration. Also, it will be more realistic to make the acceleration proportional to the tilt angle. Given the accel values from the iPhone itself, you can assign a 1:1 relation between the balls accel and the device reading. So, you'd like something like:
BallAccX = AccelX * Am_I_NOT_touching_a_wall_in_X_direction() * Ka
BallVelX = BallVelX + BallAccX * dT * Kv
BallPosX = BallPosX + BallVelX * dT * Kp
Note: the above formulae for velocity and position are 1st order approximation but should be sufficient for the purpose of this game.
Ka, Kv, Kp are some proportion coefficients. Choose them to make the relation between the sensed acceleration and the ball movement as you like. dT is the time difference between updates of the state of the ball. The function Am_I_NOT_touching_a_wall_in_X_direction() returns a 1 if the ball is free to move horizontally (in the direction of the tilt) and 0 otherwise.
Calculations for Y movement is symmetrical.
After trying alot I thought it is not easy to produce real time effect without using any physics engine. So its better to use BOX2d or Chipmunks or any other physics engines.