6/13/21

 

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.