-
Notifications
You must be signed in to change notification settings - Fork 1.4k
/
Copy pathcubemap-renderer.js
142 lines (119 loc) · 5.02 KB
/
cubemap-renderer.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
// How to use in the Editor:
// - create entity with Camera component - position of the entity defines where the cubemap is rendered from
// and properties of the Camera are used to render cubemap (adjust near / far distance, clearing, layers and other properties)
// Note: the layers should contain all layers visible by cubemap camera.
// - to use generated cube map, you can access it like this using script:
// material.cubeMap = entity.script.cubemapRenderer.cubeMap;
var CubemapRenderer = pc.createScript('cubemapRenderer');
CubemapRenderer.attributes.add('resolution', {
title: 'Resolution',
description: 'Resolution of one side of a cubemap. Use power of 2 resolution if you wish to use Mipmaps.',
type: 'number',
default: 64
});
CubemapRenderer.attributes.add('mipmaps', {
title: 'Mipmaps',
description: 'If set to true, mipmaps will be allocated and autogenerated.',
type: 'boolean',
default: true
});
CubemapRenderer.attributes.add('depth', {
title: 'Depth',
description: 'If set to true, depth buffer will be created.',
type: 'boolean',
default: true
});
// initialize code called once per entity
CubemapRenderer.prototype.initialize = function () {
// this entity needs to have camera component as well
var camera = this.entity.camera;
if (!camera) {
console.error('CubemapRenderer component requires Camera component to be created on the Entity.');
return;
}
// disable camera component, as it's used only as a source of properties
camera.enabled = false;
// limit maximum texture size
var resolution = Math.min(this.resolution, this.app.graphicsDevice.maxCubeMapSize);
// Create cubemap render target with specified resolution and mipmap generation
this.cubeMap = new pc.Texture(this.app.graphicsDevice, {
name: `${this.entity.name}:CubemapRenderer-${resolution}`,
width: resolution,
height: resolution,
format: pc.PIXELFORMAT_SRGBA8,
cubemap: true,
mipmaps: this.mipmaps,
minFilter: pc.FILTER_LINEAR_MIPMAP_LINEAR,
magFilter: pc.FILTER_LINEAR
});
// angles to render camera for all 6 faces
var cameraRotations = [
new pc.Quat().setFromEulerAngles(0, 90, 0),
new pc.Quat().setFromEulerAngles(0, -90, 0),
new pc.Quat().setFromEulerAngles(-90, 0, 180),
new pc.Quat().setFromEulerAngles(90, 0, 180),
new pc.Quat().setFromEulerAngles(0, 180, 0),
new pc.Quat().setFromEulerAngles(0, 0, 0)
];
// set up rendering for all 6 faces
let firstCamera = null;
let lastCamera = null;
for (var i = 0; i < 6; i++) {
// render target, connected to cubemap texture face
var renderTarget = new pc.RenderTarget({
name: `CubemapRenderer-Face${i}`,
colorBuffer: this.cubeMap,
depth: this.depth,
face: i,
flipY: !this.app.graphicsDevice.isWebGPU
});
// create a child entity with the camera for this face
var e = new pc.Entity(`CubeMapCamera_${i}`);
e.addComponent('camera', {
aspectRatio: 1,
fov: 90,
// cubemap will render all layers as setup on Entity's camera
layers: camera.layers,
// priority
priority: camera.priority,
// copy other camera properties
clearColor: camera.clearColor,
clearColorBuffer: camera.clearColorBuffer,
clearDepthBuffer: camera.clearDepthBuffer,
clearStencilBuffer: camera.clearStencilBuffer,
farClip: camera.farClip,
nearClip: camera.nearClip,
frustumCulling: camera.frustumCulling,
gammaCorrection: camera.gammaCorrection,
toneMapping: camera.toneMapping,
fog: camera.fog,
// this camera renders into texture target
renderTarget: renderTarget
});
// add the camera as a child entity
this.entity.addChild(e);
// set up its rotation
e.setRotation(cameraRotations[i]);
// keep the first and last camera
if (i === 0) firstCamera = e.camera;
if (i === 5) lastCamera = e.camera;
}
// Before the first camera renders, trigger onCubemapPreRender event on the entity.
this.evtPreRender = this.app.scene.on('prerender', (cameraComponent) => {
if (cameraComponent === firstCamera) {
this.entity.fire('onCubemapPreRender');
}
});
// When last camera is finished rendering, trigger onCubemapPostRender event on the entity.
// This can be listened to by the user, and the resulting cubemap can be further processed (e.g pre-filtering)
this.evtPostRender = this.app.scene.on('postrender', (cameraComponent) => {
if (cameraComponent === lastCamera) {
this.entity.fire('onCubemapPostRender');
}
});
// when the script is destroyed, remove event listeners
this.on('destroy', () => {
this.evtPreRender.off();
this.evtPostRender.off();
});
};