我在Github上找到了一个很棒的计算着色器(归功于Allen Chou 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变换来移动计算着色器。但是它弄乱了着色器的运动,将其从此变为
进入此
进一步的研究表明,我需要使用矩阵乘法来使这项工作正确。所以我把它放在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的位置而移动。
如果您有任何建议,请告诉我。
谢谢您的帮助。
这是示例“湍流Rainbow GPU粒子”中的不变代码。
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;
}
}
}
}
着色器:
#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;
}