Comments (28)
Hi,
It is possible to render 30k+ lights (without shadows). You will have to see if it's efficient enough for what you want. The following Lua script can be loaded in the Editor easily and will put 32400 point lights in the scene, in a grid slightly above ground level:
local scene = GetScene()
for i=1,180 do
for j=1,180 do
local entity = CreateEntity()
local transform = scene.Component_CreateTransform(entity)
local light = scene.Component_CreateLight(entity)
transform.Translate(Vector(i-90, 0.5, j))
light.SetRange(3)
end
end
You can save this script as a text file with .lua extension and use the "Load Script" button in the Editor to open the file and execute the script.
Some gotchas:
- By default, the engine is limiting the number of lights below 30k as optimization for a more usual case. ShaderInterop_Renderer.h has a constant named SHADER_ENTITY_COUNT, which says how many lights, entities can be accessed by shaders at maximum. You can set this value to 33000 for example and it will be able to render up to 33000 lights, but the engine will allocate more memory. You will need to rebuild the engine after this change.
- Editor will have light picking enabled by default, and in this mode a point light image is rendered on screen for every light, so this will result in 30k+ draw calls. Renderer window -> Pick Lights checkbox can disable this.
- You can consider using different Render Paths. The best one for this amount of lights will be either Tiled Deferred or Tiled Forward. The simple Deferred could work too. The simple Forward will be very slow.
- Also, light range will affect performance, so I set it to 3 units. Bigger light range = slower perf.
Thanks for the interest, let me know if you have any problems with this.
from wickedengine.
Thanks for the detailed explanation.
I have tested it and it seems to be too much for the PC/Engine to handle (tested with ~22000 so I dont have to recompile the engine).
I need the lights to be visible using some geometry. Is it possible to use spheres with an emissive pbr material? Can I change the emissive color of the material in realtime?
from wickedengine.
Yes, you can add emissive objects. There is a sample script as well that shows how to load object and set emissive color on a material: scripts/set_material_emissive.lua
If you just want to show lights, there is also a light visualizer thing that can be turned on from the Editor.. but it's not exposed to lua right now.
from wickedengine.
After trying out the script I think an object with emissive material would be a good fit. Now I am wondering how I can create 30k entities in C++ using the same model (a sphere converted to .wiscene in the editor) without loading the model 30k times (like it would when I use LoadModel())
from wickedengine.
In either C++ or LUA script, you can use Scene::Entity_Duplicate(entity). You want to duplicate the entity that has the ObjectComponent, but not the MeshComponents and MaterialComponents. If you do this, all instances will be rendered in 1 draw call.
You can find out the name of the Entity that has the object component by opening in the Editor, selecting the object and the ObjectWindow will display the name and Entity ID. If you have the name, you can use Scene::Entity_FindByName. The Entity ID is not persistent across different runs of the program, so I suggest retrieve entities by name if you load them from file.
from wickedengine.
An other approach - but this will not instance the models - is to LoadModel() into a custom Scene. Two Scenes can be merged with Scene::Merge().
So in this case, you would LoadScene() once, create 30k copies of the Scene that you loaded, then Merge all of them to the main scene.
from wickedengine.
In either C++ or LUA script, you can use Scene::Entity_Duplicate(entity). You want to duplicate the entity that has the ObjectComponent, but not the MeshComponents and MaterialComponents. If you do this, all instances will be rendered in 1 draw call.
You can find out the name of the Entity that has the object component by opening in the Editor, selecting the object and the ObjectWindow will display the name and Entity ID. If you have the name, you can use Scene::Entity_FindByName. The Entity ID is not persistent across different runs of the program, so I suggest retrieve entities by name if you load them from file.
Is it possible to change the emissive color per object with this approach? Or do all share the same material?
from wickedengine.
Right now it's not supported to change emissive color per object. However, you can do it with a small modification to the shaders. In objectHF.hlsli file, you can modify the ApplyEmissive function from this:
inline void ApplyEmissive(in Surface surface, inout Lighting lighting)
{
lighting.direct.specular += surface.emissiveColor.rgb * surface.emissiveColor.a;
}
To this:
inline void ApplyEmissive(in Surface surface, inout Lighting lighting)
{
lighting.direct.specular += surface.emissiveColor.rgb * surface.emissiveColor.a * surface.baseColor.rgb;
}
(You will need to build the shaders again)
This will multiply the material emissive color with object's base color, so you can have white material emissive and change the emissive by changing the object's color (ObjectComponent::color). Also, this is not exposed to LUA yet, but it should be, so I will add this later.
from wickedengine.
Alright, I recompiled the shaders and used Entity_Duplicate to clone the object entity.
Changing the color using the object component works as described.
Now I am stuck at setting the transform of the light points (i.e. the cloned entities). Using the transform component of the object entity does not work. Can you give me some pointers?
from wickedengine.
Hm, that should work.. are you modifying the TransformComponent with the functions provided (eg. Translate()), or by writing to data members? Functions will make the Transform dirty, otherwise you have to make it dirty yourself by calling SetDirty(). This way, UpdateTransform() will refresh the transform matrix. UpdateTransform() is called automatically by the engine once per frame for every dirty transform.
If that doesn't solve your problem, could you show how you are trying to obtain the TransformComponent of the object entity?
from wickedengine.
Here you go:
auto entity = CreateModel(render_scene, archive, XMMatrixIdentity(), true);
for(auto id : scene.lights()) // "scene" is the collection of lights I try to visualize
{
const Eigen::Vector3f pos = (istring::to_eigen(scene.get_position(id)) - center) / scaler;
auto light_entity = render_scene.Entity_Duplicate(entity);
auto transform = render_scene.transforms.GetComponent(light_entity);
transform->ClearTransform();
transform->Scale({ .1f, .1f, .1f });
transform->Translate({ pos.x(), pos.y(), pos.z() });
transform->ApplyTransform();
entities.push_back(light_entity);
}
In CreateModel I create the initial entities:
wiECS::Entity CreateModel(wiSceneSystem::Scene& scene, wiArchive& archive, const XMMATRIX& transformMatrix, bool attached)
{
// Serialize it from file:
scene.Serialize(archive);
// First, create new root:
auto root = wiECS::CreateEntity();
scene.transforms.Create(root);
scene.layers.Create(root).layerMask = ~0;
{
// Apply the optional transformation matrix to the new scene:
// Parent all unparented transforms to new root entity
for (size_t i = 0; i < scene.transforms.GetCount() - 1; ++i) // GetCount() - 1 because the last added was the "root"
{
wiECS::Entity entity = scene.transforms.GetEntity(i);
if (!scene.hierarchy.Contains(entity))
{
scene.Component_Attach(entity, root);
}
}
// The root component is transformed, scene is updated:
scene.transforms.GetComponent(root)->MatrixTransform(transformMatrix);
scene.Update(0);
}
if (!attached)
{
// In this case, we don't care about the root anymore, so delete it. This will simplify overall hierarchy
scene.Component_DetachChildren(root);
scene.Entity_Remove(root);
root = wiECS::INVALID_ENTITY;
}
return scene.objects.GetEntity(0); // There is only one object in the scene I load
}
from wickedengine.
I think the problem is that you are calling ApplyTransform(). What it does is decompose the world matrix into scale, rotation and translation. However, world matrix is not updated immediately after you call Translate(), so it is probably identity matrix at that time. The world matrix is updated when you call UpdateTransform().
TLDR: I think you might want to do UpdateTransform() instead of ApplyTransform() here. UpdateTransform() is also automatically called when the scene is updated every frame, so you might not even need to call it, unless you really want to use the updated world matrix right away.
I'm sorry that this is not well documented at the moment.
from wickedengine.
Same result after removing ApplyTransform() (I also tried with UpdateTransform())
I also tried the alternative approach of using scene.Merge() which worked but it was too slow for larger light scenes
from wickedengine.
Ok, that was a false alarm, it works. Oddly I had to do a full clean/rebuild the project
from wickedengine.
I have set up a floor using a metallic plane but the reflections (using voxel GI) are white (because the emissive color is white). Is there something I can do to get the color I set in the object component to be reflected?
from wickedengine.
For voxel GI, emissive is computed in a different shader. Look into objectPS_voxelizer.hlsl and find this line:
color.rgb += emissiveColor.rgb * emissiveColor.a;
And change it to this:
color.rgb += emissiveColor.rgb * emissiveColor.a * baseColor.rgb;
(You will need to recompile only this shader)
Let me know if you find any other issues.
from wickedengine.
That worked, thanks!
Looking at the results I am afraid that I will need an orthographic camera. Depending on the resolution, view angle and distance, light points are disappearing/popping up creating artifacts:
same scene after maximizing the window (and thus changing the resolution):
The second pictures comes close to what it actually looks like. However there are still artifacts as the real grid is regular but in the visualization the light points are not evenly distributed.
Do you have plans to implement/support orthographic cameras? Do you have any other ideas to reduce the artifacts?
Thanks in advance, your comments are always helpful.
Edit
An extrem example using spheres as light points:
Maximized:
from wickedengine.
Alright, seems like making the camera orthographic is just a matter of using XMMatrixOrthographicLH() in CameraComponent::UpdateCamera() to create the projection matrix. I could add an enum to be able to set the camera type in the camera component and reuse width and height members for the frustum size. I can create a PR for that if u like.
from wickedengine.
So the orthographic camera solved your issue here? It might be a bit more difficult to support orthographic camera properly, just because there are so many systems relying on camera matrix. I am thinking cascaded shadows, light culling, deferred, raytracing, and many more.. It might be challenging to implement and test it for all systems. I'm not against the idea, but if it is to be added to the core engine, all or at least most systems should work with it.
from wickedengine.
Yes it does reduce artifacts quite a bit. For my purposes rendering with the orthopgraphic camera seems fine but I cant say if some render systems are affected.
I did some tests with larger scenes (~23000 light points) and I am hitting a performance bottleneck. Rendering takes about 35 ms to 42 ms which is too slow for realtime visualization. Is there something I can tweak to improve render performance?
from wickedengine.
What kind of RenderPath are you using? You could try an other one and compare performance. Apart from that, you could reduce the resolution scaling to something below 1 for example:
wiRenderer::SetResolutionScale(0.5f);
This would reduce the resolution to half, but upscale it to window resolution at the end of rendering.
Last resort would be actually rewriting lighting shaders to have simpler lighting, and cutting unneeded parts from shaders in general.
from wickedengine.
I've tried some different render paths but it does not really make a difference which one I choose (apart from the path tracer).
Upscaling decreases visual quality too much and does not improve performance significantly.
Maybe its a problem how I call MainComponent::Run(). I am running a fixed interval loop where I first update the lights and then run the main component. Could this be a problem?
from wickedengine.
It sounds like you might have a CPU bottleneck if resolution doesn't affect the framerate significantly. When you update the lights for yourself, are you doing that on a single thread? It might also be the engine's fault internally, I have to say I didn't really test much with that many entities yet.
from wickedengine.
Updating the lights is done in parallel and takes about 10 - 15 ms. However, it does not run concurrently to the MainComponent::Run(), essentially:
while(true)
{
// uses a parallel algorithm
// but sequential in respect to MainComponent::Run()
if (elapsed > 30ms)
UpdateLights();
// this is run every iteration
ProcessWindowMessages();
MainComponent::Run();
}
from wickedengine.
Yeah, your code looks okay. Although the best place to update would be in RenderPath::Update() function, that would be same performance-wise I suspect. This looks like a limitation of engine currently. Need a round of profiling to find slow parts and optimize them. Not an easy task. Do you have this repo you are working on somewhere online?
from wickedengine.
Alright.
Unfortunately not as it is a closed-source project at work.
from wickedengine.
Hi, have you got anywhere with this?
from wickedengine.
Feel free to reopen if you want to continue the discussion.
from wickedengine.
Related Issues (20)
- Incomprehensible work of wi::jobsystem. HOT 5
- UI/UX improvments HOT 5
- Double-clicking an object in Entities menu moves the to it HOT 2
- Terrain uv mapping performs correct? HOT 8
- Engine crashes after trying to upload a video HOT 1
- Export video HOT 2
- Implement snap to view buttons/hotkeys
- How to build template HOT 1
- SDL2 right-click mouse rubber-banding in WickedEngineEditor HOT 1
- Editor Hangs when changing "content" script (on Linux). HOT 11
- dxcompiler May 2024 breaks Linux version on Intel Xe and Radeon HOT 3
- [Feature Request] Remappable Keys HOT 2
- DOF. Incorrect resource allocation HOT 5
- found something, not good HOT 4
- [Linux] make install not creating a working editor install HOT 3
- "editor gui anti aliasing" broke UI rendering on AMD cards HOT 6
- Feature request: entity metadata component HOT 1
- Feature request: scene and general asset packing
- Feature request: scene and general asset packing HOT 7
- [Feature request] Add icon flipping HOT 5
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from wickedengine.