forked from gkjohnson/three-gpu-pathtracer
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathHDRImageGenerator.js
152 lines (107 loc) · 3.15 KB
/
HDRImageGenerator.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
import { compress, encode, findTextureMinMax } from '@monogrid/gainmap-js/dist/encode.js';
import { encodeJPEGMetadata } from '../libs/libultrahdr.js';
import { FloatType, LinearSRGBColorSpace, RGBAFormat } from 'three';
export class HDRImageGenerator {
get completeImage() {
return this._lastUrl !== null;
}
constructor( renderer, imageElement = new Image() ) {
this.renderer = renderer;
this.image = imageElement;
this.encoding = false;
this._lastUrl = null;
this._encodingId = - 1;
}
async updateFrom( renderTarget ) {
if ( this.encoding ) {
throw new Error( 'HDRImageGenerator: HDR image already being encoded.' );
}
const renderer = this.renderer;
const buffer = new Float32Array( renderTarget.width * renderTarget.height * 4 );
renderer.readRenderTargetPixels( renderTarget, 0, 0, renderTarget.width, renderTarget.height, buffer );
const imageInformation = {
header: {},
width: renderTarget.width,
height: renderTarget.height,
data: buffer,
format: RGBAFormat,
colorSpace: LinearSRGBColorSpace,
type: FloatType,
};
this._encodingId ++;
this.encoding = true;
const currentId = this._encodingId;
const jpegData = await encodeHDR( imageInformation );
if ( this._encodingId === currentId ) {
if ( this._lastUrl ) {
URL.revokeObjectURL( this._lastUrl );
}
const blob = new Blob( [ jpegData ], { type: 'octet/stream' } );
this._lastUrl = URL.createObjectURL( blob );
this.image.src = this._lastUrl;
this.encoding = false;
}
}
reset() {
if ( this.encoding ) {
this.encoding = false;
this._encodingId ++;
}
if ( this._lastUrl ) {
URL.revokeObjectURL( this._lastUrl );
this.image.src = '';
this._lastUrl = null;
}
}
}
async function encodeHDR( image ) {
// find RAW RGB Max value of a texture
const textureMax = await findTextureMinMax( image );
// Encode the gainmap
const encodingResult = encode( {
image,
// this will encode the full HDR range
maxContentBoost: Math.max.apply( this, textureMax ) || 1
} );
// obtain the RAW RGBA SDR buffer and create an ImageData
const sdrImageData = new ImageData(
encodingResult.sdr.toArray(),
encodingResult.sdr.width,
encodingResult.sdr.height
);
// obtain the RAW RGBA Gain map buffer and create an ImageData
const gainMapImageData = new ImageData(
encodingResult.gainMap.toArray(),
encodingResult.gainMap.width,
encodingResult.gainMap.height
);
// parallel compress the RAW buffers into the specified mimeType
const mimeType = 'image/jpeg';
const quality = 0.9;
const [ sdr, gainMap ] = await Promise.all( [
compress( {
source: sdrImageData,
mimeType,
quality,
flipY: true // output needs to be flipped
} ),
compress( {
source: gainMapImageData,
mimeType,
quality,
flipY: true // output needs to be flipped
} )
] );
// obtain the metadata which will be embedded into
// and XMP tag inside the final JPEG file
const metadata = encodingResult.getMetadata();
// embed the compressed images + metadata into a single
// JPEG file
const jpegBuffer = await encodeJPEGMetadata( {
...encodingResult,
...metadata,
sdr,
gainMap
} );
return jpegBuffer;
}