Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Help  Try to modify the velocity
#1
Code:
void Start()
{
     m_solver.OnSimulationEnd += ColorFromVelocity_OnInterpolate;
}

private void ColorFromVelocity_OnInterpolate(ObiSolver solver, float simulatedTime, float substepTime)
{
     if (solver.backendType == ObiSolver.BackendType.Compute)
     {
         m_computeShader.SetFloat("DeltaTime", Time.deltaTime);
         m_computeShader.SetBuffer(0, "ObiVelocityBuffer", solver.velocities.computeBuffer);
         int threadGroups = ComputeMath.ThreadGroupCount(solver.allocParticleCount, 128);
         m_computeShader.Dispatch(0, Mathf.CeilToInt(threadGroups), 1, 1);
     }
}
Code:
RWStructuredBuffer<float4> ObiVelocityBuffer;
float DeltaTime;

[numthreads(128,1,1)]
void CSMain (uint3 id : SV_DispatchThreadID)
{
    ObiVelocityBuffer[id.x] = float4(0, DeltaTime*10, 0, 0);
}

I followed the instructions on this page and tried applying extra force, but nothing seems to have changed.
Did I do something wrong?
Reply
#2
Hi!

There's quite a few problems with this code:

- Your compute shader does not declare any kernels. As a result, it won't do anything. You must add this line at the top:
Code:
#pragma kernel CSMain

- You're not checking whether the current thread can do any work. As a result your compute shader will access memory outside of the structured buffer and yield undefined behavior, including the chance of your game crashing. You must pass the total amount of particles to your shader and add this code at the beginning of your kernel:

Code:
if(id.x >= particleCount)
        return;

- You're using Time.deltaTime to set the velocity, which is expressed in m/s. As a result, the velocity will randomly change every frame. As usual when dealing with physics, you should only multiply by the time delta if adding an acceleration and in that case you should use the time delta passed to you as an argument to the solver's callback, not the frame's delta time.

- Math.CeilToInt does nothing, as you're already passing an int as argument.

Also note this code only does something if using the Compute backend, it won't work when using the Burst backend for obvious reasons (compute shaders run on the GPU, not the CPU).

kind regards,
Reply
#3
(12-08-2024, 07:44 AM)josemendez Wrote: Hi!

There's quite a few problems with this code:

- Your compute shader does not declare any kernels. As a result, it won't do anything. You must add this line at the top:
Code:
#pragma kernel CSMain

- You're not checking whether the current thread can do any work. As a result your compute shader will access memory outside of the structured buffer and yield undefined behavior, including the chance of your game crashing. You must pass the total amount of particles to your shader and add this code at the beginning of your kernel:

Code:
if(id.x >= particleCount)
        return;

- You're using Time.deltaTime to set the velocity, which is expressed in m/s. As a result, the velocity will randomly change every frame. As usual when dealing with physics, you should only multiply by the time delta if adding an acceleration and in that case you should use the time delta passed to you as an argument to the solver's callback, not the frame's delta time.

- Math.CeilToInt does nothing, as you're already passing an int as argument.

Also note this code only does something if using the Compute backend, it won't work when using the Burst backend for obvious reasons (compute shaders run on the GPU, not the CPU).

kind regards,

Thanks for your quick reply!

I tried another test
1. Setting the velocity to zero in the compute shader
2. Copying data from the buffer to the array at both the beginning and the end of the process

As a result, It seems that the velocity buffer was successfully rewritten, but everything resets in the next tick
Not sure what happened.

I know setting velocity to zero doesn't make sense, just for the test purpose

Code:
#pragma kernel CSMain

RWStructuredBuffer<float4> ObiVelocityBuffer;
int ParticleCount;

[numthreads(128,1,1)]
void CSMain (uint3 id : SV_DispatchThreadID)
{
    if (id.x >= ParticleCount)
        return;
   
    ObiVelocityBuffer[id.x] = float4(0, 0, 0, 0);
}
Code:
using Obi;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class VFXFluidParticle_Obi : MonoBehaviour
{
    public ObiSolver m_solver;
    public ComputeShader m_computeShader;
    public Vector4[] m_veclocityArray;

    void Start()
    {
        m_solver.OnSimulationEnd += ColorFromVelocity_OnInterpolate;
    }

    private void ColorFromVelocity_OnInterpolate(ObiSolver solver, float simulatedTime, float substepTime)
    {

        if(m_veclocityArray == null || m_veclocityArray.Length != solver.positions.computeBuffer.count)
        {
            m_veclocityArray = new Vector4[solver.positions.computeBuffer.count];
        }

        if (solver.backendType == ObiSolver.BackendType.Compute)
        {
            solver.velocities.computeBuffer.GetData(m_veclocityArray);
            Debug.Log(m_veclocityArray[100]);

            m_computeShader.SetInt("ParticleCount", solver.positions.computeBuffer.count);
            m_computeShader.SetBuffer(0, "ObiVelocityBuffer", solver.velocities.computeBuffer);
            int threadGroups = ComputeMath.ThreadGroupCount(solver.allocParticleCount, 128);
            m_computeShader.Dispatch(0, threadGroups, 1, 1);

            solver.velocities.computeBuffer.GetData(m_veclocityArray);
            Debug.Log(m_veclocityArray[100]);
        }
    }
}


Attached Files Thumbnail(s)
   
Reply
#4
Hi,

Just realized you're executing this code at the end of the simulation step (OnSimulationEnd). At the start of the next simulation step, positions and velocities will be replaced with the ones in CPU memory as part of the normal simulation cycle. See:

http://obi.virtualmethodstudio.com/manua...icles.html
Quote:Obi will automatically upload particle data to the GPU at the start of every simulation step (only those data lists that have been modified by the CPU since the last time they were uploaded). However, it will only automatically read positions and velocities back from the GPU at the end of each step for performance reasons. If you want to read any other data from the CPU (eg. colors or user data) you need to manually ask Obi to read this data back from the GPU. Note this is also necessary if you're going to modify this data on the CPU, otherwise you would be working with stale data.

If you want to modify the velocity after taking a simulation step instead of before, I've attached a slightly modified version of ObiSolver.cs that allows you to use solver.OnRequestReadback for this purpose instead. OnRequestReadback also takes place at the end of the simulation step just like OnSimulationEnd, but before requesting an async readback of positions/velocities. This will ensure any modifications made to the GPU copy of the data at the end of each step will be read back to the CPU.

kind regards,


Attached Files
.cs   ObiSolver.cs (Size: 90.17 KB / Downloads: 1)
Reply