Shader "Custom/Particle_GS" { Properties { _ParticleTexture("Diffuse Tex", 2D) = "white" {} _particleSize("particleSize", Range(0, 1)) = 0.5 [Space(5)] [Header(Blend State)] [Enum(UnityEngine.Rendering.BlendMode)] _SrcBlend("SrcBlend", Float) = 1 //"One" [Enum(UnityEngine.Rendering.BlendMode)] _DstBlend("DestBlend", Float) = 0 //"Zero" [Space(5)] [Header(Other)] [Enum(UnityEngine.Rendering.CullMode)] _Cull("Cull", Float) = 2 //"Back" [Enum(UnityEngine.Rendering.CompareFunction)] _ZTest("ZTest", Float) = 4 //"LessEqual" [Enum(Off,0,On,1)] _ZWrite("ZWrite", Float) = 1.0 //"On" [Enum(UnityEngine.Rendering.ColorWriteMask)] _ColorWriteMask("ColorWriteMask", Float) = 15 //"All" _Alpha("Alpha", float) = 0.5 } SubShader { Pass { Tags{ "Queue" = "Transparent" "IgnoreProjector" = "True" "RenderType" = "Transparent" } //Blend SrcAlpha One Blend[_SrcBlend][_DstBlend] ZTest[_ZTest] ZWrite[_ZWrite] Cull[_Cull] ColorMask[_ColorWriteMask] CGPROGRAM // Physically based Standard lighting model, and enable shadows on all light types #pragma vertex vert #pragma geometry GSMAIN #pragma fragment frag #include "UnityCG.cginc" // Use shader model 3.0 target, to get nicer looking lighting #pragma target 5.0 struct Particle{ float3 position; float3 velocity; float3 targetPos; }; struct PS_INPUT{ float4 position : SV_POSITION; float2 texcoords: TEXCOORD0; //float4 color : COLOR; }; // particles' data StructuredBuffer<Particle> particleBuffer; Texture2D _ParticleTexture; SamplerState sampler_ParticleTexture; float _particleSize; float _Alpha; float _maxDist_Glow_Particle; PS_INPUT vert(uint vertex_id : SV_VertexID, uint instance_id : SV_InstanceID) { PS_INPUT o = (PS_INPUT)0; // Color //float life = particleBuffer[instance_id].life; //float lerpVal = life * 0.25f; //o.color = fixed4(1.0f - lerpVal+0.1, lerpVal+0.1, 1.0f, lerpVal); // Position //o.position = UnityObjectToClipPos(float4(particleBuffer[instance_id].position, 1.0f)); o.position = float4(particleBuffer[instance_id].position,0); return o; } float4 RotPoint(float4 p, float3 offset, float3 sideVector, float3 upVector) { float3 finalPos = p.xyz; finalPos += offset.x * sideVector; finalPos += offset.y * upVector; return float4(finalPos, 1); } [maxvertexcount(4)] void GSMAIN(point PS_INPUT p[1], inout TriangleStream<PS_INPUT> triStream) { PS_INPUT pIn; float2 halfS = _particleSize; float4 pos = p[0].position;//TODO FIND OUT WHY *2 pos.w = 1; //float4 col = p[0].color; float4 v[4]; v[0] = 0; v[1] = 0; v[2] = 0; v[3] = 0; if(length(ObjSpaceViewDir(pos))<_maxDist_Glow_Particle) { float3 up = normalize(float3(0, 1, 0)); float3 look = _WorldSpaceCameraPos - p[0].position.xyz; //if (_StaticCylinderSpherical == 1) // look.y = 0; look = normalize(look); float3 right = normalize(cross(look, up)); up = normalize(cross(right, look)); float wid = halfS.x; float len = halfS.y; v[0] = RotPoint(p[0].position, float3(-wid, -len, 0), right, up); v[1] = RotPoint(p[0].position, float3(-wid, len, 0), right, up); v[2] = RotPoint(p[0].position, float3(wid, -len, 0), right, up); v[3] = RotPoint(p[0].position, float3(wid, len, 0), right, up); } pIn.position = mul(UNITY_MATRIX_VP, v[0]); pIn.texcoords = float2(0.0f, 0.0f); //pIn.color = col; triStream.Append(pIn); pIn.position = mul(UNITY_MATRIX_VP, v[1]); pIn.texcoords = float2(0.0f, 1.0f); //pIn.color = col; triStream.Append(pIn); pIn.position = mul(UNITY_MATRIX_VP, v[2]); pIn.texcoords = float2(1.0f, 0.0f); //pIn.color = col; triStream.Append(pIn); pIn.position = mul(UNITY_MATRIX_VP, v[3]); pIn.texcoords = float2(1.0f, 1.0f); //pIn.color = col; triStream.Append(pIn); } float4 frag(PS_INPUT i) : COLOR { //float4 col = i.color; float4 color = _ParticleTexture.Sample(sampler_ParticleTexture, i.texcoords); color.a *= _Alpha; //color *= col; return color; } ENDCG } } FallBack Off }
using System.Collections; using System.Collections.Generic; using UnityEngine; public class Mesh_Run_Compute : MonoBehaviour { private Mesh emit_Attract_Mesh; public GameObject emit_Attract_Surface; private Vector3[] verts; // struct struct Particle { public Vector3 position; public Vector3 velocity; public Vector3 target_pos; } [Range(0f, 10f)] public float atten = 1f; public bool bRunning = false; [Range(0f, 1f)] public float particleSize = 1f; [Range(0f, 1f)] public float initVelMod = 1f; [Range(0f, 3f)] public float maxDist_Glow_Particle = 1f; [Range(0f, 5f)] public float damping = 1f; public Transform finger_T; public Transform finger_T2; /// <summary> /// Size in octet of the Particle struct. /// since float = 4 bytes... /// 4 floats = 16 bytes /// </summary> //private const int SIZE_PARTICLE = 24; private const int SIZE_PARTICLE = 36; // since property "life" is added... /// <summary> /// Number of Particle created in the system. /// </summary> public int particleCount = 25000; /// <summary> /// Material used to draw the Particle on screen. /// </summary> public Material material; /// <summary> /// Compute shader used to update the Particles. /// </summary> public ComputeShader computeShader; /// <summary> /// Id of the kernel used. /// </summary> private int mComputeShaderKernelID; /// <summary> /// Buffer holding the Particles. /// </summary> ComputeBuffer particleBuffer; /// <summary> /// Number of particle per warp. /// </summary> private const int WARP_SIZE = 256; // TODO? /// <summary> /// Number of warp needed. /// </summary> private int mWarpCount; // TODO? private float eps = 0.000001f; //public ComputeShader shader; // Use this for initialization void Start() { InitComputeShader(); } void InitComputeShader() { mWarpCount = Mathf.CeilToInt((float)particleCount / WARP_SIZE); Transform transMesh = emit_Attract_Surface.transform; Vector3 vMeshPos = transMesh.position; emit_Attract_Mesh = emit_Attract_Surface.GetComponent<MeshFilter>().sharedMesh; verts = emit_Attract_Mesh.vertices; int walkVerts = 0; // initialize the particles Particle[] particleArray = new Particle[particleCount]; for (int i = 0; i < particleCount; i++) { Vector3 xyz; xyz = verts[walkVerts]; if (++walkVerts > verts.Length - 1) walkVerts = 0; particleArray[i].position.x = xyz.x + Random.value * eps; particleArray[i].position.y = xyz.y + Random.value * eps; particleArray[i].position.z = xyz.z + Random.value * eps; particleArray[i].target_pos.x = xyz.x; particleArray[i].target_pos.y = xyz.y; particleArray[i].target_pos.z = xyz.z; particleArray[i].velocity.x = Random.value * initVelMod; particleArray[i].velocity.y = Random.value * initVelMod; particleArray[i].velocity.z = Random.value * initVelMod; } // create compute buffer particleBuffer = new ComputeBuffer(particleCount, SIZE_PARTICLE); particleBuffer.SetData(particleArray); // find the id of the kernel mComputeShaderKernelID = computeShader.FindKernel("CSParticle"); // bind the compute buffer to the shader and the compute shader computeShader.SetBuffer(mComputeShaderKernelID, "particleBuffer", particleBuffer); material.SetBuffer("particleBuffer", particleBuffer); } public void On_Toggle_Run() { bRunning = !bRunning; } void OnRenderObject() { material.SetPass(0); material.SetFloat("_particleSize", particleSize); material.SetFloat("_maxDist_Glow_Particle", maxDist_Glow_Particle); Graphics.DrawProceduralNow(MeshTopology.Points, 1, particleCount); } void OnDestroy() { if (particleBuffer != null) particleBuffer.Release(); } // Update is called once per frame void Update() { if (bRunning) { Vector3 targ = finger_T.position; float[] targPosFloats = { targ.x, targ.y, targ.z }; Vector3 tipAxis = finger_T.up; float[] tipAxisFloats = { tipAxis.x, tipAxis.y, tipAxis.z }; // Send datas to the compute shader computeShader.SetFloat("deltaTime", Time.deltaTime); computeShader.SetFloats("targetPosition", targPosFloats); targ = finger_T2.position; float[] targPosFloats2 = { targ.x, targ.y, targ.z }; computeShader.SetFloats("targetPos_2", targPosFloats2); computeShader.SetFloat("atten", atten); computeShader.SetFloats("wandAxis", tipAxisFloats); computeShader.SetFloats("damping", damping); // Update the Particles computeShader.Dispatch(mComputeShaderKernelID, mWarpCount, 1, 1); } } }
#pragma kernel CSParticle // Particle's data struct Particle { float3 position; float3 velocity; float3 target_pos; }; // Particle's data, shared with the shader RWStructuredBuffer<Particle> particleBuffer; // Variables set from the CPU float deltaTime; float3 targetPosition; float atten; float3 wandAxis; float damping; //VU Variables float3 pPos; float3 newVel; float3 vRadius; float3 att_Dir; float3 att_Perp; float3 att_A; float3 att_B; float3 att_A_Dir; float3 att_B_Dir; float att_Dist; float att_A_Dist; float att_B_Dist; float3 nPerp; float3 velDeref; float coeff; float nrand(float2 uv) { return frac(sin(dot(uv, float2(12.9898, 78.233))) * 43758.5453); } uint rng_state; uint rand_xorshift() { // Xorshift algorithm from George Marsaglia's paper rng_state ^= (rng_state << 13); rng_state ^= (rng_state >> 17); rng_state ^= (rng_state << 5); return rng_state; } [numthreads(256, 1, 1)] void CSParticle(uint3 id : SV_DispatchThreadID) { pPos = particleBuffer[id.x].position; velDeref = particleBuffer[id.x].velocity; //ADD the life based on deltaTime //particleBuffer[id.x].life += deltaTime; targetPosition = particleBuffer[id.x].target_pos; vRadius = pPos - targetPosition; att_Dist = length(vRadius); att_Dir = -normalize(vRadius); nPerp = normalize(cross(att_Dir,wandAxis)); att_A = targetPosition + 0.1*nPerp; att_B = targetPosition - 0.1*nPerp; att_A_Dir = normalize(att_A - pPos); att_B_Dir = normalize(att_B - pPos); coeff = atten * deltaTime /att_Dist; //(att_Dist * att_Dist); velDeref += coeff * att_A_Dir; velDeref += coeff * att_B_Dir; newVel = velDeref; if (damping > 0) { newVel = velDeref*pow(abs(damping), deltaTime); } particleBuffer[id.x].position = pPos + velDeref * deltaTime;; particleBuffer[id.x].velocity = newVel; if (false) { // http://www.reedbeta.com/blog/quick-and-easy-gpu-random-numbers-in-d3d11/ rng_state = id.x; float f0 = float(rand_xorshift()) * (1.0 / 4294967296.0) - 0.5; float f1 = float(rand_xorshift()) * (1.0 / 4294967296.0) - 0.5; float f2 = float(rand_xorshift()) * (1.0 / 4294967296.0) - 0.5; float3 normalF3 = normalize(float3(f0, f1, f2)) * 0.8f; normalF3 *= float(rand_xorshift()) * (1.0 / 4294967296.0); particleBuffer[id.x].position = float3(normalF3.x + targetPosition.x, normalF3.y + targetPosition.y, normalF3.z + targetPosition.z); // reset the life of this particle //particleBuffer[id.x].life = 0; particleBuffer[id.x].velocity = float3(0, 0,0); } }
https://vimeo.com/yumayanagisawa
Flow from Yuma Yanagisawa on Vimeo.