devNotes 9-4-16 Second Pendulum – Shader Study – Performance Limits

color picker
application button
second pendulum

using UnityEngine;
using System.Collections;
using VRTK;
using UnityEngine.UI;
// Author: Eric Eastwood (ericeastwood.com)
//
// Description:
//		Written for this gd.se question: http://gamedev.stackexchange.com/a/75748/16587
//		Simulates/Emulates pendulum motion in code
// 		Works in any 3D direction and with any force/direciton of gravity
//
// Demonstration: https://i.imgur.com/vOQgFMe.gif
//
// Usage: https://i.imgur.com/BM52dbT.png
public class Pendulum : MonoBehaviour
{

    public GameObject Pivot;
    public GameObject Bob;
    public VRTK_ControllerEvents ctrl_events;
    private Vector2 vTouchAxis = Vector2.zero;
    public Text debugText;

    public float mass = 1f;

    float bob_length = 500f;

    public float dampFactor = 1f;

    Vector3 bobStartingPosition;
    bool bobStartingPositionSet = false;

    // You could define these in the `PendulumUpdate()` loop 
    // But we want them in the class scope so we can draw gizmos `OnDrawGizmos()`

    public Vector3 vGravity;

    public UI_Control uiControl;

    private Vector3 gravityDirection;
    private Vector3 tensionDirection;

    private Vector3 tangentDirection;
    private Vector3 pendulumSideDirection;

    private float tensionForce = 0f;
    private float gravityForce = 0f;


    // Keep track of the current velocity
    Vector3 currentVelocity = new Vector3();

    // We use these to smooth between values in certain framerate situations in the `Update()` loop
    Vector3 currentStatePosition;
    Vector3 previousStatePosition;

    // Use this for initialization
    void Start()
    {
        // Set the starting position for later use in the context menu reset methods
        this.bobStartingPosition = this.Bob.transform.position;
        this.bobStartingPositionSet = true;

        this.PendulumInit();

        ctrl_events.TouchpadAxisChanged += new ControllerInteractionEventHandler(DoTouchpadAxisChanged);
        ctrl_events.TouchpadTouchEnd += new ControllerInteractionEventHandler(DoTouchpadTouchEnd);

    }

    private void DoTouchpadAxisChanged(object sender, ControllerInteractionEventArgs e)
    {
        SetTouchAxis(e.touchpadAxis, e.buttonPressure);
    }

    private void DoTouchpadTouchEnd(object sender, ControllerInteractionEventArgs e)
    {
        SetTouchAxis(Vector2.zero, e.buttonPressure);
    }

    private void SetTouchAxis(Vector2 touchpadAxis, float pressure)
    {
        vTouchAxis = touchpadAxis;
        float presf = pressure;
        debugText.text = "Touch = " + vTouchAxis.ToString("F3") + presf.ToString("F3");
    }

    private void updateBobLength()
    {
        float delt = Time.deltaTime;

        bob_length += vTouchAxis.y * 10.0f * delt;

    }

    float t = 0f;
    float dt = 0.01f;
    float currentTime = 0f;
    float accumulator = 0f;

    void Update()
    {
        vGravity.y = -uiControl.pendulumGravity;
        //bob_length = uiControl.bob_length;

        updateBobLength();
        
        /* */
        // Fixed deltaTime rendering at any speed with smoothing
        // Technique: http://gafferongames.com/game-physics/fix-your-timestep/
        float frameTime = Time.time - currentTime;
        this.currentTime = Time.time;

        this.accumulator += frameTime;

        while (this.accumulator >= this.dt)
        {
            this.previousStatePosition = this.currentStatePosition;
            this.currentStatePosition = this.PendulumUpdate(this.currentStatePosition, this.dt);
            //integrate(state, this.t, this.dt);
            accumulator -= this.dt;
            this.t += this.dt;
        }

        float alpha = this.accumulator / this.dt;

        Vector3 newPosition = this.currentStatePosition * alpha + this.previousStatePosition * (1f - alpha);

        this.Bob.transform.position = newPosition; //this.currentStatePosition;
                                                   /* */

        //this.Bob.transform.position = this.PendulumUpdate(this.Bob.transform.position, Time.deltaTime);
    }


    // Use this to reset forces and go back to the starting position
    [ContextMenu("Reset Pendulum Position")]
    public void ResetPendulumPosition()
    {
        ResetPendulumForces();
        //if (this.bobStartingPositionSet)
        //    this.MoveBob(this.bobStartingPosition);
        //else
            this.PendulumInit();
    }

    // Use this to reset any built up forces
    [ContextMenu("Reset Pendulum Forces")]
    void ResetPendulumForces()
    {
        this.currentVelocity = Vector3.zero;

        // Set the transition state
        this.currentStatePosition = this.Bob.transform.position;
    }

    void PendulumInit()
    {
        // Get the initial rope length from how far away the bob is now
        this.bob_length = Vector3.Distance(Pivot.transform.position, Bob.transform.position);
        this.ResetPendulumForces();
    }

    void MoveBob(Vector3 resetBobPosition)
    {
        // Put the bob back in the place we first saw it at in `Start()`
        this.Bob.transform.position = resetBobPosition;

        // Set the transition state
        this.currentStatePosition = resetBobPosition;
    }


    Vector3 PendulumUpdate(Vector3 currentStatePosition, float deltaTime)
    {
        // Add gravity free fall
        this.gravityForce = this.mass * vGravity.magnitude;
        this.gravityDirection = vGravity.normalized;
        this.currentVelocity += this.gravityDirection * this.gravityForce * deltaTime;

        Vector3 pivot_p = this.Pivot.transform.position;
        Vector3 bob_p = this.currentStatePosition;


        Vector3 auxiliaryMovementDelta = this.currentVelocity * deltaTime;
        float distanceAfterGravity = Vector3.Distance(pivot_p, bob_p + auxiliaryMovementDelta);

        // If at the end of the rope
        if (distanceAfterGravity > this.bob_length || Mathf.Approximately(distanceAfterGravity, this.bob_length))
        {

            this.tensionDirection = (pivot_p - bob_p).normalized;

            this.pendulumSideDirection = (Quaternion.Euler(0f, 90f, 0f) * this.tensionDirection);
            this.pendulumSideDirection.Scale(new Vector3(1f, 0f, 1f));
            this.pendulumSideDirection.Normalize();

            this.tangentDirection = (-1f * Vector3.Cross(this.tensionDirection, this.pendulumSideDirection)).normalized;


            float inclinationAngle = Vector3.Angle(bob_p - pivot_p, this.gravityDirection);

            this.tensionForce = this.mass * Physics.gravity.magnitude * Mathf.Cos(Mathf.Deg2Rad * inclinationAngle);
            float centripetalForce = ((this.mass * Mathf.Pow(this.currentVelocity.magnitude, 2)) / this.bob_length);
            this.tensionForce += centripetalForce;

            this.currentVelocity += this.tensionDirection * this.tensionForce * deltaTime;

            this.currentVelocity = (1f - dampFactor/1000f) * this.currentVelocity;
        }

        // Get the movement delta
        Vector3 movementDelta = Vector3.zero;
        movementDelta += this.currentVelocity * deltaTime;


        //return currentStatePosition + movementDelta;

        float distance = Vector3.Distance(pivot_p, currentStatePosition + movementDelta);
        return this.GetPointOnLine(pivot_p, currentStatePosition + movementDelta, distance <= this.bob_length ? distance : this.bob_length);
    }

    Vector3 GetPointOnLine(Vector3 start, Vector3 end, float distanceFromStart)
    {
        return start + (distanceFromStart * Vector3.Normalize(end - start));
    }

    void OnDrawGizmos()
    {
        // purple
        Gizmos.color = new Color(.5f, 0f, .5f);
        Gizmos.DrawWireSphere(this.Pivot.transform.position, this.bob_length);

        Gizmos.DrawWireCube(this.bobStartingPosition, new Vector3(.5f, .5f, .5f));

        // Blue: Auxilary
        Gizmos.color = new Color(.3f, .3f, 1f); // blue
        Vector3 auxVel = .3f * this.currentVelocity;
        Gizmos.DrawRay(this.Bob.transform.position, auxVel);
        Gizmos.DrawSphere(this.Bob.transform.position + auxVel, .2f);

        // Yellow: Gravity
        Gizmos.color = new Color(1f, 1f, .2f);
        Vector3 gravity = .3f * this.gravityForce * this.gravityDirection;
        Gizmos.DrawRay(this.Bob.transform.position, gravity);
        Gizmos.DrawSphere(this.Bob.transform.position + gravity, .2f);

        // Orange: Tension
        Gizmos.color = new Color(1f, .5f, .2f); // Orange
        Vector3 tension = .3f * this.tensionForce * this.tensionDirection;
        Gizmos.DrawRay(this.Bob.transform.position, tension);
        Gizmos.DrawSphere(this.Bob.transform.position + tension, .2f);

        // Red: Resultant
        Gizmos.color = new Color(1f, .3f, .3f); // red
        Vector3 resultant = gravity + tension;
        Gizmos.DrawRay(this.Bob.transform.position, resultant);
        Gizmos.DrawSphere(this.Bob.transform.position + resultant, .2f);


        /* * /
		// Green: Pendulum side direction
		Gizmos.color = new Color(.3f, 1f, .3f);
		Gizmos.DrawRay(this.Bob.transform.position, 3f*this.pendulumSideDirection);
		Gizmos.DrawSphere(this.Bob.transform.position + 3f*this.pendulumSideDirection, .2f);
		/* */

        /* * /
		// Cyan: tangent direction
		Gizmos.color = new Color(.2f, 1f, 1f); // cyan
		Gizmos.DrawRay(this.Bob.transform.position, 3f*this.tangentDirection);
		Gizmos.DrawSphere(this.Bob.transform.position + 3f*this.tangentDirection, .2f);
		/* */
    }
}

Shader "Argos_Compute/Tetra_Emitter"
{
       Properties
        {
            _ParticleTexture("Diffuse Tex", 2D) = "white" {}
            _Color("Main Color", Color) = (0, 1, 1,0.3)
            _SpeedColor("Speed Color", Color) = (1, 0, 0, 0.3)
           _colorSwitch("Switch", Range(0, 120)) = 60
        }

            SubShader
            {
                Pass
            {
            //Blend SrcAlpha OneMinusSrcAlpha // Traditional transparency
            //Blend One OneMinusSrcAlpha // Premultiplied transparency
            //Blend One One // Additive
            //Blend OneMinusDstColor One // Soft Additive
            //Blend DstColor Zero // Multiplicative
            //Blend DstColor SrcColor // 2x Multiplicative       
            
            
            Tags{ "Queue" = "Transparent" "IgnoreProjector" = "True" "RenderType" = "Transparent" }
            Blend SrcAlpha One
            Cull Off
            Lighting Off
            ZWrite Off
            Fog{ Color(0,0,0,0) }


            CGPROGRAM
#pragma target 5.0
#pragma vertex VSMAIN
#pragma fragment PSMAIN
#pragma geometry GSMAIN
#include "UnityCG.cginc" 

        struct Particle
        {
            //int    index;
            float3 position;
            float3 velocity;
            //float  size;
            //float  age;
            //float  normAge;
            //int    type;

        };

        StructuredBuffer<Particle>  particles;

        // Variables from the properties.
        float4 _Color;
        float4 _SpeedColor;
        float _colorSwitch;

        Texture2D                   _ParticleTexture;
        SamplerState                sampler_ParticleTexture;

        Texture2D                   _Ramp1Texture;
        SamplerState                sampler_Ramp1Texture;

        float3                      _worldPos;
        float                       _particleSize;

        float maxAge;
        float maxRad;

        struct VS_INPUT
        {
            uint vertexid           : SV_VertexID;
        };
        //--------------------------------------------------------------------------------
        struct GS_INPUT
        {
            float4 position         : SV_POSITION;
            float4 color            : COLOR;
            //float size : TEXCOORD0;
            //float age : TEXCOORD1;
            //float type : TEXCOORD2;
        };
        //--------------------------------------------------------------------------------
        struct PS_INPUT
        {
            float4 position         : SV_POSITION;
            float2 texcoords        : TEXCOORD0;
            float4 color            : COLOR;
            //float size : TEXCOORD1;
            //float age : TEXCOORD2;
            //float type : TEXCOORD3;
        };
        //--------------------------------------------------------------------------------
        GS_INPUT VSMAIN(in VS_INPUT input)
        {
            GS_INPUT output;

            output.position.xyz = particles[input.vertexid].position;
            output.position.w = 1.0;
            float speed = length(particles[input.vertexid].velocity);
            float lerpValue = clamp(speed / _colorSwitch, 0, 1);
            output.color = lerp(_Color, _SpeedColor, lerpValue);

            //output.age = particles[input.vertexid].normAge;
            //output.size = particles[input.vertexid].size;
            //output.type = particles[input.vertexid].type;
            return output;
        }


        //    Dimensions
        //    Length of tetrahedron edge : a.
        //    The height of an regular tetrahedron :
        //      h = sqrt(2 / 3) * a;

        //    The radius of circumsphere :
        //          R = sqrt(3 / 8) * a;

        //    The height of the incenter O or
        //    the radius of the insphere :
        //          r = 1 / 3 * R or   r = 1 / sqrt(24) * a;

        //    The radius of midsphere(tangent to edges) :
        //    rm = 1 / sqrt(8) * a;

        //    The angle between two faces : ~70, 53 (yellow)
        //    Angle(C, MAB, D) = degrees(atan(2 * sqrt(2)));

        //    The angle between an edge and a face : ~54, 74 (green)
        //    Angle(0, A, D) = degrees(atan(sqrt(2)));

        //    The angle vertex - center - vertex: ~109.471 (violet)
        //    Angle(A, 0, D) = degrees(acos(-1 / 3));

        //void make_face(float3 a, float3 b, float3 c)
        //{
        //    float3 face_normal = normalize(cross(c - a, c - b));
        //    EmitVertex();

        //    gl_Position = mvpMatrix * vec4(b, 1.0);
        //    color = face_color;
        //    EmitVertex();

        //    gl_Position = mvpMatrix * vec4(c, 1.0);
        //    color = face_color;
        //    EmitVertex();

        //    EndPrimitive();
        //}
        //--------------------------------------------------------------------------------
        [maxvertexcount(12)]
        void GSMAIN(point GS_INPUT p[1], inout TriangleStream<PS_INPUT> triStream)
        {
            float4 pos = p[0].position*2;
            pos.w = 1;
            float4 col = p[0].color;
            //float4 pos = mul(UNITY_MATRIX_MVP, p[0].position);
            float scl = 20;
            
            float4 v[4];

            float3 A, B, C, D;
            float4 a, b, c, d;

            A = _particleSize*float3(0, 0.612, 0);
            B = _particleSize*float3(0.5, -0.204, -0.288);
            C = _particleSize*float3(0, -0.204, 0.578);
            D = _particleSize*float3(-0.5, -0.204, -0.288);

            v[0] = pos + float4(A.x, A.y, A.z, 1);
            v[1] = pos + float4(B.x, B.y, B.z, 1);
            v[2] = pos + float4(C.x, C.y, C.z, 1);
            v[3] = pos + float4(D.x, D.y, D.z, 1);

            PS_INPUT pIn;

            a = mul(UNITY_MATRIX_MVP, v[0]);
            b = mul(UNITY_MATRIX_MVP, v[1]);
            c = mul(UNITY_MATRIX_MVP, v[2]);
            d = mul(UNITY_MATRIX_MVP, v[3]);

            pIn.position = a;
            pIn.texcoords = float2(0.0f, 1.0f);
            pIn.color = col;
            triStream.Append(pIn);

            pIn.position = c;
            pIn.texcoords = float2(1.0f, 1.0f);
            pIn.color = col;
            triStream.Append(pIn);

            pIn.position = b;
            pIn.texcoords = float2(0.0f, 0.0f);
            pIn.color = col;
            triStream.Append(pIn);

            triStream.RestartStrip();

            pIn.position = a;
            pIn.texcoords = float2(0.0f, 1.0f);
            pIn.color = col;
            triStream.Append(pIn);

            pIn.position = b;
            pIn.texcoords = float2(1.0f, 1.0f);
            pIn.color = col;
            triStream.Append(pIn);

            pIn.position = d;
            pIn.texcoords = float2(0.0f, 0.0f);
            pIn.color = col;
            triStream.Append(pIn);

            triStream.RestartStrip();

            pIn.position = a;
            pIn.texcoords = float2(0.0f, 1.0f);
            pIn.color = col;
            triStream.Append(pIn);

            pIn.position = d;
            pIn.texcoords = float2(1.0f, 1.0f);
            pIn.color = col;
            triStream.Append(pIn);

            pIn.position = c;
            pIn.texcoords = float2(0.0f, 0.0f);
            pIn.color = col;
            triStream.Append(pIn);

            triStream.RestartStrip();

            pIn.position = b;
            pIn.texcoords = float2(0.0f, 1.0f);
            pIn.color = col;
            triStream.Append(pIn);

            pIn.position = c;
            pIn.texcoords = float2(1.0f, 1.0f);
            pIn.color = col;
            triStream.Append(pIn);

            pIn.position = d;
            pIn.texcoords = float2(0.0f, 0.0f);
            pIn.color = col;
            triStream.Append(pIn);

            triStream.RestartStrip();
          
            //ref
            //pIn.size = p[0].size;
            //pIn.age = p[0].age;
            //pIn.type = p[0].type;
        }
        //--------------------------------------------------------------------------------
        float4 PSMAIN(in PS_INPUT input) : COLOR
        {
            float4 col = input.color;
            float4 color = _ParticleTexture.Sample(sampler_ParticleTexture, input.texcoords);
            color *= col;
            return color;
        }
            //--------------------------------------------------------------------------------
            ENDCG
        }
    }
}

 

Screenshot_269.0889_

Screenshot_208.6667_

Screenshot_933.9111_