-
Notifications
You must be signed in to change notification settings - Fork 16
/
Copy pathskybox.js
153 lines (131 loc) · 3.98 KB
/
skybox.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
const createQuad = require('primitive-quad')
const SKYBOX_VERT = require('./shaders/skybox/skybox.vert.js')
const SKYBOX_FRAG = require('./shaders/skybox/skybox.frag.js')
const SKYTEXTURE_VERT = require('./shaders/skybox/sky-env-map.vert.js')
const SKYTEXTURE_FRAG = require('./shaders/skybox/sky-env-map.frag.js')
const Signal = require('signals')
function Skybox(opts) {
this.type = 'Skybox'
this.enabled = true
this.changed = new Signal()
this.rgbm = false
this.backgroundBlur = 0
const ctx = (this._ctx = opts.ctx)
this.texture = null
this.diffuseTexture = null
this._dirtySunPosition = true
this.set(opts)
const skyboxPositions = [[-1, -1], [1, -1], [1, 1], [-1, 1]]
const skyboxFaces = [[0, 1, 2], [0, 2, 3]]
const flags = []
if (ctx.capabilities.maxColorAttachments > 1) {
flags.push('#define USE_DRAW_BUFFERS')
}
this._drawCommand = {
name: 'Skybox.draw',
pipeline: ctx.pipeline({
vert: SKYBOX_VERT,
frag: flags.join('\n') + '\n' + SKYBOX_FRAG,
depthTest: true
}),
attributes: {
aPosition: ctx.vertexBuffer(skyboxPositions)
},
indices: ctx.indexBuffer(skyboxFaces),
uniforms: {
uUseTonemapping: false,
uExposure: 1
}
}
var quad = createQuad()
this._skyTexture = ctx.texture2D({
width: 512,
height: 256,
pixelFormat: this.rgbm ? ctx.PixelFormat.RGBA8 : ctx.PixelFormat.RGBA16F,
encoding: this.rgbm ? ctx.Encoding.RGBM : ctx.Encoding.Linear,
min: ctx.Filter.Linear,
mag: ctx.Filter.Linear
})
this._updateSkyTexture = {
name: 'Skybox.updateSkyTexture',
pass: ctx.pass({
name: 'Skybox.updateSkyTexture',
color: [this._skyTexture],
clearColor: [0, 0, 0, 0]
}),
pipeline: ctx.pipeline({
vert: SKYTEXTURE_VERT,
frag: SKYTEXTURE_FRAG
}),
uniforms: {
uSunPosition: [0, 0, 0]
},
attributes: {
aPosition: ctx.vertexBuffer(quad.positions),
aTexCoord0: ctx.vertexBuffer(quad.uvs)
},
indices: ctx.indexBuffer(quad.cells)
}
}
Skybox.prototype.init = function(entity) {
this.entity = entity
}
Skybox.prototype.set = function(opts) {
Object.assign(this, opts)
if (opts.sunPosition) {
this._dirtySunPosition = true
}
Object.keys(opts).forEach((prop) => this.changed.dispatch(prop))
}
Skybox.prototype.draw = function(camera, opts) {
var ctx = this._ctx
if (!this.texture && this._dirtySunPosition) {
this._dirtySunPosition = false
ctx.submit(this._updateSkyTexture, {
uniforms: {
uSunPosition: this.sunPosition,
uRGBM: this.rgbm
}
})
}
let texture = this.texture || this._skyTexture
let backgroundBlur = 0
if (opts.backgroundMode) {
if (this.backgroundTexture) {
texture = this.backgroundTexture
}
if (this.backgroundBlur > 0) {
backgroundBlur = this.backgroundBlur
if (!this._reflectionProbe) {
this._reflectionProbe = this.entity.renderer.getComponents(
'ReflectionProbe'
)[0]
}
if (this._reflectionProbe) {
texture = this._reflectionProbe._reflectionMap
}
}
}
const postProcessingCmp = camera.entity ? camera.entity.getComponent('PostProcessing') : null
const useTonemapping = !(postProcessingCmp && postProcessingCmp.enabled)
// TODO: can we somehow avoid creating an object every frame here?
ctx.submit(this._drawCommand, {
uniforms: {
uProjectionMatrix: camera.projectionMatrix,
uViewMatrix: camera.viewMatrix,
uModelMatrix: this.entity.transform.modelMatrix,
uEnvMap: texture,
uEnvMapEncoding: texture.encoding,
uOutputEncoding: opts.outputEncoding,
uBackgroundBlur: backgroundBlur,
uUseTonemapping: opts.backgroundMode ? useTonemapping : false,
uExposure: opts.backgroundMode ? camera.exposure : 1
}
})
}
module.exports = function createSkybox(opts) {
if (!opts.sunPosition && !opts.texture) {
throw new Error('Skybox requires either a sunPosition or a texture')
}
return new Skybox(opts)
}