forked from playcanvas/engine
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathrender-target.js
More file actions
279 lines (251 loc) · 10.1 KB
/
render-target.js
File metadata and controls
279 lines (251 loc) · 10.1 KB
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
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
import { PIXELFORMAT_DEPTH, PIXELFORMAT_DEPTHSTENCIL } from './constants.js';
import { GraphicsDevice } from './graphics-device.js';
var defaultOptions = {
depth: true,
face: 0
};
/**
* @class
* @name RenderTarget
* @classdesc A render target is a rectangular rendering surface.
* @description Creates a new render target. A color buffer or a depth buffer must be set.
* @param {object} options - Object for passing optional arguments.
* @param {Texture} [options.colorBuffer] - The texture that this render target will treat as a rendering surface.
* @param {boolean} [options.depth] - If set to true, depth buffer will be created. Defaults to true. Ignored if depthBuffer is defined.
* @param {boolean} [options.stencil] - If set to true, depth buffer will include stencil. Defaults to false. Ignored if depthBuffer is defined or depth is false.
* @param {Texture} [options.depthBuffer] - The texture that this render target will treat as a depth/stencil surface (WebGL2 only). If set, the 'depth' and 'stencil' properties are ignored.
* Texture must have {@link PIXELFORMAT_DEPTH} or PIXELFORMAT_DEPTHSTENCIL format.
* @param {number} [options.samples] - Number of hardware anti-aliasing samples (WebGL2 only). Default is 1.
* @param {boolean} [options.autoResolve] - If samples > 1, enables or disables automatic MSAA resolve after rendering to this RT (see {@link RenderTarget#resolve}). Defaults to true;
* Defaults to true.
* @param {number} [options.face] - If the colorBuffer parameter is a cubemap, use this option to specify the
* face of the cubemap to render to. Can be:
* @param {string} [options.name] - The name of the render target.
*
* * {@link CUBEFACE_POSX}
* * {@link CUBEFACE_NEGX}
* * {@link CUBEFACE_POSY}
* * {@link CUBEFACE_NEGY}
* * {@link CUBEFACE_POSZ}
* * {@link CUBEFACE_NEGZ}
*
* Defaults to {@link CUBEFACE_POSX}.
* @example
* // Create a 512x512x24-bit render target with a depth buffer
* var colorBuffer = new pc.Texture(graphicsDevice, {
* width: 512,
* height: 512,
* format: pc.PIXELFORMAT_R8_G8_B8
* });
* var renderTarget = new pc.RenderTarget({
* colorBuffer: colorBuffer,
* depth: true
* });
*
* // Set the render target on a layer
* layer.renderTarget = renderTarget;
*/
class RenderTarget {
constructor(options) {
var _arg2 = arguments[1];
var _arg3 = arguments[2];
if (options instanceof GraphicsDevice) {
// old constructor
this._colorBuffer = _arg2;
options = _arg3;
} else {
// new constructor
this._colorBuffer = options.colorBuffer;
}
// mark color buffer texture as render target
if (this._colorBuffer) {
this._colorBuffer._isRenderTarget = true;
}
this._glFrameBuffer = null;
this._glDepthBuffer = null;
// Process optional arguments
options = (options !== undefined) ? options : defaultOptions;
this._depthBuffer = options.depthBuffer;
this._face = (options.face !== undefined) ? options.face : 0;
if (this._depthBuffer) {
var format = this._depthBuffer._format;
if (format === PIXELFORMAT_DEPTH) {
this._depth = true;
this._stencil = false;
} else if (format === PIXELFORMAT_DEPTHSTENCIL) {
this._depth = true;
this._stencil = true;
} else {
// #if _DEBUG
console.warn('Incorrect depthBuffer format. Must be pc.PIXELFORMAT_DEPTH or pc.PIXELFORMAT_DEPTHSTENCIL');
// #endif
this._depth = false;
this._stencil = false;
}
} else {
this._depth = (options.depth !== undefined) ? options.depth : true;
this._stencil = (options.stencil !== undefined) ? options.stencil : false;
}
this._samples = (options.samples !== undefined) ? options.samples : 1;
this.autoResolve = (options.autoResolve !== undefined) ? options.autoResolve : true;
this._glResolveFrameBuffer = null;
this._glMsaaColorBuffer = null;
this._glMsaaDepthBuffer = null;
// use specified name, othewise get one from color or depth buffer
this.name = options.name;
if (!this.name) {
this.name = this._colorBuffer?.name;
}
if (!this.name) {
this.name = this._depthBuffer?.name;
}
if (!this.name) {
this.name = "Untitled";
}
}
/**
* @function
* @name RenderTarget#destroy
* @description Frees resources associated with this render target.
*/
destroy() {
var device = this._device;
if (!device) return;
var idx = device.targets.indexOf(this);
if (idx !== -1) {
device.targets.splice(idx, 1);
}
var gl = device.gl;
if (this._glFrameBuffer) {
gl.deleteFramebuffer(this._glFrameBuffer);
this._glFrameBuffer = null;
}
if (this._glDepthBuffer) {
gl.deleteRenderbuffer(this._glDepthBuffer);
this._glDepthBuffer = null;
}
if (this._glResolveFrameBuffer) {
gl.deleteFramebuffer(this._glResolveFrameBuffer);
this._glResolveFrameBuffer = null;
}
if (this._glMsaaColorBuffer) {
gl.deleteRenderbuffer(this._glMsaaColorBuffer);
this._glMsaaColorBuffer = null;
}
if (this._glMsaaDepthBuffer) {
gl.deleteRenderbuffer(this._glMsaaDepthBuffer);
this._glMsaaDepthBuffer = null;
}
}
// called when context was lost, function releases all context related resources
loseContext() {
this._glFrameBuffer = undefined;
this._glDepthBuffer = undefined;
this._glResolveFrameBuffer = undefined;
this._glMsaaColorBuffer = undefined;
this._glMsaaDepthBuffer = undefined;
}
/**
* @function
* @name RenderTarget#resolve
* @description If samples > 1, resolves the anti-aliased render target (WebGL2 only).
* When you're rendering to an anti-aliased render target, pixels aren't written directly to the readable texture.
* Instead, they're first written to a MSAA buffer, where each sample for each pixel is stored independently.
* In order to read the results, you first need to 'resolve' the buffer - to average all samples and create a simple texture with one color per pixel.
* This function performs this averaging and updates the colorBuffer and the depthBuffer.
* If autoResolve is set to true, the resolve will happen after every rendering to this render target, otherwise you can do it manually,
* during the app update or inside a {@link Command}.
* @param {boolean} [color] - Resolve color buffer. Defaults to true.
* @param {boolean} [depth] - Resolve depth buffer. Defaults to true if the render target has a depth buffer.
*/
resolve(color = true, depth = !!this._depthBuffer) {
if (!this._device) return;
if (!this._device.webgl2) return;
var gl = this._device.gl;
gl.bindFramebuffer(gl.READ_FRAMEBUFFER, this._glFrameBuffer);
gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, this._glResolveFrameBuffer);
gl.blitFramebuffer(0, 0, this.width, this.height,
0, 0, this.width, this.height,
(color ? gl.COLOR_BUFFER_BIT : 0) | (depth ? gl.DEPTH_BUFFER_BIT : 0),
gl.NEAREST);
gl.bindFramebuffer(gl.FRAMEBUFFER, this._glFrameBuffer);
}
/**
* @function
* @name RenderTarget#copy
* @description Copies color and/or depth contents of source render target to this one. Formats, sizes and anti-aliasing samples must match.
* Depth buffer can only be copied on WebGL 2.0.
* @param {RenderTarget} source - Source render target to copy from.
* @param {boolean} [color] - If true will copy the color buffer. Defaults to false.
* @param {boolean} [depth] - If true will copy the depth buffer. Defaults to false.
* @returns {boolean} True if the copy was successful, false otherwise.
*/
copy(source, color, depth) {
if (!this._device) {
if (source._device) {
this._device = source._device;
} else {
// #if _DEBUG
console.error("Render targets are not initialized");
// #endif
return false;
}
}
return this._device.copyRenderTarget(source, this, color, depth);
}
/**
* @readonly
* @name RenderTarget#colorBuffer
* @type {Texture}
* @description Color buffer set up on the render target.
*/
get colorBuffer() {
return this._colorBuffer;
}
/**
* @readonly
* @name RenderTarget#depthBuffer
* @type {Texture}
* @description Depth buffer set up on the render target. Only available, if depthBuffer was set in constructor.
* Not available, if depth property was used instead.
*/
get depthBuffer() {
return this._depthBuffer;
}
/**
* @readonly
* @name RenderTarget#face
* @type {number}
* @description If the render target is bound to a cubemap, this property
* specifies which face of the cubemap is rendered to. Can be:
*
* * {@link CUBEFACE_POSX}
* * {@link CUBEFACE_NEGX}
* * {@link CUBEFACE_POSY}
* * {@link CUBEFACE_NEGY}
* * {@link CUBEFACE_POSZ}
* * {@link CUBEFACE_NEGZ}
*/
get face() {
return this._face;
}
/**
* @readonly
* @name RenderTarget#width
* @type {number}
* @description Width of the render target in pixels.
*/
get width() {
return this._colorBuffer ? this._colorBuffer.width : this._depthBuffer.width;
}
/**
* @readonly
* @name RenderTarget#height
* @type {number}
* @description Height of the render target in pixels.
*/
get height() {
return this._colorBuffer ? this._colorBuffer.height : this._depthBuffer.height;
}
}
export { RenderTarget };