Я нашел отличный вычислительный шейдер на Github (благодарность Аллену Чоу https://github.com/TheAllenChou/unity-cj-lib) и пытался изменить его для своего игрового использования. Я пытаюсь заставить позиции экземпляра вычислительного шейдера следовать позиции родительский GameObject, не влияющий на форму / движение вычислительного шейдера.
Два подхода, которые я пробовал:
Передача вычислительному шейдеру переменной от Main.cs
до transform.parent.position
. Затем в строке шейдера particleBuffer[id.x].position += particleBuffer[id.x].linearVelocity * time.y;
добавьте переданную переменную transform.parent.position.
Этот подход действительно работает для перемещения вычислительного шейдера с помощью родительского преобразования GameObject. Но это портит движение шейдера, превращая его из этого
![enter image description here](https://i.stack.imgur.com/zvVJk.gif)
в это
![enter image description here](https://i.stack.imgur.com/UcUXc.gif)
Дальнейшие исследования показали, что мне нужно использовать умножение матриц для правильной работы. Так что я положил это в Main.cs
:
private int m_Id;
public Vector3 scale;
public Matrix4x4 m;
и это в Main.cs OnEnable
и Update
:
scale = transform.parent.localScale;
m = transform.parent.localToWorldMatrix *
Matrix4x4.Scale(new Vector3(1.0f / scale.x, 1.0f / scale.y, 1.0f / scale.z));
m_shader.SetMatrix(m_Id, m);
и это в Main.cs OnEnable
:
m_Id = Shader.PropertyToID("matrixy");
Затем для вычислительного шейдера я поставил:
float4x4 matrixy;
вверху с другими переменными и, наконец, внизу шейдера. Я пробовал много строк, таких как:
particleBuffer[id.x].position = mul(matrixy, particleBuffer[id.x].position);
particleBuffer[id.x].position += particleBuffer[id.x].linearVelocity * time.y;
Ни одна из моих попыток со вторым подходом не заставила позиции экземпляров вычислительного шейдера двигаться в ответ на позиции родительского GameObject.
Если у вас есть какие-либо советы, пожалуйста, дайте мне знать.
Спасибо за вашу помощь.
Вот неизмененный код из Примера "Частицы графического процессора Turbulent Rainbow".
Main.cs
using UnityEngine;
using CjLib;
namespace TurbulentRainbowGpuParticles
{
public class Main : MonoBehaviour
{
public ComputeShader m_shader;
private const int kNumParticles = 10000;
private ComputeBuffer m_computeBuffer;
private ComputeBuffer m_instanceArgsBuffer;
private Mesh m_mesh;
private Material m_material;
private MaterialPropertyBlock m_materialProperties;
private int m_csInitKernelId;
private int m_csStepKernelId;
private int m_csParticleBufferId;
private int m_csScaleId;
private int m_csDampingId;
private int m_csSpeedId;
private int m_csLifetimeId;
private int m_csNumParticlesId;
private int m_csTimeId;
void OnEnable()
{
m_mesh = new Mesh();
m_mesh = PrimitiveMeshFactory.BoxFlatShaded();
int particleStride = sizeof(float) * 24;
m_computeBuffer = new ComputeBuffer(kNumParticles, particleStride);
uint[] instanceArgs = new uint[] { 0, 0, 0, 0, 0 };
m_instanceArgsBuffer = new ComputeBuffer(1, instanceArgs.Length * sizeof(uint), ComputeBufferType.IndirectArguments);
instanceArgs[0] = (uint) m_mesh.GetIndexCount(0);
instanceArgs[1] = (uint) kNumParticles;
instanceArgs[2] = (uint) m_mesh.GetIndexStart(0);
instanceArgs[3] = (uint) m_mesh.GetBaseVertex(0);
m_instanceArgsBuffer.SetData(instanceArgs);
m_csInitKernelId = m_shader.FindKernel("Init");
m_csStepKernelId = m_shader.FindKernel("Step");
m_csParticleBufferId = Shader.PropertyToID("particleBuffer");
m_csScaleId = Shader.PropertyToID("scale");
m_csDampingId = Shader.PropertyToID("damping");
m_csSpeedId = Shader.PropertyToID("speed");
m_csLifetimeId = Shader.PropertyToID("lifetime");
m_csNumParticlesId = Shader.PropertyToID("numParticles");
m_csTimeId = Shader.PropertyToID("time");
m_material = new Material(Shader.Find("CjLib/Example/TurbulentRainbowParticle"));
m_material.enableInstancing = true;
m_material.SetBuffer(m_csParticleBufferId, m_computeBuffer);
m_materialProperties = new MaterialPropertyBlock();
m_shader.SetFloats(m_csScaleId, new float[] { 0.15f, 0.3f });
m_shader.SetFloat(m_csDampingId, 6.0f);
m_shader.SetFloats(m_csSpeedId, new float[] { 3.0f, 4.0f, 1.0f, 6.0f });
m_shader.SetFloats(m_csLifetimeId, new float[] { 0.1f, 0.5f, 0.5f, 0.1f });
m_shader.SetInt(m_csNumParticlesId, kNumParticles);
m_shader.SetBuffer(m_csInitKernelId, m_csParticleBufferId, m_computeBuffer);
m_shader.SetBuffer(m_csStepKernelId, m_csParticleBufferId, m_computeBuffer);
m_shader.Dispatch(m_csInitKernelId, kNumParticles, 1, 1);
}
void Update()
{
m_shader.SetFloats(m_csTimeId, new float[] { Time.time, Time.fixedDeltaTime });
m_shader.Dispatch(m_csStepKernelId, kNumParticles, 1, 1);
Graphics.DrawMeshInstancedIndirect(m_mesh, 0, m_material, new Bounds(Vector3.zero, 20.0f * Vector3.one), m_instanceArgsBuffer, 0, m_materialProperties, UnityEngine.Rendering.ShadowCastingMode.On);
}
void OnDisable()
{
if (m_computeBuffer != null)
{
m_computeBuffer.Dispose();
m_computeBuffer = null;
}
if (m_instanceArgsBuffer != null)
{
m_instanceArgsBuffer.Dispose();
m_instanceArgsBuffer = null;
}
}
}
}
Shader:
#pragma kernel Init
#pragma kernel Step
#include "../../CjLib/Shader/Math/Color.cginc"
#include "../../CjLib/Shader/Math/Math.cginc"
#include "../../CjLib/Shader/Noise/Noise.cginc"
#include "ParticleStruct.cginc"
RWStructuredBuffer<Particle> particleBuffer;
float2 scale; // (min, max)
float damping;
float4 speed; // (min linear, max linear, min angular, max angular)
float4 lifetime; // (head, min body, max body, tail)
int numParticles;
[numthreads(1, 1, 1)]
void Init(uint3 id : SV_DispatchThreadID)
{
float t = float(id.x) / float(numParticles);
float3 seed = id.x;
particleBuffer[id.x].position = float3(0.0, 0.0, 0.0);
float3 rotationAxis = rand_uvec(seed);
seed = rand_vec(seed);
float rotationAngle = rand(seed.x) * kPi;
seed = rand_vec(seed);
particleBuffer[id.x].rotation = quat_axis_angle(rotationAxis, rotationAngle);
particleBuffer[id.x].scale = rand_range(seed.x, scale.x, scale.y);
seed = rand_vec(seed);
particleBuffer[id.x].damping = damping;
float3 linearDirection = normalize(rand_vec(seed));
seed = rand_vec(seed);
float linearSpeed = rand_range(seed.x, speed.x, speed.y);
seed = rand_vec(seed);
particleBuffer[id.x].linearVelocity = linearSpeed * linearDirection;
float3 angularDirection = rand_uvec(seed);
seed = rand_vec(seed);
float angularSpeed = rand_range(seed.x, speed.z, speed.w);
seed = rand_vec(seed);
particleBuffer[id.x].angularVelocity = quat_axis_angle(angularDirection, angularSpeed);
float lifetimeBody = rand_range(seed.x, lifetime.y, lifetime.z);
seed = rand_vec(seed);
float lifetimeCurrent = -t * (lifetime.x + lifetime.z + lifetime.w);
particleBuffer[id.x].lifetime = float4(lifetime.x, lifetimeBody, lifetime.w, lifetimeCurrent);
particleBuffer[id.x].color = float4(hsv2rgb(float3(t, 1.0, 1.0)), 1.0);
}
float2 time; // (current, delta)
[numthreads(1, 1, 1)]
void Step(uint3 id : SV_DispatchThreadID)
{
// respawn particle
float prevLife = particleBuffer[id.x].lifetime.w;
particleBuffer[id.x].lifetime.w += time.y;
float4 lifetime = particleBuffer[id.x].lifetime;
float totalLife = dot(lifetime.xyz, float3(1.0, 1.0, 1.0));
if (lifetime.w > totalLife)
{
// easy way to achieve sub-frame interpolation
lifetime.w -= totalLife;
time.x += lifetime.w;
float3 seed = id.x + time.x;
float3 emitterPos = float3(6.0 * sin(3.0 * time.x), 1.0 * sin(6.0 * time.x), 1.0 * sin(6.0 * time.x));
particleBuffer[id.x].position = emitterPos;
particleBuffer[id.x].lifetime.w = 0.0;
float3 linearDirection = normalize(rand_vec(seed));
seed = rand_vec(seed);
float linearSpeed = rand_range(seed.x, speed.x, speed.y);
seed = rand_vec(seed);
particleBuffer[id.x].linearVelocity = linearSpeed * linearDirection;
float3 angularDirection = rand_uvec(seed);
seed = rand_vec(seed);
float angularSpeed = rand_range(seed.x, speed.z, speed.w);
seed = rand_vec(seed);
particleBuffer[id.x].angularVelocity = quat_axis_angle(angularDirection, angularSpeed);
}
if (lifetime.w < 0.0)
return;
// turbulence
float3 turbulence = snoise_grad(0.4 * particleBuffer[id.x].position, float3(0.0, time.x, 0.0), 2, 1.2).xyz;
particleBuffer[id.x].linearVelocity += 0.3f * turbulence;
// integrate
particleBuffer[id.x].position += particleBuffer[id.x].linearVelocity * time.y;
float4 q = quat_pow(particleBuffer[id.x].angularVelocity, time.y);
particleBuffer[id.x].rotation = quat_concat(q, particleBuffer[id.x].rotation);
// damping
float d = 1.0 - particleBuffer[id.x].damping * time.y;
particleBuffer[id.x].linearVelocity *= d;
}