Skip to content

Commit eeb5a59

Browse files
authored
Camera support opaque texture (#1989)
* feat: camera support opaque texture
1 parent c1edb04 commit eeb5a59

34 files changed

+718
-362
lines changed

e2e/case/camera-opaque-texture.ts

+115
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
/**
2+
* @title Opaque Texture
3+
* @category Camera
4+
*/
5+
import {
6+
Animator,
7+
BaseMaterial,
8+
Camera,
9+
DirectLight,
10+
Engine,
11+
Entity,
12+
GLTFResource,
13+
Logger,
14+
MeshRenderer,
15+
PrimitiveMesh,
16+
RenderFace,
17+
Shader,
18+
Vector3,
19+
WebGLEngine
20+
} from "@galacean/engine";
21+
import { OrbitControl } from "@galacean/engine-toolkit";
22+
import { initScreenshot, updateForE2E } from "./.mockForE2E";
23+
24+
Logger.enable();
25+
WebGLEngine.create({ canvas: "canvas" }).then((engine) => {
26+
engine.canvas.resizeByClientSize(2);
27+
const scene = engine.sceneManager.activeScene;
28+
const rootEntity = scene.createRootEntity();
29+
30+
// camera
31+
const cameraEntity = rootEntity.createChild("camera_node");
32+
cameraEntity.transform.position = new Vector3(0, 1, 3);
33+
const camera = cameraEntity.addComponent(Camera);
34+
cameraEntity.addComponent(OrbitControl).target = new Vector3(0, 1, 0);
35+
camera.opaqueTextureEnabled = true;
36+
37+
const lightNode = rootEntity.createChild("light_node");
38+
lightNode.addComponent(DirectLight).intensity = 0.6;
39+
lightNode.transform.lookAt(new Vector3(0, 0, 1));
40+
lightNode.transform.rotate(new Vector3(0, 90, 0));
41+
42+
engine.resourceManager
43+
.load<GLTFResource>("https://gw.alipayobjects.com/os/bmw-prod/5e3c1e4e-496e-45f8-8e05-f89f2bd5e4a4.glb")
44+
.then((gltfResource) => {
45+
const { defaultSceneRoot } = gltfResource;
46+
rootEntity.addChild(defaultSceneRoot);
47+
const animator = defaultSceneRoot.getComponent(Animator);
48+
animator.play("agree");
49+
50+
showOpaquePlane(engine, cameraEntity);
51+
52+
53+
updateForE2E(engine);
54+
55+
initScreenshot(engine, camera);
56+
});
57+
});
58+
59+
function showOpaquePlane(engine: Engine, camera: Entity): void {
60+
const entity = camera.createChild("Plane");
61+
entity.transform.setPosition(0, 0, -1);
62+
entity.transform.rotate(new Vector3(90, 0, 0));
63+
const renderer = entity.addComponent(MeshRenderer);
64+
renderer.mesh = PrimitiveMesh.createPlane(engine, 0.5, 0.5);
65+
66+
// Create material
67+
const material = new BaseMaterial(engine, Shader.find("RenderOpaqueTexture"));
68+
material.isTransparent = true;
69+
renderer.setMaterial(material);
70+
}
71+
72+
const renderOpaqueVS = `
73+
#include <common>
74+
#include <common_vert>
75+
#include <blendShape_input>
76+
#include <uv_share>
77+
#include <FogVertexDeclaration>
78+
79+
void main() {
80+
#include <begin_position_vert>
81+
#include <blendShape_vert>
82+
#include <skinning_vert>
83+
#include <uv_vert>
84+
#include <position_vert>
85+
86+
#include <FogVertex>
87+
}`;
88+
89+
const renderOpaqueFS = `
90+
#include <common>
91+
#include <uv_share>
92+
#include <FogFragmentDeclaration>
93+
94+
uniform sampler2D camera_OpaqueTexture;
95+
96+
void main() {
97+
vec4 baseColor = texture2D(camera_OpaqueTexture, v_uv);
98+
#ifndef ENGINE_IS_COLORSPACE_GAMMA
99+
baseColor = gammaToLinear(baseColor);
100+
#endif
101+
102+
gl_FragColor = baseColor;
103+
104+
#ifndef MATERIAL_IS_TRANSPARENT
105+
gl_FragColor.a = 1.0;
106+
#endif
107+
108+
#include <FogFragment>
109+
110+
#ifndef ENGINE_IS_COLORSPACE_GAMMA
111+
gl_FragColor = linearToGamma(gl_FragColor);
112+
#endif
113+
}`;
114+
115+
Shader.create("RenderOpaqueTexture", renderOpaqueVS, renderOpaqueFS);

e2e/config.ts

+7
Original file line numberDiff line numberDiff line change
@@ -133,5 +133,12 @@ export const E2E_CONFIG = {
133133
caseFileName: "primitive-torus",
134134
threshold: 0.1
135135
}
136+
},
137+
Camera: {
138+
opaqueTexture: {
139+
category: "Camera",
140+
caseFileName: "camera-opaque-texture",
141+
threshold: 0.1
142+
}
136143
}
137144
};
Loading

packages/core/src/2d/sprite/SpriteRenderer.ts

+6-1
Original file line numberDiff line numberDiff line change
@@ -283,7 +283,12 @@ export class SpriteRenderer extends Renderer {
283283
/**
284284
* @internal
285285
*/
286-
protected override _updateShaderData(context: RenderContext): void {
286+
override _updateShaderData(context: RenderContext, onlyMVP: boolean): void {
287+
if (onlyMVP) {
288+
// @ts-ignore
289+
this._updateMVPShaderData(context, Matrix._identity);
290+
return;
291+
}
287292
// @ts-ignore
288293
this._updateTransformShaderData(context, Matrix._identity);
289294
}

packages/core/src/2d/text/TextRenderer.ts

+6-1
Original file line numberDiff line numberDiff line change
@@ -353,7 +353,12 @@ export class TextRenderer extends Renderer {
353353
/**
354354
* @internal
355355
*/
356-
protected override _updateShaderData(context: RenderContext): void {
356+
override _updateShaderData(context: RenderContext, onlyMVP: boolean): void {
357+
if (onlyMVP) {
358+
// @ts-ignore
359+
this._updateMVPShaderData(context, Matrix._identity);
360+
return;
361+
}
357362
// @ts-ignore
358363
this._updateTransformShaderData(context, Matrix._identity);
359364
}

packages/core/src/BasicResources.ts

+52
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import { Engine } from "./Engine";
2+
import { Buffer } from "./graphic/Buffer";
3+
import { VertexElement } from "./graphic/VertexElement";
4+
import { BufferBindFlag } from "./graphic/enums/BufferBindFlag";
5+
import { BufferUsage } from "./graphic/enums/BufferUsage";
6+
import { MeshTopology } from "./graphic/enums/MeshTopology";
7+
import { VertexElementFormat } from "./graphic/enums/VertexElementFormat";
8+
import { Material } from "./material";
9+
import { ModelMesh } from "./mesh";
10+
import { Shader } from "./shader/Shader";
11+
12+
/**
13+
* @internal
14+
*/
15+
export class BasicResources {
16+
readonly blitMesh: ModelMesh;
17+
readonly flipYBlitMesh: ModelMesh;
18+
readonly blitMaterial: Material;
19+
20+
constructor(engine: Engine) {
21+
// prettier-ignore
22+
const vertices = new Float32Array([
23+
-1, -1, 0, 1, // left-bottom
24+
1, -1, 1, 1, // right-bottom
25+
-1, 1, 0, 0, // left-top
26+
1, 1, 1, 0]); // right-top
27+
28+
// prettier-ignore
29+
const flipYVertices = new Float32Array([
30+
1, -1, 1, 0, // right-bottom
31+
-1, -1, 0, 0, // left-bottom
32+
1, 1, 1, 1, // right-top
33+
-1, 1, 0, 1]); // left-top
34+
35+
const blitMaterial = new Material(engine, Shader.find("blit"));
36+
blitMaterial.renderState.depthState.enabled = false;
37+
blitMaterial.renderState.depthState.writeEnabled = false;
38+
39+
this.blitMesh = this._createBlitMesh(engine, vertices);
40+
this.flipYBlitMesh = this._createBlitMesh(engine, flipYVertices);
41+
this.blitMaterial = blitMaterial;
42+
}
43+
44+
private _createBlitMesh(engine: Engine, vertices: Float32Array): ModelMesh {
45+
const mesh = new ModelMesh(engine);
46+
mesh._addReferCount(1);
47+
mesh.setVertexElements([new VertexElement("POSITION_UV", 0, VertexElementFormat.Vector4, 0)]);
48+
mesh.setVertexBufferBinding(new Buffer(engine, BufferBindFlag.VertexBuffer, vertices, BufferUsage.Static), 16);
49+
mesh.addSubMesh(0, 4, MeshTopology.TriangleStrip);
50+
return mesh;
51+
}
52+
}

packages/core/src/Camera.ts

+78
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,16 @@ import { DependentMode, dependentComponents } from "./ComponentsDependencies";
55
import { Entity } from "./Entity";
66
import { Layer } from "./Layer";
77
import { BasicRenderPipeline } from "./RenderPipeline/BasicRenderPipeline";
8+
import { PipelineUtils } from "./RenderPipeline/PipelineUtils";
89
import { Transform } from "./Transform";
910
import { VirtualCamera } from "./VirtualCamera";
1011
import { Logger } from "./base";
1112
import { deepClone, ignoreClone } from "./clone/CloneManager";
1213
import { CameraClearFlags } from "./enums/CameraClearFlags";
1314
import { CameraType } from "./enums/CameraType";
1415
import { DepthTextureMode } from "./enums/DepthTextureMode";
16+
import { Downsampling } from "./enums/Downsampling";
17+
import { MSAASamples } from "./enums/MSAASamples";
1518
import { Shader } from "./shader/Shader";
1619
import { ShaderData } from "./shader/ShaderData";
1720
import { ShaderMacroCollection } from "./shader/ShaderMacroCollection";
@@ -35,6 +38,8 @@ class MathTemp {
3538
export class Camera extends Component {
3639
/** @internal */
3740
static _cameraDepthTextureProperty = ShaderProperty.getByName("camera_DepthTexture");
41+
/** @internal */
42+
static _cameraOpaqueTextureProperty = ShaderProperty.getByName("camera_OpaqueTexture");
3843

3944
private static _inverseViewMatrixProperty = ShaderProperty.getByName("camera_ViewInvMat");
4045
private static _cameraPositionProperty = ShaderProperty.getByName("camera_Position");
@@ -47,6 +52,7 @@ export class Camera extends Component {
4752

4853
/**
4954
* Determining what to clear when rendering by a Camera.
55+
*
5056
* @defaultValue `CameraClearFlags.All`
5157
*/
5258
clearFlags: CameraClearFlags = CameraClearFlags.All;
@@ -59,10 +65,26 @@ export class Camera extends Component {
5965

6066
/**
6167
* Depth texture mode.
68+
* If `DepthTextureMode.PrePass` is used, the depth texture can be accessed in the shader using `camera_DepthTexture`.
69+
*
6270
* @defaultValue `DepthTextureMode.None`
6371
*/
6472
depthTextureMode: DepthTextureMode = DepthTextureMode.None;
6573

74+
/**
75+
* Opacity texture down sampling.
76+
*
77+
* @defaultValue `Downsampling.TwoX`
78+
*/
79+
opaqueTextureDownsampling: Downsampling = Downsampling.TwoX;
80+
81+
/**
82+
* Multi-sample anti-aliasing samples when use independent canvas mode.
83+
*
84+
* @remarks The `independentCanvasEnabled` property should be `true` to take effect, otherwise it will be invalid.
85+
*/
86+
msaaSamples: MSAASamples = MSAASamples.None;
87+
6688
/** @internal */
6789
_cameraType: CameraType = CameraType.Normal;
6890
/** @internal */
@@ -95,6 +117,8 @@ export class Camera extends Component {
95117
private _customAspectRatio: number | undefined = undefined;
96118
private _renderTarget: RenderTarget = null;
97119
private _depthBufferParams: Vector4 = new Vector4();
120+
private _customIndependentCanvas: boolean = false;
121+
private _opaqueTextureEnabled: boolean = false;
98122

99123
@ignoreClone
100124
private _frustumChangeFlag: BoolUpdateFlag;
@@ -113,6 +137,46 @@ export class Camera extends Component {
113137
@deepClone
114138
private _invViewProjMat: Matrix = new Matrix();
115139

140+
/**
141+
* Whether to enable opaque texture.
142+
* If enabled, the opaque texture can be accessed in the shader using `camera_OpaqueTexture`.
143+
*
144+
* @defaultValue `false`
145+
*
146+
* @remarks If enabled, the `independentCanvasEnabled` property will be forced to be true.
147+
*/
148+
get opaqueTextureEnabled(): boolean {
149+
return this._opaqueTextureEnabled;
150+
}
151+
152+
set opaqueTextureEnabled(value: boolean) {
153+
if (this._opaqueTextureEnabled !== value) {
154+
this._opaqueTextureEnabled = value;
155+
this._checkMainCanvasAntialiasWaste();
156+
}
157+
}
158+
159+
/**
160+
* Whether to use an independent canvas in viewport area.
161+
*
162+
* @remarks If true, the msaa in viewport can turn or off independently by `msaaSamples` property.
163+
*/
164+
get independentCanvasEnabled(): boolean {
165+
const forceIndependent = this._forceUseInternalCanvas();
166+
return forceIndependent || this._customIndependentCanvas;
167+
}
168+
169+
set independentCanvasEnabled(value: boolean) {
170+
const forceIndependent = this._forceUseInternalCanvas();
171+
if (forceIndependent && !value) {
172+
console.warn(
173+
"The camera is forced to use the independent canvas because the opaqueTextureEnabled property is enabled."
174+
);
175+
}
176+
this._customIndependentCanvas = value;
177+
this._checkMainCanvasAntialiasWaste();
178+
}
179+
116180
/**
117181
* Shader data.
118182
*/
@@ -325,6 +389,7 @@ export class Camera extends Component {
325389
value && this._addResourceReferCount(value, 1);
326390
this._renderTarget = value;
327391
this._onPixelViewportChanged();
392+
this._checkMainCanvasAntialiasWaste();
328393
}
329394
}
330395

@@ -712,9 +777,22 @@ export class Camera extends Component {
712777
return this._inverseProjectionMatrix;
713778
}
714779

780+
private _forceUseInternalCanvas(): boolean {
781+
return !this._renderTarget && this.opaqueTextureEnabled;
782+
}
783+
715784
@ignoreClone
716785
private _onPixelViewportChanged(): void {
717786
this._updatePixelViewport();
718787
this._customAspectRatio ?? this._projectionMatrixChange();
788+
this._checkMainCanvasAntialiasWaste();
789+
}
790+
791+
private _checkMainCanvasAntialiasWaste(): void {
792+
if (this.independentCanvasEnabled && Vector4.equals(this._viewport, PipelineUtils.defaultViewport)) {
793+
console.warn(
794+
"Camera use independent canvas and viewport cover the whole screen, it is recommended to disable antialias, depth and stencil to save memory when create engine."
795+
);
796+
}
719797
}
720798
}

packages/core/src/Engine.ts

+4
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import {
99
import { Color } from "@galacean/engine-math";
1010
import { SpriteMaskInteraction } from "./2d";
1111
import { Font } from "./2d/text/Font";
12+
import { BasicResources } from "./BasicResources";
1213
import { Camera } from "./Camera";
1314
import { Canvas } from "./Canvas";
1415
import { EngineSettings } from "./EngineSettings";
@@ -91,6 +92,8 @@ export class Engine extends EventDispatcher {
9192
/* @internal */
9293
_textRenderDataPool: ClassPool<TextRenderData> = new ClassPool(TextRenderData);
9394

95+
/* @internal */
96+
_basicResources: BasicResources;
9497
/* @internal */
9598
_spriteDefaultMaterial: Material;
9699
/** @internal */
@@ -298,6 +301,7 @@ export class Engine extends EventDispatcher {
298301
colorSpace === ColorSpace.Gamma && this._macroCollection.enable(Engine._gammaMacro);
299302
innerSettings.colorSpace = colorSpace;
300303

304+
this._basicResources = new BasicResources(this);
301305
this._particleBufferUtils = new ParticleBufferUtils(this);
302306
}
303307

0 commit comments

Comments
 (0)