# 内建管线
除了内建节点,小游戏框架还提供了内置管线来满足不同需求,默认有两条管线。
# 前向渲染管线
前向渲染管线ForwardBaseRG
是小游戏框架内置的标准管线,其集成了Forward Base
和Forward Add
,同时将UI、Gizmo、Skybox全部纳入其中。
作为内置管线,为了和其他部分结合,其是基于相机Camera
构建的,每个场景中正在活跃的相机都拥有一个自己的子渲染图。所以这个内建管线的基本流程是:
onCamerasChange
-> 遍历cameras
-> 为每个camera
生成子图 -> 把这些子图首尾相连
图本身针对子图进行了缓存,避免每次修改都重构整张图。而除了构建图,另一个重要的部分是全局Uniform的创建和更新,这个直接影响到了每个渲染节点的更新。除此之外,一般而言对默认降级效果的注册也是在这里面进行的。
由于涉及到方方面面,下面就直接将整个整个内建管线的代码带注释放出:
export default class ForwardBaseRG extends RenderGraph {
// 是否已经注册过降级的阴影Effect
protected _shadowFallbackRegistered: boolean = false;
// 全局Uniform
protected _globalUniforms!: {
// ForwardBase阶段的全局Uniform
fbUniforms: engine.UniformBlock;
// ForwardAdd阶段的全局Uniform
faUniforms: engine.UniformBlock;
// UI阶段的全局Uniform
uiUniforms?: engine.UniformBlock;
// 阴影阶段的全局Uniform
scUniforms?: engine.UniformBlock;
// #if defined(EDITOR)
rcUniforms?: engine.UniformBlock;
// #endif
};
protected _subGraphs: {[nodeId: number]: BaseCamera} = {};
// 用于默认清屏的View
protected _defaultClearView?: engine.View;
public onActive(context: RenderSystem) {
this._globalUniforms = {
fbUniforms: new engine.UniformBlock(forwardBaseUD),
faUniforms: new engine.UniformBlock(forwardAddUD),
uiUniforms: new engine.UniformBlock(forwardBaseUD),
scUniforms: new engine.UniformBlock(shadowCaterUD),
// #if defined(EDITOR)
rcUniforms: new engine.UniformBlock(forwardBaseUD)
// #endif
}
this._defaultClearView = new engine.View({
passAction: {
clearColor: [0, 0, 0, 1],
colorAction: engine.ELoadAction.CLEAR
},
viewport: {x: 0, y: 0, w: 1, h: 1},
scissor: {x: 0, y: 0, w: 1, h: 1}
});
this._rebuildRG(context.cameras, context.changedCameras);
}
public onCamerasChange(cameras: BaseCamera[], changeCameras: BaseCamera[]) {
this._rebuildRG(cameras, changeCameras);
}
// 通过`cameras`创建整张图,相机列表已经排好序
protected _rebuildRG(cameras: BaseCamera[], changeCameras: BaseCamera[]) {
// 首先清空整张图,但保留Camera没有变过的子图
const cachedCameras: {[cameraId: number]: TRGNodeAny} = {};
this._clear((node: TRGNodeAny) => {
const camera = this._subGraphs[node.id];
if (camera && changeCameras.indexOf(camera) < 0) {
cachedCameras[camera.id] = node;
return true;
}
delete this._subGraphs[node.id];
return false;
});
let lastNode: TRGNodeAny;
// 如果只有一个相机并且是UI相机,则强制清屏一次,因为主UI相机默认不清颜色
if (cameras[0] instanceof UICamera && (!cameras[0].renderTarget || cameras[0].renderTarget instanceof Screen)) {
const defaultCameraNode = this.createNode<RGGenViewNode>(`default-view-gen`, RGGenViewNode, {viewObject: {view: this._defaultClearView!}});
const defaultRtNode = this.createNode<RGGenRenderTargetNode>('rt-screen', RGGenRenderTargetNode, {createRenderTarget: () => this.context.screen});
const defaultClearNode = this.createNode<RGClearNode>('default-view-clear', RGClearNode, {});
this.connect(defaultCameraNode, defaultClearNode, 'camera');
this.connect(defaultRtNode, defaultClearNode, 'renderTarget');
lastNode = defaultClearNode;
}
// 利用缓存,创建所有子图节点
cameras.forEach(camera => {
let subGraph = cachedCameras[camera.id] as TForwardBaseSubGraphNode;
if (!subGraph) {
subGraph = this.createNode<TForwardBaseSubGraphNode>(`subGraph-${camera.entity.name}`, RGSubGraphNode, {
// 子图的类
RGClass: ForwardBaseSubGraph,
options: {
...this._globalUniforms,
camera
}
});
this._subGraphs[subGraph.id] = camera;
}
lastNode && this.connect(lastNode, subGraph);
lastNode = subGraph;
});
}
// 每帧渲染开始前,设置全局Uniform
public onExecuteBegin(context: RenderSystem) {
const dirL = context.lights.mainDirectionalLight;
const uniforms = this._globalUniforms.fbUniforms!;
const settings = this.game.activeScene.settings;
uniforms.setUniform("u_ambientLight", settings.ambientLight._raw);
tempArr4[0] = settings.fogMode;
tempArr4[1] = settings.fogStart;
tempArr4[2] = settings.fogStart + settings.fogRange;
tempArr4[3] = settings.fogDensity;
uniforms.setUniform("u_fogInfos", tempArr4);
uniforms.setUniform("u_fogColor", settings.fogColor._raw);
uniforms.setUniform("u_lightDir", dirL ? dirL.direction._raw : [0, 0, 1]);
uniforms.setUniform("u_lightColor", dirL ? dirL.color.scale(dirL.intensity, tempVec3)._raw : [0, 0, 0]);
tempArr1[0] = this.game.gameTime;
uniforms.setUniform("u_gameTime", tempArr1);
tempArr3[0] = settings.subtractiveShadowColor.r / 255.0;
tempArr3[1] = settings.subtractiveShadowColor.g / 255.0;
tempArr3[2] = settings.subtractiveShadowColor.b / 255.0;
uniforms.setUniform("u_shadowColor", tempArr3);
tempArr1[0] = 1 - (dirL ? dirL.shadowStrength : 1);
uniforms.setUniform("u_shadowStrength", tempArr1);
uniforms.setUniform("u_shadowMapTex", BuildInTextures.white._getengineTexture()!);
uniforms.setUniform("u_lightMap", BuildInTextures.black._getengineTexture()!);
uniforms.setUniform("u_speCube", settings.mainEnvironmentMap._getengineTexture()!);
uniforms.setUniform("u_environmentMap", settings.mainPanoramaMap._getengineTexture()!);
uniforms.setUniform('u_csmFarBounds', [0, 0, 0, 0]);
uniforms.setUniform('u_SH', settings.shCoefficients);
this._globalUniforms.uiUniforms?.copy(uniforms);
const faUniforms = this._globalUniforms.faUniforms!;
tempArr4[0] = settings.fogMode;
tempArr4[1] = settings.fogStart;
tempArr4[2] = settings.fogStart + settings.fogRange;
tempArr4[3] = settings.fogDensity;
faUniforms.setUniform("u_fogInfos", tempArr4);
faUniforms.setUniform("u_fogColor", settings.fogColor._raw);
tempArr1[0] = this.game.gameTime;
faUniforms.setUniform("u_gameTime", tempArr1);
// 注册默认阴影Effect
if (!this._shadowFallbackRegistered) {
const scEffect = engine.loader.getAsset<Effect>('System::Effect::ShadowCasterFallBack');
if (scEffect) {
renderEnv.registerFallbackEffect('ShadowCaster', scEffect);
this._shadowFallbackRegistered = true;
}
}
}
// 在所有相机的处理结束后,清除当前game内部的UICanvas中脏列表的,顶点脏位以及材质脏位,以及脏列表本身
public onExecuteDone(context: RenderSystem) {
const uiCanvas = this.game!.rootUICanvas;
if (uiCanvas._vertexDirtyList.length > 0) {
for (let i = 0; i < uiCanvas._vertexDirtyList.length; i++) {
const dirtyRenderable2d = uiCanvas._vertexDirtyList.data[i];
if (dirtyRenderable2d._vertexDirtyUpdateFlag) {
// 清理顶点脏位
dirtyRenderable2d._vertexDirtyUpdateFlag = false;
}
}
uiCanvas._vertexDirtyList.reset();
}
if (uiCanvas._materialDirtyList.length > 0) {
for (let i = 0; i < uiCanvas._materialDirtyList.length; i++) {
const dirtyRenderable2d = uiCanvas._materialDirtyList.data[i];
if (dirtyRenderable2d._materialDirtyUpdateFlag) {
// 清理材质脏位
dirtyRenderable2d._materialDirtyUpdateFlag = false;
}
}
uiCanvas._materialDirtyList.reset();
}
if (uiCanvas._designResolutionDirty) {
uiCanvas._designResolutionDirty = false;
}
}
public onDisable(context: RenderSystem) {
// 取消注册默认阴影Effect
this._shadowFallbackRegistered && renderEnv.unregisterFallbackEffect('ShadowCaster');
}
}
而子图的实现为:
export interface IForwardBaseSubGraphOptions {
// 相机
camera: BaseCamera;
// ForwardBase阶段的全局Uniform
fbUniforms: engine.UniformBlock;
// ForwardAdd阶段的全局Uniform
faUniforms: engine.UniformBlock;
// UI阶段的全局Uniform
uiUniforms?: engine.UniformBlock;
// 阴影阶段的全局Uniform
scUniforms?: engine.UniformBlock;
// #if defined(EDITOR)
rcUniforms?: engine.UniformBlock;
// #endif
}
/**
* 默认的内置前向渲染管线的子图,针对每个相机生成。
* 实际上包含了Forward Add部分。
*/
export default class ForwardBaseSubGraph extends RenderGraph<IForwardBaseSubGraphOptions> {
// 用于默认清屏的View
protected _defaultClearView?: engine.View;
public onActive(context: RenderSystem, options: IForwardBaseSubGraphOptions) {
this._rebuildRG(options);
}
// 通过`cameras`创建整张图,相机列表已经排好序
protected _rebuildRG(options: IForwardBaseSubGraphOptions) {
// 首先清空整张图
this._clear();
const {camera} = options;
if (camera instanceof UICamera) {
// 如果是UI相机,则创建UI相机的子图
const cameraNode = this.createNode<RGCameraNode>(`camera-${camera.entity.name}`, RGCameraNode, {camera});
const clearNode = this.createNode<RGClearNode>('ui-clear', RGClearNode, {});
const uiNode = this.createNode<RGUIPrepareNode>('ui-prepare', RGUIPrepareNode, {camera});
const uiRTNode = this.createNode<RGGenRenderTargetNode>('render-target', RGGenRenderTargetNode, {createRenderTarget: () => this._createRT(camera)});
const uiRenderNode = this.createNode<RGRenderNode>('ui-render', RGRenderNode, {
lightMode: 'ForwardBase',
createUniformBlocks: () => [options.uiUniforms!]
});
this.connect(cameraNode, uiRenderNode, 'camera');
this.connect(cameraNode, clearNode, 'camera');
this.connect(uiRTNode, clearNode, 'renderTarget');
this.connect(uiRTNode, uiRenderNode, 'renderTarget');
this.connect(clearNode, uiRenderNode);
this.connect(uiNode, uiRenderNode, 'meshList');
return;
}
this._buildNodesForCamera(camera as Camera, options)
}
// 将会返回`lastNode`,用于连接各个子图,保证顺序
protected _buildNodesForCamera(camera: Camera, options: IForwardBaseSubGraphOptions) {
// 以下是ForwardBase节点创建部分
let rtNode!: RGGenRenderTargetNode;
if (camera.postProcess) {
rtNode = this.createNode<RGGenRenderTargetNode>('render-target', RGGenRenderTargetNode, {createRenderTarget: () => this._createPostProcessRT(camera, camera.postProcess?.hdr || false)});
} else {
rtNode = this.createNode<RGGenRenderTargetNode>('render-target', RGGenRenderTargetNode, {createRenderTarget: () => this._createRT(camera)});
}
const cameraNode = this.createNode<RGCameraNode>(`camera-${camera.entity.name}`, RGCameraNode, {camera});
const fwCullNode = this.createNode<RGCullNode>('fb-cull', RGCullNode, {lightMode: 'ForwardBase'});
const fwClearNode = this.createNode<RGClearNode>('fb-clear', RGClearNode, {});
const fwRenderNode = this.createNode<RGRenderNode<{shadowMap: 'RenderTarget'}>>('fb-render', RGRenderNode, {
lightMode: 'ForwardBase',
createUniformBlocks: () => [options.fbUniforms!],
inputTypes: {'shadowMap': 'RenderTarget'},
uniformsMap: {
u_shadowMapTex: {
inputKey: 'shadowMap',
name: 'color'
}
}
});
// 以下是Skybox节点创建连接部分
if (camera.drawSkybox) {
const skyBoxNode = this.createNode<RGSkyBoxNode>('skybox', RGSkyBoxNode, {});
const skyBoxRenderNode = this.createNode<RGLightNode>('skybox-render', RGRenderNode, {
lightMode: 'Skybox',
createUniformBlocks: () => [options.fbUniforms!]
});
this.connect(cameraNode, skyBoxRenderNode, 'camera');
this.connect(skyBoxNode, skyBoxRenderNode, 'meshList');
this.connect(rtNode, skyBoxRenderNode, 'renderTarget');
this.connect(skyBoxRenderNode, fwRenderNode);
}
// 以下是阴影渲染节点创建连接部分
if (camera.shadowMode !== engine.EShadowMode.None) {
const scRTNode = this.createNode<RGGenRenderTargetNode>('sc-render-target', RGGenRenderTargetNode, {createRenderTarget: () => this._createRT(camera, 'ShadowCaster')});
const scRenderNode = this.createNode<RGLightNode>('shadow-caster', RGLightNode, {
lightMode: 'ShadowCaster',
createUniformBlocks: () => [options.scUniforms!]
});
this.connect(cameraNode, scRenderNode, 'camera');
this.connect(scRTNode, scRenderNode, 'renderTarget');
this.connect(fwCullNode, scRenderNode, 'meshList');
this.connect(scRenderNode, fwRenderNode, 'shadowMap');
}
// 以下是ForwardBase节点连接部分
this.connect(cameraNode, fwCullNode, 'camera');
this.connect(cameraNode, fwRenderNode, 'camera');
this.connect(rtNode, fwClearNode, 'renderTarget');
this.connect(cameraNode, fwClearNode, 'camera');
this.connect(rtNode, fwRenderNode, 'renderTarget');
this.connect(fwClearNode, fwRenderNode);
this.connect(fwCullNode, fwRenderNode, 'meshList');
// 以下是ForwardAdd节点创建连接部分
const faCullLightNode = this.createNode<RGCullLightFANode>('fa-cull-light', RGCullLightFANode, {});
const faCullMeshNode = this.createNode<RGCullMeshFANode>('fa-cull-mesh', RGCullMeshFANode, {});
const faRenderNode = this.createNode<RGFARenderNode>('fa-render', RGFARenderNode, {
lightMode: 'ForwardAdd',
createUniformBlocks: () => [options.faUniforms!],
});
this.connect(fwCullNode, faCullLightNode, 'meshList');
this.connect(fwCullNode, faCullMeshNode, 'meshList');
this.connect(faCullLightNode, faCullMeshNode);
this.connect(fwRenderNode, faRenderNode);
this.connect(cameraNode, faRenderNode, 'camera');
this.connect(rtNode, faRenderNode, 'renderTarget');
this.connect(faCullMeshNode, faRenderNode, 'meshList');
this.connect(faCullLightNode, faRenderNode, 'lightList');
// 以下是后处理节点创建连接部分
let finalBlitNode!: RGBlitPostProcessNode;
if (camera.postProcess) {
let preNode: RGNode<any, any, any> = rtNode;
const {hdr, steps} = camera.postProcess.getData();
steps.forEach((step, index) => {
let currentNode: RGNode<any, any, any>;
switch (step.type) {
case EBuiltinPostProcess.BLIT:
currentNode = this.createNode('post-process-blit', RGBlitPostProcessNode, {hdr, ...step.data});
break;
case EBuiltinPostProcess.BLOOM:
currentNode = this.createNode('post-process-bloom', RGBloomPostProcessNode, {hdr, ...step.data});
break;
case EBuiltinPostProcess.FXAA:
currentNode = this.createNode('post-process-fxaa', RGFXAAPostProcessNode, {hdr, ...step.data});
break;
case EBuiltinPostProcess.HDR:
currentNode = this.createNode('post-process-tone', RGHDRPostProcessNode, {hdr, ...step.data});
break;
}
this.connect(preNode, currentNode, 'sourceTex');
index === 0 && this.connect(faRenderNode, currentNode);
preNode = currentNode;
});
finalBlitNode = this.createNode('final-blit', RGBlitPostProcessNode, {dstTex: this._createRT(camera)});
this.connect(preNode, finalBlitNode, 'sourceTex');
}
// 以下是Gizmo节点创建连接部分
if (camera.drawGizmo) {
const gizmoNode = this.createNode<RGGizmosNode>('gizmo', RGGizmosNode, {});
const gizmoRenderNode = this.createNode<RGRenderNode>('gizmo-render', RGRenderNode, {
lightMode: 'ForwardBase',
createUniformBlocks: () => [options.fbUniforms!],
});
const rect = camera.viewport;
const x = rect ? rect.xMin : 0;
const y = rect ? rect.yMin : 0;
const width = rect ? rect.width : 1;
const height = rect ? rect.height : 1;
const viewPortRect: engine.IRect = {x, y, w: width, h: height};
const scissorRect: engine.IRect = {x, y, w: width, h: height};
const gizmoView = new engine.View({
passAction: {
depthAction: engine.ELoadAction.CLEAR,
clearDepth: 1,
colorAction: engine.ELoadAction.LOAD,
stencilAction: engine.ELoadAction.LOAD,
},
viewport: viewPortRect,
scissor: scissorRect
})
// 在画gizmo之前,将深度清除,保证gizmo画在最前面
const gizmoClearDepthNode = this.createNode<RGClearNode>('gizmo-clear-depth', RGClearNode, {})
const gizmoViewNode = this.createNode<RGGenViewNode>('gizmo-view', RGGenViewNode, { viewObject: { view: gizmoView } })
this.connect(rtNode, gizmoClearDepthNode, 'renderTarget');
this.connect(finalBlitNode || faRenderNode, gizmoViewNode);
this.connect(gizmoViewNode, gizmoClearDepthNode, 'camera')
this.connect(gizmoClearDepthNode, gizmoRenderNode);
this.connect(cameraNode, gizmoNode, 'camera');
this.connect(cameraNode, gizmoRenderNode, 'camera');
this.connect(rtNode, gizmoRenderNode, 'renderTarget');
this.connect(gizmoNode, gizmoRenderNode, 'meshList');
}
}
// 通过相机和lightMode创建RenderTarget
protected _createRT(camera: BaseCamera, lightMode: string = 'ForwardBase') {
let fbRT: RenderTexture | Screen | null = null;
if (camera.renderTarget) {
fbRT = camera.renderTarget;
} else {
// 模拟器跟ide都需要这个逻辑,所以不加define editor
if (camera.editorRenderTarget) {
fbRT = camera.editorRenderTarget
} else {
fbRT = this.context.screen;
}
}
if (lightMode === 'ShadowCaster') {
// 如果是阴影流程,则创建一张RenderTexture
return new RenderTexture({width: 2048, height: 2048});
}
return fbRT;
}
protected _createPostProcessRT(camera: BaseCamera, hdr: boolean): RenderTexture {
const origRT = camera.renderTarget || this.context.screen;
const {width, height} = origRT;
return new RenderTexture({width, height, colors: [{
pixelFormat: hdr ? engine.ETextureFormat.RGBA16F : engine.ETextureFormat.RGBA8
}]});
}
}
# 调试管线
调试管线DebugRG
专门用于调试当前渲染环境是否正常,要使用它非常简单:
// 初始化参数中的`texture`可以改,这里默认给了个绿色
const debugRG = new engine.DebugRG({texture: engine.BuildInTextures.green});
game.renderSystem.useRenderGraph(debugRG);
如果屏幕上显示了四分之一个绿色的色块,则表明渲染系统运作正常。