**Don't merge now**
**Please review**
Rewritten post effects are here: https…://playcanvas.com/editor/scene/574813
Test scene with most standard features: https://playcanvas.com/editor/scene/561894
Casino with reflections done through a layer: https://playcanvas.com/editor/scene/580749
Originally started as just sorting PR, it almost changed the whole engine.
The main is idea is to split up rendering into **Layers**, that user can easily arrange and assign models/lights/cameras to them. Layers are then rendered in defined order.
Layer is similar to what pc.Scene used to be, but you can have many of them. Layers can be used for 2D games to define different groups of objects and their order (background, enemies, bullets...) or to create various special effects (depth map update, reflection map rendering, GBuffer+post, etc). The idea is to make rendering flexible, so things like that can be done using public API without hacks, while not making it more difficult to do existing things.
**Changes:**
- pc.Layer represents a Layer. It has many properties, most of which are optional:
-- enabled (bool).
-- name (must be unique).
-- opaqueSortMode and transparentSortMode: these define which sorting algorithm to apply to opaque/transparent draw calls. Options are: SORTMODE_NONE (don't do anything), SORTMODE_MANUAL (use meshInstance.drawOrder), SORTMODE_BACK2FRONT (back to front - like for alpha blending), SORTMODE_FRONT2BACK (front to back - saving fillrate).
-- renderTarget: it's now a property of layer, not camera. **However there's still renderTarget on camera, deprecate it?**
-- shaderPass: what kind of shader to use? SHADER_FORWARD, SHADER_DEPTH, SHADER_PICKER, etc... this value is passed down to material.updateShader(), and you can even make up your own pass id and change the standard shader based on it (see onUpdateShader next)
-- simple: not a very descriptive name, but it means this layer is just pass-through meshInstance drawing - used for UI and Gizmo layers. They don't have lights, shadows, culling, etc. **Make a better name?**
-- layerReference: by default each layer has its own list of mesh instances, HOWEVER, you can supply reference to another layer, and then you'll reuse objects from it. Culling results are also shared. Useful primarily for multiple passes with different shader, like depth/forward.
-- cullingMask: just a bit mask that interacts with meshInstance.mask. Especially useful combined with layerReference, allowing to filter some objects, while sharing their list and culling.
-- onPreRender: callback before the layer is rendered (example: check if screen size is changed and re-create render target). Called before the first sublayer in the composition.
-- onDrawCall: callback before a draw call in this layer is performed (example: mess with uniforms).
-- onPostRender: callback after the layer is rendered (example: set some global values back). Called after the last sublayer in the composition.
-- onPreRenderOpaque/onPostRenderOpaque: same, but only for the opaque portion of layer.
-- onPreRenderTransparent/onPostRenderTransparent: same, but only for the transparent portion of layer.
-- onPreCull/onPostCull: callbacks before/after culling this layer. Useful when you're modifying camera transformations and still want to cull the scene properly (e.g. reflections).
-- onEnable: callback when the layer is enabled (example: create screen-size render target).
-- onDisable: callback when the layer is disabled (example: destroy render targets).
-- [PROFILER] skipRenderAfter: debug variable similar to existing pc.skipRenderAfter
-- incrementCounter()/decrementCounter(): each layer has a counter, which defaults to 1. If counter is decremented to 0, the layer is disabled. If counter goes above 0, the layer is enabled. That means calling all enable/disable callbacks. This is useful when multiple effects require a layer (e.g. particle systems wanting a depth map). Each effect would increment the counter, when activated and decrement when destroyed. So, for example, if the last depthmap-dependent particle system is destroyed, engine will no longer render the depth map.
-- addMeshInstances()/removeMeshInstances()/addShadowCasters()/removeShadowCasters(): called by the model component now.
-- addLight()/removeLight(): called by the light component now.
-- addCamera()/removeCamera(): called by the camera component now.
-- clearMeshInstances()/clearLights()/clearCameras().
- pc.LayerComposition defines the order of Layers and caches all common data required for rendering them in such order. For each layer you can separately add its Opaque and Transparent part. This is useful, given you may have many intersecting (with zbuffer) objects from different layers, and you want to render their alphablended parts after. So, for example, the default order is: Depth (opaque), World (opaque), Skybox (opaque), World (transparent), UI (transparent), Gizmos (transparent).
Methods:
-- pushLayer()
-- insertLayerBefore()
-- insertLayerAfter()
-- insertSublayerAt()/removeSublayerAt(): "sublayer" is just a part (one of two: opaque or transparent) of the layer. Previous methods add both at once.
-- getLayerById()/getLayerByName(): search through added layers.
- Model, element, camera and light components now have a _layers_ property. It's an array of layer IDs. Alternatively you can use setLayerNames() method.
- Application creates default layer composition with default layers. They are: depth, world, skybox, UI and gizmos.
- Depth map is now not a separate part of rendering, but a regular layer with shader pass override.
- Immediate rendering is also not a special case now, but a layer.
- [BREAKING] ForwardRenderer doesn't have render() method now, use renderComposition() instead.
- [BREAKING] Scene object now doesn't have many properties it used to have, but it holds _activeLayerComposition_.
- [BREAKING] Drawcall profiling is now per-layer. pc.skipRenderAfter won't work. Use layer._skipRenderCounter.
- [BREAKING] Picker now accepts Camera component instead of pc.Camera.
- [BREAKING] Scene.getModels() is removed.
- Layers have their own drawcall/rendertime stats.
- Picker is changed to use layers.
- Lightmapper is changed to use lower level API, that forward renderer uses, when rendering layers.
- Probably something else
- StandardMaterial now has onUpdateShader() that you can set on any material. It's called right after all shader generator options are calculated and before actually generating the shader. The only argument is the "options" object passed to the generator with all material/scene/light/meshInstance/pass parameters. The intended use is to modify options based on pass (e.g. if rendering a custom normals pass, override endPS chunk with normal output and remove all lights). **Shader generator options are undocumented though**.
- pc.PostEffectPass is the new way to make post effects. Each post effect pass holds a layer without mesh instances, but with onPreRender/onPostRender callbacks, a render target, etc. The idea is that each post effect creates one or many PostEffectPasses and inserts their layers to LayerComposition. That allows precisely controlling post effect order, e.g. not apply it to UI. Parameters:
-- script: user script (that created the pass) with an optional render() function. **Maybe replace it with just a callback.** This function is called before the quad is rendered to set effect uniforms **Maybe call it differently** Function arguments are: device, postEffectPass, screen resolution, input texture, output render target.
-- srcRenderTarget: image we're processing. Will read from backbuffer if not set.
-- destRenderTarget: where the output is written. Will write to backbuffer if not set.
-- shader: the shader that will read srcRenderTarget and write destRenderTarget. Input texture must be called uColorBuffer.
-- hdr: if effect requires HDR color as input.
-- blending: if effect must be blended on top of destRenderTarget. In this case blending parameters can be set up in the render() function by calling device methods.
-- bilinear: if srcRenderTarget must be sampled with linear filtering (nearest otherwise).
-- unmodifiedUvs: optimization hint that tells if this effect doesn't need to distort UVs (e.g. not blur or refraction).
-- addToComposition(order): inserts this post effect pass into the active layer composition by calling insertSublayerAt.
- Certain optimizations and tricks are made for this system, like:
-- reading from backbuffer is handled automatically by redirecting all backbuffer-writing layers to readable render targets.
-- MSAA Just Works on WebGL 2.
-- Multiple passes with unmodifiedUvs == true will be concatenated together to a single big shader! Global variable name collisions are resolved (but uniform collisions prevent it). That should reduce fillrate quite a bit when using many simple effects (e.g. bloom blending + color correction + vignette).
Misc:
- Various rendering optimizations. Skin/Morph is only updated ONCE for every rendered object (wasn't the case with multiple cameras). Same with local light shadowmaps. Euler angles are finally removed from point light shadow rendering. Camera frustum is only calculated once per frame. Per-frame culling and light sorting allocations are removed. Picker has culling.
- hash.js now contains a simple hashing function. This function was used before to produce Standard material ids, but then I needed it in other places, so I moved it to its own file.
- picker, screen depth and shadow map shader generators were moved to Standard shader generator. That saves us from various inconsistencies, like opacity map tiling not matching in different shaders and stuff. That means they also support chunks now.
- scene.updateShaders() function is removed. It is now scene.updateSkybox() (updateShaders used to do that too!) and renderer.updateShaders().
- Depth pass now only renders the scene on WebGL1. On WebGL2 it just does a single blitFramebuffer! Depth sampling code must be different although, due to different encoding. Default particles support both with #ifdef GL2 branching. **Breaks on Macs, but works on Win/iOS/Android. Probably need to report a bug and run a small test on engine init**.
- added camera.aspectRatioMode.
- scene.skyboxModel is now public, and there is "set:skybox" event.
- scene also emits "set:layers" event.