-
Notifications
You must be signed in to change notification settings - Fork 1.4k
/
Copy pathplanar-renderer.js
164 lines (133 loc) · 5.75 KB
/
planar-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
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
// How to use:
// - create a reflection entity with a camera component, set up its layers to what you want to reflect.
// - add the planarRenderer script to it, set the sceneCameraEntity to the main camera of the scene.
// - call frameUpdate on the script to update the reflection texture. This needs to be called
// after the main camera properties including the transform has been set already.
// Note: Objects that use the reflected texture cannot be in the layers the reflection camera renders.
var PlanarRenderer = pc.createScript('planarRenderer');
PlanarRenderer.attributes.add('sceneCameraEntity', {
type: 'entity',
description: 'The entity containing the main camera of the scene.'
});
PlanarRenderer.attributes.add('scale', {
title: 'Scale',
description: 'Scale of the texture compared to the render buffer of the main camera.',
type: 'number',
default: 0.5
});
PlanarRenderer.attributes.add('mipmaps', {
title: 'Mipmaps',
description: 'If set to true, mipmaps will be and autogenerated.',
type: 'boolean',
default: false
});
PlanarRenderer.attributes.add('depth', {
title: 'Depth',
description: 'If set to true, depth buffer will be created.',
type: 'boolean',
default: true
});
PlanarRenderer.attributes.add('planePoint', {
type: 'vec3',
default: [0, 0, 0],
title: 'Plane Point',
description: 'Point on a reflection plane.'
});
PlanarRenderer.attributes.add('planeNormal', {
type: 'vec3',
default: [0, 1, 0],
title: 'Plane Normal',
description: 'Normal of a reflection plane.'
});
// initialize code called once per entity
PlanarRenderer.prototype.initialize = function () {
this.plane = new pc.Plane();
this.reflectionMatrix = new pc.Mat4();
// sceneCameraEntity needs to be set
var sceneCamera = this.sceneCameraEntity.camera;
if (!sceneCamera) {
console.error('PlanarRenderer component requires cameraEntity attribute to be set.');
return;
}
// this entity needs to have camera component as well
var planarCamera = this.entity.camera;
if (!planarCamera) {
console.error('PlanarRenderer component requires a camera component on the same entity.');
return;
}
// When the camera is finished rendering, trigger onPlanarPostRender event on the entity.
// This can be listened to by the user, and the resulting texture can be further processed (e.g prefiltered)
this.evtPostRender = this.app.scene.on('postrender', (cameraComponent) => {
if (planarCamera === cameraComponent) {
this.entity.fire('onPlanarPostRender');
}
});
// when the script is destroyed, remove event listeners
this.on('destroy', () => {
this.evtPostRender.off();
});
};
PlanarRenderer.prototype.updateRenderTarget = function () {
// main camera resolution
var sceneCamera = this.sceneCameraEntity.camera;
var sceneCameraWidth = sceneCamera.renderTarget?.width ?? this.app.graphicsDevice.width;
var sceneCameraHeight = sceneCamera.renderTarget?.height ?? this.app.graphicsDevice.height;
// reflection texture resolution
var width = Math.floor(sceneCameraWidth * this.scale);
var height = Math.floor(sceneCameraHeight * this.scale);
// limit maximum texture size
width = Math.min(width, this.app.graphicsDevice.maxTextureSize);
height = Math.min(height, this.app.graphicsDevice.maxTextureSize);
var planarCamera = this.entity.camera;
if (!planarCamera.renderTarget || planarCamera.renderTarget.width !== width || planarCamera.renderTarget.height !== height) {
// destroy old render target
if (planarCamera.renderTarget) {
this.texture.destroy();
planarCamera.renderTarget.destroy();
}
// Create texture render target with specified resolution and mipmap generation
this.texture = new pc.Texture(this.app.graphicsDevice, {
name: `${this.entity.name}:PlanarRenderer-`,
width: width,
height: height,
format: pc.PIXELFORMAT_SRGBA8,
mipmaps: this.mipmaps,
addressU: pc.ADDRESS_CLAMP_TO_EDGE,
addressV: pc.ADDRESS_CLAMP_TO_EDGE,
minFilter: pc.FILTER_LINEAR_MIPMAP_LINEAR,
magFilter: pc.FILTER_LINEAR
});
// render target
var renderTarget = new pc.RenderTarget({
colorBuffer: this.texture,
depth: this.depth
});
planarCamera.renderTarget = renderTarget;
}
};
PlanarRenderer.prototype.frameUpdate = function () {
this.updateRenderTarget();
var planarCamera = this.entity.camera;
if (planarCamera.enabled) {
// update reflection camera orientation by mirroring the scene camera by the plane
this.plane.setFromPointNormal(this.planePoint, this.planeNormal);
this.reflectionMatrix.setReflection(this.plane.normal, this.plane.distance);
var pos = this.sceneCameraEntity.getPosition();
var reflectedPos = this.reflectionMatrix.transformPoint(pos);
var target = pos.clone().add(this.sceneCameraEntity.forward);
var reflectedTarget = this.reflectionMatrix.transformPoint(target);
this.entity.setPosition(reflectedPos);
this.entity.lookAt(reflectedTarget);
// copy other properties from the scene camera
var sceneCamera = this.sceneCameraEntity.camera;
planarCamera.fov = sceneCamera.fov;
planarCamera.orthoHeight = sceneCamera.orthoHeight;
planarCamera.nearClip = sceneCamera.nearClip;
planarCamera.farClip = sceneCamera.farClip;
planarCamera.aperture = sceneCamera.aperture;
planarCamera.sensitivity = sceneCamera.sensitivity;
planarCamera.shutter = sceneCamera.shutter;
return this.texture;
}
return null;
};