GPU GUI
Orillusion
provides high-performance GUI components for developers to use.
By using GUI components reasonably, you can flexibly display 2D/3D GUI content in the project. In this chapter, we first understand some basic concepts of GUI:
GUI Space Mode
Currently, GUI
supports two rendering modes ViewSpace
and WorldSpace
:
- ViewSpace mode: In this mode, the GUI component is rendered in screen space, and it will not change with the camera's perspective, nor will it have a 3D space occlusion relationship with other objects;
- WorldSpace mode: In this mode, the GUI component can be regarded as a canvas in three-dimensional space, with 3D properties (rotation, scaling, translation), can participate in depth detection, etc., to achieve occlusion and occlusion relationship with other objects.
import { ViewPanel, WorldPanel } from '@orillusion/core'
// Create a panel object
let panelRoot: Object3D = new Object3D()
// Add ViewPanel and set it to ViewSpace mode
panelRoot.addComponent(ViewPanel)
// Or add WorldPanel and set it to WorldSpace mode
panelRoot.addComponent(WorldPanel)
This following example shows the difference between ViewPanel
and WorldPanel
:
import { Engine3D, Scene3D, Object3D, Camera3D, View3D, ViewPanel, TextAnchor, UITextField, HoverCameraController, AtmosphericComponent, BitmapTexture2D, UIImage, makeAloneSprite, WorldPanel, GPUCullMode, UIPanel } from '@orillusion/core';
import * as dat from 'dat.gui';
// initializa engine
await Engine3D.init();
// create new scene as root node
let scene3D: Scene3D = new Scene3D();
scene3D.addComponent(AtmosphericComponent);
// create camera
let cameraObj: Object3D = new Object3D();
let camera = cameraObj.addComponent(Camera3D);
// adjust camera view
camera.perspective(60, Engine3D.aspect, 1, 5000.0);
// set camera controller
let controller = cameraObj.addComponent(HoverCameraController);
controller.setCamera(0, -20, 100);
// add camera node
scene3D.addChild(cameraObj);
let view = new View3D();
view.scene = scene3D;
view.camera = camera;
Engine3D.startRenderView(view);
// create a UICanvas
let canvas = view.enableUICanvas();
// create view sapce panel
let viewPanel: Object3D = new Object3D();
viewPanel.addComponent(ViewPanel);
// add to UICanvas
canvas.addChild(viewPanel);
// create world sapce panel
let worldPanel: Object3D = new Object3D();
worldPanel.localScale.set(0.15, 0.15, 0.15);
let panel: UIPanel = worldPanel.addComponent(WorldPanel);
// render double side
panel.cullMode = GPUCullMode.none;
// add to UICanvas
canvas.addChild(worldPanel);
// load a BitmapTexture2D
let bitmapTexture2D = new BitmapTexture2D();
bitmapTexture2D.flipY = true;
await bitmapTexture2D.load('https://cdn.orillusion.com/images/webgpu.png');
// create image node
let imageQuad = new Object3D();
viewPanel.addChild(imageQuad);
// create UIImage component
let image: UIImage = imageQuad.addComponent(UIImage);
// set image size
image.uiTransform.resize(320, 320);
// set image source
image.sprite = makeAloneSprite('webgpu', bitmapTexture2D);
let GUIHelp = new dat.GUI();
let f = GUIHelp.addFolder('GUI Space');
let params = {
ViewSpace: () => {
viewPanel.addChild(imageQuad);
},
WorldSpace: () => {
worldPanel.addChild(imageQuad);
}
};
f.add(params, 'ViewSpace');
f.add(params, 'WorldSpace');
f.open();
UICanvas
GUI components also need canvas for drawing. Each View3D
in the engine has a built-in array of Canvas
. We can activate the UICanvas
object by specifying enableUICanvas
:
let view = new View3D()
...
let canvas:UICanvas = view.enableUICanvas();
By default, we only need one UICanvas
. If we need multiple canvas drawings, we can activate multiple UICanvas
by setting different index
, which are independent of each other:
let canvas0:UICanvas = view.enableUICanvas(0);
let canvas1:UICanvas = view.enableUICanvas(1);
let canvas2:UICanvas = view.enableUICanvas(2);
//...
This following example shows the performance of multiple UICanvas
coexistence:
import { Engine3D, Object3D, UIImage, ImageType, Color, UIPanel, ViewPanel, Scene3D, Vector2, UITextField, UIShadow, AtmosphericComponent, Camera3D, HoverCameraController, View3D } from '@orillusion/core';
class Sample_UIMultiCanvas {
async run() {
Engine3D.setting.shadow.autoUpdate = true;
// initializa engine
await Engine3D.init();
// load fnt
await Engine3D.res.loadFont('https://cdn.orillusion.com/fnt/0.fnt');
// create new scene as root node
let scene3D: Scene3D = new Scene3D();
scene3D.addComponent(AtmosphericComponent);
// create camera
let cameraObj: Object3D = new Object3D();
let camera = cameraObj.addComponent(Camera3D);
// adjust camera view
camera.perspective(60, Engine3D.aspect, 1, 5000.0);
// set camera controller
let controller = cameraObj.addComponent(HoverCameraController);
controller.setCamera(0, -20, 50);
// add camera node
scene3D.addChild(cameraObj);
let view = new View3D();
view.scene = scene3D;
view.camera = camera;
Engine3D.startRenderView(view);
let total: number = 4;
for (let i = 0; i < total; i++) {
let size: Vector2 = new Vector2();
size.x = 500 - i * 100;
size.y = 400 - i * 100;
this.createPanel(scene3D, i, size);
}
}
private createPanel(scene: Scene3D, index: number, size: Vector2): UIPanel {
let panelRoot: Object3D = new Object3D();
// enable ui canvas at index
let canvas = scene.view.enableUICanvas(index);
let panel = panelRoot.addComponent(ViewPanel);
canvas.addChild(panel.object3D);
// create image
let obj3D = new Object3D();
panelRoot.addChild(obj3D);
let image = obj3D.addComponent(UIImage);
image.isShadowless = true;
image.imageType = ImageType.Sliced;
image.uiTransform.resize(size.x, size.y);
image.color = Color.random();
//text
let text = obj3D.addComponent(UITextField);
text.text = 'Canvas index: ' + index;
text.fontSize = 24;
//shadow
let shadow = obj3D.addComponent(UIShadow);
shadow.shadowOffset.multiplyScaler(0.4);
return panel;
}
}
new Sample_UIMultiCanvas().run();
UIPanel
The panel UIPanel
is used to carry the specific GUI component rendering, and needs to be added to the UICanvas
;
let panelObj = new Object3D();
let panel:UIPanel = panelObj.addComponent(ViewPanel) // 创建一个屏幕空间面板组件 Create a screen space panel component
let canvas:UICanvas = view.enableUICanvas(); // 启用默认的 UICanvas Enable the default UICanvas
canvas.addChild(panel.object3D); // 添加面板 Add panel
Each UIPanel
can be regarded as the root container of the GUI component. Other types of GUI components can be added in the UIPanel
:
// Create a UIImage component
let imageQuad = new Object3D();
let image:UIImage = imageQuad.addComponent(UIImage);
// Create a UIPanel
let panel:UIPanel = new Object3D().addComponent(ViewPanel); // 创建一个屏幕空间面板组件 Create a screen space panel component
// Add the Object3D of UIImage to the Object3D of UIPanel
panel.object3D.addChild(imageQuad);
Rendering Order
In the same UICanvas
, it is allowed to have multiple ViewPanel
or WorldPanel
coexist, and their rendering hierarchy meets the following rules:
ViewPanel
will always be displayed aboveWorldPanel
.- The drawing priority of
ViewPanel
is controlled by the propertypanelOrder
. Under the samepanelOrder
, the order ofObject3D
mounted in the scene tree is subject to. - The drawing priority of
WorldPanel
is controlled by the propertypanelOrder
. Under the samepanelOrder
,UIPanel
can be automatically sorted according to the distance of the camera byneedSortOnCameraZ
.
let panel1 = new Object3D().addComponent(ViewPanel);
let panel2 = new Object3D().addComponent(ViewPanel);
let panel3 = new Object3D().addComponent(WorldPanel);
let panel4 = new Object3D().addComponent(WorldPanel);
// Manually set panelOrder, panel2 covers panel1
panel1.panelOrder = 1
panel2.panelOrder = 2
// ViewPanel panel1/2 always covers WorldPanel panel3/4
panel3.panelOrder = 3
panel4.panelOrder = 4 // panel4 covers panel3 first
// If panelOrder is the same, sort automatically according to the camera position
panel3.panelOrder = panel4.panelOrder = 3
panel3.needSortOnCameraZ = true;
panel4.needSortOnCameraZ = true;
WorldPanel
WorldPanel
component has more properties and functions than ViewPanel
:
Camera Lock
We can control the rendering angle of the panel by setting the billboard
property of the panel:
let panel = new Object3D().addComponent(WorldPanel);
panel.billboard = BillboardType.None; // Default view, keep the rendering angle of the object itself
panel.billboard = BillboardType.BillboardY; // Lock the Y axis, the XZ plane of the panel always faces the camera direction
panel.billboard = BillboardType.BillboardXYZ; // Always face the camera
Depth Test
Set whether the panel participates in depth sorting:
let panel = new Object3D().addComponent(WorldPanel);
panel.depthTest = true; // Participate in depth sorting, get occlusion relationship
panel.depthTest = false; // Do not participate in depth sorting, always float on the surface of all objects
Cull Mode
Similar to Material Culling, we can also set the cullMode
of the UIPanel
rendering material ball to switch the culling method:
let panel = new Object3D().addComponent(WorldPanel);
panel.cullMode = GPUCullMode.none; // Both sides are displayed
panel.cullMode = GPUCullMode.front; // Front culling, back display
panel.cullMode = GPUCullMode.back; // Default back culling, front display
The following example focuses on the spatial relationship between the panels and the rendering characteristics of WorldPanel
:
import { Engine3D, Object3DUtil, Object3D, UIImage, ImageType, Color, WorldPanel, UIPanel, GUICanvas, BillboardType, AtmosphericComponent, Camera3D, HoverCameraController, Scene3D, View3D, GPUCullMode, ViewPanel } from '@orillusion/core';
import * as dat from 'dat.gui';
class Sample_UIPanelOrder {
GUIHelp: dat.GUI;
async run() {
// initializa engine
await Engine3D.init();
// create new scene as root node
let scene3D: Scene3D = new Scene3D();
scene3D.addComponent(AtmosphericComponent);
// create camera
let cameraObj: Object3D = new Object3D();
let camera = cameraObj.addComponent(Camera3D);
// adjust camera view
camera.perspective(60, Engine3D.aspect, 1, 5000.0);
// set camera controller
let controller = cameraObj.addComponent(HoverCameraController);
controller.setCamera(0, -10, 100);
// add camera node
scene3D.addChild(cameraObj);
let view = new View3D();
view.scene = scene3D;
view.camera = camera;
Engine3D.startRenderView(view);
// create floor
let floor = Object3DUtil.GetSingleCube(100, 2, 50, 0.5, 0.5, 0.5);
scene3D.addChild(floor);
floor.y = -40;
// enable ui canvas at index 0
let canvas = scene3D.view.enableUICanvas();
//create UI root
let panelRoot: Object3D = new Object3D();
panelRoot.name = 'WorldPanel red';
panelRoot.scaleX = panelRoot.scaleY = panelRoot.scaleZ = 0.1;
let panelRoot2: Object3D = new Object3D();
panelRoot2.name = 'WorldPanel blue';
panelRoot2.z = 20;
panelRoot2.y = -10;
panelRoot2.x = -10;
panelRoot2.scaleX = panelRoot2.scaleY = panelRoot2.scaleZ = 0.1;
let panelRoot3: Object3D = new Object3D();
panelRoot3.name = 'ViewPanel Green';
this.GUIHelp = new dat.GUI();
this.createPanel(panelRoot, canvas, new Color(1.0, 0, 0.0, 0.8), 'world');
this.createPanel(panelRoot2, canvas, new Color(0, 0, 1, 0.8), 'world');
this.createPanel(panelRoot3, canvas, new Color(0, 1, 0, 0.5), 'view');
}
private createPanel(panelRoot: Object3D, canvas: GUICanvas, color: Color, type: string) {
let f = this.GUIHelp.addFolder(panelRoot.name);
if (type === 'world') {
let panel = panelRoot.addComponent(WorldPanel);
f.add(panel, 'panelOrder', 0, 10, 1);
panel.billboard = BillboardType.BillboardXYZ;
panel.needSortOnCameraZ = true;
f.add(panel, 'needSortOnCameraZ');
f.add({ cullMode: GPUCullMode.none }, 'cullMode', {
none: GPUCullMode.none,
front: GPUCullMode.front,
back: GPUCullMode.back
}).onChange((v) => {
panel.cullMode = v;
});
f.add({ billboard: panel.billboard }, 'billboard', {
None: BillboardType.None,
Y: BillboardType.BillboardY,
XYZ: BillboardType.BillboardXYZ
}).onChange((v) => {
panel.billboard = v;
});
f.add(panel, 'depthTest');
} else {
let panel = panelRoot.addComponent(ViewPanel);
f.add(panel, 'panelOrder', 0, 10, 1);
canvas;
}
f.open();
// create a UIImage
let obj3D = new Object3D();
panelRoot.addChild(obj3D);
let image = obj3D.addComponent(UIImage);
image.imageType = ImageType.Sliced;
image.uiTransform.resize(400, 300);
image.color = color;
canvas.addChild(panelRoot);
}
}
new Sample_UIPanelOrder().run();