-
-
Notifications
You must be signed in to change notification settings - Fork 35.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Load and render an envirornment in hdr format (and file) #5552
Comments
We don't have a loader for hdr files. Added it to #5524. |
Great thanks, any way i can start developing a loader myself ( can also add it as a pull quest if you like) since i need this for a project right now? |
Seems like this is the place to start: |
Thanks mrdoob, i'll try that, |
mrdoob, a further question i think of adding a generic BinaryTextuireLoader (or DataTextureLoader) class (similar to the CompressedTextureLoader class) so that allfurther loaders can etxed it and just use a different parser i have already added the classes (BinaryTextureLoader and RGBELoader which extends it) and use a DataTexture, but how is the DataTexture supposed to be used? ( i have already some code for .obj and .stl 3D loaders as well ) BinaryTextureLoader.js THREE.DataTextureLoader = THREE.BinaryTextureLoader = function () {
// override in sub classes
this._parser = null;
};
THREE.BinaryTextureLoader.prototype = {
constructor: THREE.BinaryTextureLoader,
load: function ( url, onLoad, onError ) {
var scope = this;
var texture = new THREE.DataTexture( );
var loader = new THREE.XHRLoader();
loader.setResponseType( 'arraybuffer' );
loader.load( url, function ( buffer ) {
var texData = scope._parser( buffer );
texture.image.width = texData.width;
texture.image.height = texData.height;
texture.image.data = texData.data;
texture.wrapS = texData.wrapS !== undefined ? texData.wrapS : THREE.ClampToEdgeWrapping;
texture.wrapT = texData.wrapT !== undefined ? texData.wrapT : THREE.ClampToEdgeWrapping;
texture.magFilter = texData.magFilter !== undefined ? texData.magFilter : THREE.LinearFilter;
texture.minFilter = texData.minFilter !== undefined ? texData.minFilter : THREE.LinearMipMapLinearFilter;
texture.anisotropy = texData.anisotropy !== undefined ? texData.anisotropy : 1;
texture.format = texData.format;
texture.type = texData.type;
if ( texData.mipmaps ) {
texture.mipmaps = texData.mipmaps;
}
if ( texData.mipmapCount === 1 ) {
texture.minFilter = THREE.LinearFilter;
}
texture.needsUpdate = true;
if ( onLoad ) onLoad( texture );
} );
return texture;
}
}; RGBELoader (in progress) THREE.HDRLoader = THREE.RGBELoader = function ( manager ) {
this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager;
};
// extend THREE.BinaryTextureLoader
THREE.RGBELoader.prototype = Object.create( THREE.BinaryTextureLoader.prototype );
// adapted from http://www.graphics.cornell.edu/~bjw/rgbe.html
THREE.RGBELoader.prototype._parser = function( buffer ) {
var byteArray = new Uint8Array( buffer ); // byteArray.byteLength;
var
/* return codes for rgbe routines */
RGBE_RETURN_SUCCESS = 0,
RGBE_RETURN_FAILURE = -1,
/* default error routine. change this to change error handling */
rgbe_read_error = 1,
rgbe_write_error = 2,
rgbe_format_error = 3,
rgbe_memory_error = 4,
rgbe_error = function(rgbe_error_code, msg) {
switch (rgbe_error_code) {
case rgbe_read_error: throw new Error("RGBE read error");
break;
case rgbe_write_error: throw new Error("RGBE write error");
break;
case rgbe_format_error: throw new Error("RGBE bad file format: " + msg);
break;
default:
case rgbe_memory_error: throw new Error("RGBE error: " + msg);
}
return RGBE_RETURN_FAILURE;
},
/* offsets to red, green, and blue components in a data (float) pixel */
RGBE_DATA_RED = 0,
RGBE_DATA_GREEN = 1,
RGBE_DATA_BLUE = 2,
/* number of floats per pixel */
RGBE_DATA_SIZE = 3,
rgbe_header_info = {
valid: 0, /* indicate which fields are valid */
programtype: 'RGBE', /* listed at beginning of file to identify it
* after "#?". defaults to "RGBE" */
width: 0, height: 0,
gamma: 1.0, /* image has already been gamma corrected with
* given gamma. defaults to 1.0 (no correction) */
exposure: 1.0 /* a value of 1.0 in an image corresponds to
* <exposure> watts/steradian/m^2.
* defaults to 1.0 */
},
/* flags indicating which fields in an rgbe_header_info are valid */
RGBE_VALID_PROGRAMTYPE = 1,
RGBE_VALID_GAMMA = 2,
RGBE_VALID_EXPOSURE = 4,
/* standard conversion from rgbe to float pixels */
/* note: Ward uses ldexp(col+0.5,exp-(128+8)). However we wanted pixels */
/* in the range [0,1] to map back into the range [0,1]. */
rgbe2float = function( rgb, index, rgbe ) {
var f;
if ( rgbe[3] ) { /*nonzero pixel*/
f = Math.exp(1.0, rgbe[3]-(128+8) );
rgb[index+RGBE_DATA_RED] = rgbe[0] * f;
rgb[index+RGBE_DATA_GREEN] = rgbe[1] * f;
rgb[index+RGBE_DATA_BLUE] = rgbe[2] * f;
}
else {
rgb[index+RGBE_DATA_RED] = rgb[index+RGBE_DATA_GREEN] = rgb[index+RGBE_DATA_BLUE] = 0.0;
}
},
/* minimal header reading. modify if you want to parse more information */
RGBE_ReadHeader = function( buffer, width, height, info ) {
var buf[128], found_format, tempf, i;
found_format = 0;
if ( info ) {
info.valid = 0;
info.programtype[0] = '';
info.gamma = info.exposure = 1.0;
}
if (fgets(buf,sizeof(buf)/sizeof(buf[0]),fp) == NULL)
return rgbe_error(rgbe_read_error,NULL);
if ( (buf[0] != '#')||(buf[1] != '?') ) {
/* if you want to require the magic token then uncomment the next line */
/*return rgbe_error(rgbe_format_error,"bad initial token"); */
}
else if ( info ) {
info.valid |= RGBE_VALID_PROGRAMTYPE;
for (i=0;i<sizeof(info.programtype)-1;i++) {
if ((buf[i+2] == 0) || isspace(buf[i+2]))
break;
info.programtype[i] = buf[i+2];
}
info.programtype[i] = 0;
if (fgets(buf,sizeof(buf)/sizeof(buf[0]),fp) == 0)
return rgbe_error(rgbe_read_error,NULL);
}
for(;;) {
if ((buf[0] == 0)||(buf[0] == '\n'))
return rgbe_error(rgbe_format_error,"no FORMAT specifier found");
else if (strcmp(buf,"FORMAT=32-bit_rle_rgbe\n") == 0)
break; /* format found so break out of loop */
else if (info && (sscanf(buf,"GAMMA=%g",&tempf) == 1)) {
info.gamma = tempf;
info.valid |= RGBE_VALID_GAMMA;
}
else if (info && (sscanf(buf,"EXPOSURE=%g",&tempf) == 1)) {
info.exposure = tempf;
info.valid |= RGBE_VALID_EXPOSURE;
}
if (fgets(buf,sizeof(buf)/sizeof(buf[0]),fp) == 0)
return rgbe_error(rgbe_read_error,NULL);
}
if (fgets(buf,sizeof(buf)/sizeof(buf[0]),fp) == 0)
return rgbe_error(rgbe_read_error,NULL);
if (strcmp(buf,"\n") != 0)
return rgbe_error(rgbe_format_error, "missing blank line after FORMAT specifier");
if (fgets(buf,sizeof(buf)/sizeof(buf[0]),fp) == 0)
return rgbe_error(rgbe_read_error,NULL);
if (sscanf(buf,"-Y %d +X %d",height,width) < 2)
return rgbe_error(rgbe_format_error,"missing image size specifier");
return RGBE_RETURN_SUCCESS;
},
/* simple read routine. will not correctly handle run length encoding */
RGBE_ReadPixels = function( buffer, data, numpixels ) {
var rgbe[4], index = 0;
while( numpixels-- > 0 ) {
if (fread(rgbe, sizeof(rgbe), 1, fp) < 1)
return rgbe_error(rgbe_read_error,NULL);
rgbe2float( data, index, rgbe );
index += RGBE_DATA_SIZE;
}
return RGBE_RETURN_SUCCESS;
},
/* read routine. will handle run length encoding */
RGBE_ReadPixels_RLE = function( buffer, data, scanline_width, num_scanlines ) {
var rgbe[4], scanline_buffer, ptr, ptr_end;
var i, count;
var buf[2];
if ( (scanline_width < 8)||(scanline_width > 0x7fff) )
/* run length encoding is not allowed so read flat*/
return RGBE_ReadPixels(fp,data,scanline_width*num_scanlines);
scanline_buffer = NULL;
/* read in each successive scanline */
while( num_scanlines > 0 ) {
if (fread(rgbe,sizeof(rgbe),1,fp) < 1) {
free(scanline_buffer);
return rgbe_error(rgbe_read_error,NULL);
}
if ((rgbe[0] != 2)||(rgbe[1] != 2)||(rgbe[2] & 0x80)) {
/* this file is not run length encoded */
rgbe2float(&data[0],&data[1],&data[2],rgbe);
data += RGBE_DATA_SIZE;
free(scanline_buffer);
return RGBE_ReadPixels(fp,data,scanline_width*num_scanlines-1);
}
if ((((int)rgbe[2])<<8 | rgbe[3]) != scanline_width) {
free(scanline_buffer);
return rgbe_error(rgbe_format_error,"wrong scanline width");
}
if (scanline_buffer == NULL)
scanline_buffer = (unsigned char *)
malloc(sizeof(unsigned char)*4*scanline_width);
if (scanline_buffer == NULL)
return rgbe_error(rgbe_memory_error,"unable to allocate buffer space");
ptr = &scanline_buffer[0];
/* read each of the four channels for the scanline into the buffer */
for(i=0;i<4;i++) {
ptr_end = &scanline_buffer[(i+1)*scanline_width];
while(ptr < ptr_end) {
if (fread(buf,sizeof(buf[0])*2,1,fp) < 1) {
free(scanline_buffer);
return rgbe_error(rgbe_read_error,NULL);
}
if (buf[0] > 128) {
/* a run of the same value */
count = buf[0]-128;
if ((count == 0)||(count > ptr_end - ptr)) {
free(scanline_buffer);
return rgbe_error(rgbe_format_error,"bad scanline data");
}
while(count-- > 0)
*ptr++ = buf[1];
}
else {
/* a non-run */
count = buf[0];
if ((count == 0)||(count > ptr_end - ptr)) {
free(scanline_buffer);
return rgbe_error(rgbe_format_error,"bad scanline data");
}
*ptr++ = buf[1];
if (--count > 0) {
if (fread(ptr,sizeof(*ptr)*count,1,fp) < 1) {
free(scanline_buffer);
return rgbe_error(rgbe_read_error,NULL);
}
ptr += count;
}
}
}
}
/* now convert data from buffer into floats */
for(i=0;i<scanline_width;i++) {
rgbe[0] = scanline_buffer[i];
rgbe[1] = scanline_buffer[i+scanline_width];
rgbe[2] = scanline_buffer[i+2*scanline_width];
rgbe[3] = scanline_buffer[i+3*scanline_width];
rgbe2float(&data[RGBE_DATA_RED],&data[RGBE_DATA_GREEN],
&data[RGBE_DATA_BLUE],rgbe);
data += RGBE_DATA_SIZE;
}
num_scanlines--;
}
free(scanline_buffer);
return RGBE_RETURN_SUCCESS;
}
RGBE_ReadHeader( buffer, rgbe_header_info );
return {
width: w, height: h,
data: RGBE_ReadPixels_RLE( buffer ),
exposure: rgbe_header_info.exposure
gamma: rgbe_header_info.gamma,
format: THREE.RGBEFormat,
type: THREE.UnsignedByteType
};
;
}; |
That's looking great! |
Good, should i make the TGALoader extend the BinaryTextureLoader as well (all these loaders are binary and only the parsing changes)? Also, the RGBE is dynamic range pixels (this means they are float and not unsigned bytes), hw would this be better handled by Three.js, can a float texture be used, what do you think? In the webgl_hfr example a simple.png is used and any dynamic range is handled by shaders |
That sounds good.
Hmmm, not sure... if we can avoid floating point texture would be better I guess? As far as I understand the |
Hmm, yes any dynamic range be encoded in alpha channel and let the shaders handle it. i can do that This has the following issues however: Any dynamic range already encpded in image is truncated and re-simulated in the shaders |
I don't know either! I've never seen one. |
Created pull request here (#5620) |
…js (plus example), TGALoader.js extends BinaryTextureLoader.js
prev PR closed, new PR here #5624 |
(three.js issue: #5552) add BinaryTextireLoader.js, RGBELoader.js (plus ...
Hello,
i have an .obj format 3D object and an environment in .hdr file format i want to use for a project
How can an hdr file be used with three.js?
Thanks
The text was updated successfully, but these errors were encountered: