Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

GPU instancing #451

Closed
10 tasks done
RedImp1470 opened this issue Mar 2, 2022 · 1 comment
Closed
10 tasks done

GPU instancing #451

RedImp1470 opened this issue Mar 2, 2022 · 1 comment
Assignees
Labels
enhancement prio-should Should be implemted

Comments

@RedImp1470
Copy link
Member

RedImp1470 commented Mar 2, 2022

Implementation:

  • Add Instancing component
  • Make sure instancing components are handled correctly by the renderer
  • Add general instancing shader shards and Effect (billboard?)
  • Add point cloud instancing shader shards and Effect

Manual Tests:
Scenario: two SceneNodes containing the same Mesh and a InstanceData each.
While rendering:

  • Delete InstanceData - Need to replace the ShaderEffect too.
    Replace InstanceData
    Need to observe the SceneNode / Components? (MeshManager needs to dispose the old one and replace the entry in the _identifierToInstanceDataImpDictionary. See MeshManager.GetImpFromInstanceData)
    see Enhance and test InstanceData on all platforms #560
  • Delete Mesh
    Replace Mesh
    Need to observe the SceneNode / Components? (InstanceDataImp contains a reference to the VAO of the mesh)
    see Enhance and test InstanceData on all platforms #560

Change InstanceData Colors

Change InstanceData Transforms

@RedImp1470 RedImp1470 added this to the Release 0.12 milestone Mar 2, 2022
@RedImp1470 RedImp1470 self-assigned this Mar 2, 2022
@RedImp1470 RedImp1470 added enhancement prio-should Should be implemted labels Mar 2, 2022
@RedImp1470 RedImp1470 changed the title Render point clouds using GPU instancing GPU instancing Mar 3, 2022
@RedImp1470 RedImp1470 modified the milestones: Release 0.12, Release 0.13 May 30, 2022
@RedImp1470
Copy link
Member Author

Test Example:

using Fusee.Base.Core;
using Fusee.Engine.Common;
using Fusee.Engine.Core;
using Fusee.Engine.Core.Effects;
using Fusee.Engine.Core.Primitives;
using Fusee.Engine.Core.Scene;
using Fusee.Engine.Core.ShaderShards;
using Fusee.Engine.Gui;
using Fusee.Math.Core;
using System;
using System.Threading.Tasks;
using static Fusee.Engine.Core.Input;
using static Fusee.Engine.Core.Time;

namespace Fusee.Examples.Simple.Core
{
    [FuseeApplication(Name = "FUSEE Simple Example", Description = "A very simple example.")]
    public class Simple : RenderCanvas
    {
        // angle variables
        private static float _angleHorz, _angleVert, _angleVelHorz, _angleVelVert;

        private const float RotationSpeed = 7;
        private const float Damping = 0.8f;

        private SceneContainer _rocketScene;
        private SceneRendererForward _sceneRenderer;

        private const float ZNear = 1f;
        private const float ZFar = 1000;
        private readonly float _fovy = M.PiOver4;

        private SceneRendererForward _guiRenderer;
        private SceneContainer _gui;
        private SceneInteractionHandler _sih;

        private Transform _camPivotTransform;

        private bool _keys;

        private int instanceAmount = 10000;
        private Random _random = new();

        private InstanceData _instanceComp;
        private InstanceData _instanceComp1;
        private SceneNode _cubeNode;
        private SceneNode _cubeNode1;
        private Plane _plane;
        private Cube _cube = new();

        private float3[] GetRndInstanceTranslations(int amount)
        {
            var instanceTranslations = new float3[amount];
            for (int i = 0; i < amount; i++)
            {
                var x = _random.Next(-2, 2);
                var y = _random.Next(-2, 2);
                var z = _random.Next(-2, 2);

                instanceTranslations[i] = new float3(x, y, z);
            }
            return instanceTranslations;
        }

        public float NextFloat(float min, float max)
        {
            double val = (_random.NextDouble() * (max - min) + min);
            return (float)val;
        }

        private float3[] GetRndInstanceScales(int amount)
        {
            var instanceTranslations = new float3[amount];
            for (int i = 0; i < amount; i++)
            {
                var x = NextFloat(0.1f, 0.1f);
                var y = NextFloat(0.1f, 0.1f);
                var z = NextFloat(0.1f, 0.1f);

                instanceTranslations[i] = new float3(x, y, z);
            }
            return instanceTranslations;
        }

        public float4[] RandomColors(int amount)
        {
            var instanceColors = new float4[amount];
            for (int i = 0; i < amount; i++)
            {
                var res = float4.Zero;
                // Very bad way to generate a random color
                res.r = NextFloat(0, 1);
                res.g = NextFloat(0, 1);
                res.b = NextFloat(0, 1);
                res.a = NextFloat(0.2f, 1f);

                instanceColors[i] = res;
            }

            return instanceColors;
        }

        // Init is called on startup.
        private async Task Load()
        {
            Console.WriteLine("Loading scene ...");

            _gui = await FuseeGuiHelper.CreateDefaultGuiAsync(this, CanvasRenderMode.Screen, "FUSEE Simple Example");

            // Create the interaction handler
            _sih = new SceneInteractionHandler(_gui);

            // Load the rocket model
            _rocketScene = await AssetStorage.GetAsync<SceneContainer>("RocketFus.fus");
            _camPivotTransform = new Transform();
            var camNode = new SceneNode()
            {
                Name = "CamPivoteNode",
                Children = new ChildList()
                {
                    new SceneNode()
                    {
                        Name = "MainCam",
                        Components = new System.Collections.Generic.List<SceneComponent>()
                        {
                            new Transform() { Translation = new float3(0, 2, -10) },
                            new Camera(ProjectionMethod.Perspective, ZNear, ZFar, _fovy) { BackgroundColor = float4.One }
                        }
                    }
                },
                Components = new System.Collections.Generic.List<SceneComponent>()
                {
                    _camPivotTransform
                }
            };

            _instanceComp = new InstanceData(5, GetRndInstanceTranslations(5), null, GetRndInstanceScales(5), RandomColors(5));
            _instanceComp1 = new InstanceData(5, GetRndInstanceTranslations(5), null, GetRndInstanceScales(5), RandomColors(5));

            _plane = new Plane();
            var instanceFx = MakeEffect.FromDiffuseInstanced(new float4(1, 0, 0, 1));
            instanceFx.ColorMode = (int)ColorMode.VertexColor0;

            _cubeNode = new SceneNode()
            {
                Name = "CubeNode",
                Components = new System.Collections.Generic.List<SceneComponent>()
                {
                    new Transform() { Translation = new float3(-5, -0, 0), Scale = float3.One},
                    _instanceComp,
                    instanceFx,
                    _plane
                }
            };

            _cubeNode1 = new SceneNode()
            {
                Name = "CubeNode1",
                Components = new System.Collections.Generic.List<SceneComponent>()
                {
                    new Transform() { Translation = new float3(5, -0, 0), Scale = float3.One},
                    _instanceComp1,
                    MakeEffect.FromDiffuseInstanced(new float4(0,1,0,1)),
                    _plane
                }
            };

            _rocketScene.Children.RemoveAt(0);
            _rocketScene.Children.Add(_cubeNode);
            _rocketScene.Children.Add(_cubeNode1);
            _rocketScene.Children.Add(camNode);

            // Wrap a SceneRenderer around the model.
            _sceneRenderer = new SceneRendererForward(_rocketScene);
            _guiRenderer = new SceneRendererForward(_gui);
        }

        public override async Task InitAsync()
        {
            await Load();
            await base.InitAsync();
        }

        // Init is called on startup.
        public override void Init()
        {

        }

        public override void Update()
        {
            _camPivotTransform.RotationQuaternion = QuaternionF.FromEuler(_angleVert, _angleHorz, 0);

            // Mouse and keyboard movement
            if (Keyboard.LeftRightAxis != 0 || Keyboard.UpDownAxis != 0)
            {
                _keys = true;
            }

            if (Mouse.LeftButton)
            {
                _keys = false;
                _angleVelHorz = RotationSpeed * Mouse.XVel * DeltaTimeUpdate * 0.0005f;
                _angleVelVert = RotationSpeed * Mouse.YVel * DeltaTimeUpdate * 0.0005f;
            }
            else if (Touch != null && Touch.GetTouchActive(TouchPoints.Touchpoint_0))
            {
                _keys = false;
                var touchVel = Touch.GetVelocity(TouchPoints.Touchpoint_0);
                _angleVelHorz = RotationSpeed * touchVel.x * DeltaTimeUpdate * 0.0005f;
                _angleVelVert = RotationSpeed * touchVel.y * DeltaTimeUpdate * 0.0005f;
            }
            else
            {
                if (_keys)
                {
                    _angleVelHorz = RotationSpeed * Keyboard.LeftRightAxis * DeltaTimeUpdate;
                    _angleVelVert = RotationSpeed * Keyboard.UpDownAxis * DeltaTimeUpdate;
                }
                else
                {
                    var curDamp = (float)System.Math.Exp(-Damping * DeltaTimeUpdate);
                    _angleVelHorz *= curDamp;
                    _angleVelVert *= curDamp;
                }
            }

            _angleHorz += _angleVelHorz;
            _angleVert += _angleVelVert;
        }

        private int frameCount = 0;

        // RenderAFrame is called once a frame
        public override void RenderAFrame()
        {
            frameCount++;

            if(frameCount % 10 == 0)
            {
                _instanceComp.Positions = GetRndInstanceTranslations(5);
                _instanceComp.Colors = RandomColors(5);
            }

            if(frameCount == 50)
            {
                _cubeNode.RemoveComponent<InstanceData>();
                _cubeNode.RemoveComponent<SurfaceEffect>();
                _cubeNode.Components.Insert(1,MakeEffect.FromDiffuse(new float4(1, 0, 0, 1)));
                //_instanceComp = new InstanceData(5, new float3[5] { float3.Zero, float3.Zero, float3.Zero, float3.Zero, float3.Zero }, null, null, new float4[5] { new float4(1,0,0,1), new float4(1, 0, 0, 1) , new float4(1, 0, 0, 1) , new float4(1, 0, 0, 1) , new float4(1, 0, 0, 1) });
            }

            _camPivotTransform.RotationQuaternion = QuaternionF.FromEuler(_angleVert, _angleHorz, 0);
            _sceneRenderer.Render(RC);
            _guiRenderer.Render(RC);

            //Constantly check for interactive objects.
            //_guiRenderer.Render(RC);
            //if (!Mouse.Desc.Contains("Android"))
            //    _sih.CheckForInteractiveObjects(RC, Mouse.Position, Width, Height);
            //if (Touch != null && Touch.GetTouchActive(TouchPoints.Touchpoint_0) && !Touch.TwoPoint)
            //{
            //    _sih.CheckForInteractiveObjects(RC, Touch.GetPosition(TouchPoints.Touchpoint_0), Width, Height);
            //}

            // Swap buffers: Show the contents of the backbuffer (containing the currently rendered frame) on the front buffer.
            Present();
        }
    }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement prio-should Should be implemted
Projects
No open projects
Development

No branches or pull requests

1 participant