Skip to content
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

DataUtils: Add toRGB9E5() and fromRGB9E5(). #28012

Merged
merged 2 commits into from
Mar 28, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions docs/api/en/extras/DataUtils.html
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,25 @@ <h3>[method:Number fromHalfFloat]( [param:Number val] )</h3>
precision floating point value.
</p>


<h3>[method:Uint32Array toRGB9E5]( [param:Number r], [param:Number g], [param:Number b], [param:Uint32Array target] )</h3>
<p>
r -- A float representing the R channel.<br />
g -- A float representing the G channel.<br />
b -- A float representing the B channel.<br />
target -- An instance of `Uint32Array` with length `1`.<br /><br />

This method packs three floats into a single Uint32 value which is required for the `RGB9E5` texture format.
</p>

<h3>[method:Array fromRGB9E5]( [param:Uint32Array val], [param:Array target] )</h3>
<p>
val -- An instance of `Uint32Array` with length `1`.<br />
target -- An array holding the three unpacked floats.<br /><br />

This method unpacks three floats from a single Uint32 value holding a `RGB9E5` texel.
</p>

<h2>Source</h2>

<p>
Expand Down
93 changes: 93 additions & 0 deletions src/extras/DataUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -164,13 +164,106 @@ function fromHalfFloat( val ) {

}

// RGB9E5 packing/unpacking

const RGB9E5_MANTISSA_BITS = 9;
const RGB9E5_EXP_BIAS = 15;
const RGB9E5_MAX_VALID_BIASED_EXP = 31;
const MAX_RGB9E5 = ( RGB9E5_MAX_VALID_BIASED_EXP - RGB9E5_EXP_BIAS );
Mugen87 marked this conversation as resolved.
Show resolved Hide resolved
const RGB9E5_MANTISSA_VALUES = ( 1 << RGB9E5_MANTISSA_BITS );
const MAX_RGB9E5_MANTISSA = ( RGB9E5_MANTISSA_VALUES - 1 );

function ClampRange_for_rgb9e5( x ) {

if ( x > 0.0 ) {

if ( x >= MAX_RGB9E5 ) {

return MAX_RGB9E5;

} else {

return x;

}

} else {

/* NaN gets here too since comparisons with NaN always fail! */
return 0.0;

}

}

function FloorLog2( x ) {

return Math.floor( Math.log2( x ) );

}

// reference https://registry.khronos.org/OpenGL/extensions/EXT/EXT_texture_shared_exponent.txt

function toRGB9E5( r, g, b, target ) {

const rc = ClampRange_for_rgb9e5( r );
const gc = ClampRange_for_rgb9e5( g );
const bc = ClampRange_for_rgb9e5( b );

const maxrgb = Math.max( rc, gc, bc );
let exp_shared = Math.max( - RGB9E5_EXP_BIAS - 1, FloorLog2( maxrgb ) ) + 1 + RGB9E5_EXP_BIAS;

let denom = Math.pow( 2, exp_shared - RGB9E5_EXP_BIAS - RGB9E5_MANTISSA_BITS );
const maxm = Math.floor( maxrgb / denom + 0.5 );

if ( maxm == MAX_RGB9E5_MANTISSA + 1 ) {

denom *= 2;
exp_shared += 1;

}

const rm = Math.floor( rc / denom + 0.5 );
const gm = Math.floor( gc / denom + 0.5 );
const bm = Math.floor( bc / denom + 0.5 );

//

target[ 0 ] = ( rm << 0 ) | ( gm << 9 ) | ( bm << 18 ) | ( exp_shared << 27 );

return target;

}

function fromRGB9E5( val, target ) {

const r = ( val[ 0 ] >> 0 ) & 0x1FF;
const g = ( val[ 0 ] >> 9 ) & 0x1FF;
const b = ( val[ 0 ] >> 18 ) & 0x1FF;
const e = ( val[ 0 ] >> 27 ) & 0x01F;

const exponent = e - RGB9E5_EXP_BIAS - RGB9E5_MANTISSA_BITS;
const scale = Math.pow( 2, exponent );

target[ 0 ] = r * scale;
target[ 1 ] = g * scale;
target[ 2 ] = b * scale;

return target;

}

const DataUtils = {
toHalfFloat: toHalfFloat,
fromHalfFloat: fromHalfFloat,
toRGB9E5: toRGB9E5,
fromRGB9E5: fromRGB9E5
};

export {
toHalfFloat,
fromHalfFloat,
toRGB9E5,
fromRGB9E5,
DataUtils
};