Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
4 changes: 2 additions & 2 deletions examples/integrated/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,8 @@
<script type="importmap">
{
"imports": {
"three": "https://unpkg.com/three@0.181.1/build/three.module.js",
"three/addons/": "https://unpkg.com/three@0.181.1/examples/jsm/",
"three": "https://unpkg.com/three@0.184.0/build/three.module.js",
"three/addons/": "https://unpkg.com/three@0.184.0/examples/jsm/",
"@monogrid/gainmap-js": "https://unpkg.com/@monogrid/gainmap-js/dist/decode.js"
}
}
Expand Down
51 changes: 13 additions & 38 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@
"@semantic-release/npm": "^13.1.1",
"@semantic-release/release-notes-generator": "^14.1.0",
"@types/node": "^22.18.12",
"@types/three": "^0.182.0",
"@types/three": "^0.184.0",
"concurrently": "^9.2.1",
"conventional-changelog-conventionalcommits": "^9.1.0",
"cz-conventional-changelog": "^3.3.0",
Expand All @@ -151,7 +151,7 @@
"semantic-release": "^25.0.1",
"servez": "^2.3.2",
"sharp": "^0.34.4",
"three": "^0.182.0",
"three": "^0.184.0",
"typedoc": "^0.28.14",
"typedoc-github-wiki-theme": "^2.1.0",
"typedoc-plugin-markdown": "^4.9.0",
Expand Down
61 changes: 24 additions & 37 deletions src/decode/webgpu/materials/GainMapDecoderMaterial.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { add, exp2, float, max, min, mul, pow, sub, texture, uniform, vec3 } from 'three/tsl'
import { MeshBasicNodeMaterial, NoBlending, Texture, TextureNode, Vector3 } from 'three/webgpu'
import { texture, uniform, vec3 } from 'three/tsl'
import { MeshBasicNodeMaterial, NoBlending, Texture, TextureNode, UniformNode, Vector3 } from 'three/webgpu'

import { GainMapMetadata } from '../../../core/types'
import { type GainmapDecodingParameters } from '../../shared'
Expand All @@ -21,12 +21,12 @@ export class GainMapDecoderMaterial extends MeshBasicNodeMaterial {
private _hdrCapacityMax: GainMapMetadata['hdrCapacityMax']

// Uniforms for TSL
private _gammaUniform: ReturnType<typeof uniform<Vector3>>
private _offsetHdrUniform: ReturnType<typeof uniform<Vector3>>
private _offsetSdrUniform: ReturnType<typeof uniform<Vector3>>
private _gainMapMinUniform: ReturnType<typeof uniform<Vector3>>
private _gainMapMaxUniform: ReturnType<typeof uniform<Vector3>>
private _weightFactorUniform: ReturnType<typeof uniform<number>>
private _gammaUniform: UniformNode<'vec3', Vector3>
private _offsetHdrUniform: UniformNode<'vec3', Vector3>
private _offsetSdrUniform: UniformNode<'vec3', Vector3>
private _gainMapMinUniform: UniformNode<'vec3', Vector3>
private _gainMapMaxUniform: UniformNode<'vec3', Vector3>
private _weightFactorUniform: UniformNode<'float', number>
private _sdrTexture: TextureNode
private _gainMapTexture: TextureNode

Expand All @@ -45,51 +45,38 @@ export class GainMapDecoderMaterial extends MeshBasicNodeMaterial {
this._sdrTexture = texture(sdr)
this._gainMapTexture = texture(gainMap)

// Create uniform nodes
this._gammaUniform = uniform(vec3(1.0 / gamma[0], 1.0 / gamma[1], 1.0 / gamma[2]))
this._offsetHdrUniform = uniform(vec3(offsetHdr[0], offsetHdr[1], offsetHdr[2]))
this._offsetSdrUniform = uniform(vec3(offsetSdr[0], offsetSdr[1], offsetSdr[2]))
this._gainMapMinUniform = uniform(vec3(gainMapMin[0], gainMapMin[1], gainMapMin[2]))
this._gainMapMaxUniform = uniform(vec3(gainMapMax[0], gainMapMax[1], gainMapMax[2]))
this._gammaUniform = uniform(new Vector3(1.0 / gamma[0], 1.0 / gamma[1], 1.0 / gamma[2]))
this._offsetHdrUniform = uniform(new Vector3(offsetHdr[0], offsetHdr[1], offsetHdr[2]))
this._offsetSdrUniform = uniform(new Vector3(offsetSdr[0], offsetSdr[1], offsetSdr[2]))
this._gainMapMinUniform = uniform(new Vector3(gainMapMin[0], gainMapMin[1], gainMapMin[2]))
this._gainMapMaxUniform = uniform(new Vector3(gainMapMax[0], gainMapMax[1], gainMapMax[2]))

const weightFactor = (Math.log2(maxDisplayBoost) - hdrCapacityMin) / (hdrCapacityMax - hdrCapacityMin)
this._weightFactorUniform = uniform(weightFactor)
this._weightFactorUniform = uniform(weightFactor, 'float')

this._maxDisplayBoost = maxDisplayBoost
this._hdrCapacityMin = hdrCapacityMin
this._hdrCapacityMax = hdrCapacityMax

// Build the TSL shader graph

// Get RGB values
const rgb = this._sdrTexture.rgb
const recovery = this._gainMapTexture.rgb

// Apply gamma correction
const logRecovery = pow(recovery, this._gammaUniform)
const logRecovery = recovery.pow(this._gammaUniform)

// Calculate log boost
// logBoost = gainMapMin * (1.0 - logRecovery) + gainMapMax * logRecovery
const oneMinusLogRecovery = sub(float(1.0), logRecovery)
const logBoost = add(
mul(this._gainMapMinUniform, oneMinusLogRecovery),
mul(this._gainMapMaxUniform, logRecovery)
)
const logBoost = this._gainMapMinUniform.mul(logRecovery.oneMinus())
.add(this._gainMapMaxUniform.mul(logRecovery))

// Calculate HDR color
// hdrColor = (rgb + offsetSdr) * exp2(logBoost * weightFactor) - offsetHdr
const hdrColor = sub(
mul(
add(rgb, this._offsetSdrUniform),
exp2(mul(logBoost, this._weightFactorUniform))
),
this._offsetHdrUniform
)

// Clamp to half float range
const clampedHdrColor = max(HALF_FLOAT_MIN, min(HALF_FLOAT_MAX, hdrColor))

// Set the color output
// Note: standalone exp2() is typed for scalars only in @types/three, so we use
// the mathematically equivalent pow(2, x) which has proper vec3 typings.
const gain = vec3(2).pow(logBoost.mul(this._weightFactorUniform))
const hdrColor = rgb.add(this._offsetSdrUniform).mul(gain).sub(this._offsetHdrUniform)

const clampedHdrColor = hdrColor.min(HALF_FLOAT_MAX).max(HALF_FLOAT_MIN)

this.colorNode = clampedHdrColor
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"sdr":{"metadata":{"version":4.7,"type":"Texture","generator":"Texture.toJSON"},"uuid":"55327fab-b82f-4596-92f7-3477261b561a","name":"","image":"28892867-3d10-4beb-899d-055d9994648f","mapping":300,"channel":0,"repeat":[1,1],"offset":[0,0],"center":[0,0],"rotation":0,"wrap":[1001,1001],"format":1023,"internalFormat":null,"type":1009,"colorSpace":"srgb","minFilter":1008,"magFilter":1006,"anisotropy":1,"flipY":true,"generateMipmaps":true,"premultiplyAlpha":false,"unpackAlignment":4},"gainMap":{"metadata":{"version":4.7,"type":"Texture","generator":"Texture.toJSON"},"uuid":"faa11cf8-81f1-436b-92e6-62526490046a","name":"","image":"369e2641-90ff-4df5-966d-86e5eda0610f","mapping":300,"channel":0,"repeat":[1,1],"offset":[0,0],"center":[0,0],"rotation":0,"wrap":[1001,1001],"format":1023,"internalFormat":null,"type":1009,"colorSpace":"srgb-linear","minFilter":1008,"magFilter":1006,"anisotropy":1,"flipY":true,"generateMipmaps":true,"premultiplyAlpha":false,"unpackAlignment":4},"offsetHdr":[0.015625,0.015625,0.015625],"offsetSdr":[0.015625,0.015625,0.015625],"gainMapMin":[0,0,0],"gainMapMax":[15.9993,15.9993,15.9993],"gamma":[1,1,1],"hdrCapacityMin":0,"hdrCapacityMax":15.9993,"maxDisplayBoost":65504.20944752219}
{"sdr":{"metadata":{"version":4.7,"type":"Texture","generator":"Texture.toJSON"},"uuid":"55327fab-b82f-4596-92f7-3477261b561a","name":"","image":"28892867-3d10-4beb-899d-055d9994648f","mapping":300,"channel":0,"repeat":[1,1],"offset":[0,0],"center":[0,0],"rotation":0,"wrap":[1001,1001],"format":1023,"internalFormat":null,"type":1009,"normalized":false,"colorSpace":"srgb","minFilter":1008,"magFilter":1006,"anisotropy":1,"flipY":true,"generateMipmaps":true,"premultiplyAlpha":false,"unpackAlignment":4},"gainMap":{"metadata":{"version":4.7,"type":"Texture","generator":"Texture.toJSON"},"uuid":"faa11cf8-81f1-436b-92e6-62526490046a","name":"","image":"369e2641-90ff-4df5-966d-86e5eda0610f","mapping":300,"channel":0,"repeat":[1,1],"offset":[0,0],"center":[0,0],"rotation":0,"wrap":[1001,1001],"format":1023,"internalFormat":null,"type":1009,"normalized":false,"colorSpace":"srgb-linear","minFilter":1008,"magFilter":1006,"anisotropy":1,"flipY":true,"generateMipmaps":true,"premultiplyAlpha":false,"unpackAlignment":4},"offsetHdr":[0.015625,0.015625,0.015625],"offsetSdr":[0.015625,0.015625,0.015625],"gainMapMin":[0,0,0],"gainMapMax":[15.9993,15.9993,15.9993],"gamma":[1,1,1],"hdrCapacityMin":0,"hdrCapacityMax":15.9993,"maxDisplayBoost":65504.20944752219}
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"sdr":{"metadata":{"version":4.7,"type":"Texture","generator":"Texture.toJSON"},"uuid":"bd5be313-455c-4d19-8e9d-0b1ae5ba15be","name":"","image":"dcafd4f8-0104-4f6c-9e30-adb38805ea76","mapping":300,"channel":0,"repeat":[1,1],"offset":[0,0],"center":[0,0],"rotation":0,"wrap":[1001,1001],"format":1023,"internalFormat":null,"type":1009,"colorSpace":"srgb","minFilter":1008,"magFilter":1006,"anisotropy":1,"flipY":true,"generateMipmaps":true,"premultiplyAlpha":false,"unpackAlignment":4},"gainMap":{"metadata":{"version":4.7,"type":"Texture","generator":"Texture.toJSON"},"uuid":"ce04ca70-4a4f-4f30-b590-b517582acd54","name":"","image":"77a3b2ff-dda6-451f-962e-077f69061720","mapping":300,"channel":0,"repeat":[1,1],"offset":[0,0],"center":[0,0],"rotation":0,"wrap":[1001,1001],"format":1023,"internalFormat":null,"type":1009,"colorSpace":"srgb-linear","minFilter":1008,"magFilter":1006,"anisotropy":1,"flipY":true,"generateMipmaps":true,"premultiplyAlpha":false,"unpackAlignment":4},"offsetHdr":[0.015625,0.015625,0.015625],"offsetSdr":[0.015625,0.015625,0.015625],"gainMapMin":[0,0,0],"gainMapMax":[15.9993,15.9993,15.9993],"gamma":[1,1,1],"hdrCapacityMin":0,"hdrCapacityMax":15.9993,"maxDisplayBoost":65504.20944752219}
{"sdr":{"metadata":{"version":4.7,"type":"Texture","generator":"Texture.toJSON"},"uuid":"293b482b-4aef-431e-8385-1a21810d2eae","name":"","image":"0d415cfb-bf30-4f8a-a7d1-0f351f3afe45","mapping":300,"channel":0,"repeat":[1,1],"offset":[0,0],"center":[0,0],"rotation":0,"wrap":[1001,1001],"format":1023,"internalFormat":null,"type":1009,"normalized":false,"colorSpace":"srgb","minFilter":1008,"magFilter":1006,"anisotropy":1,"flipY":true,"generateMipmaps":true,"premultiplyAlpha":false,"unpackAlignment":4},"gainMap":{"metadata":{"version":4.7,"type":"Texture","generator":"Texture.toJSON"},"uuid":"e154e70d-535b-4c24-a8b3-0d60521aaa6c","name":"","image":"b01fbc6f-4fbf-41ef-a05a-72977bbdf5dc","mapping":300,"channel":0,"repeat":[1,1],"offset":[0,0],"center":[0,0],"rotation":0,"wrap":[1001,1001],"format":1023,"internalFormat":null,"type":1009,"normalized":false,"colorSpace":"srgb-linear","minFilter":1008,"magFilter":1006,"anisotropy":1,"flipY":true,"generateMipmaps":true,"premultiplyAlpha":false,"unpackAlignment":4},"offsetHdr":[0.015625,0.015625,0.015625],"offsetSdr":[0.015625,0.015625,0.015625],"gainMapMin":[0,0,0],"gainMapMax":[15.9993,15.9993,15.9993],"gamma":[1,1,1],"hdrCapacityMin":0,"hdrCapacityMax":15.9993,"maxDisplayBoost":65504.20944752219}
Loading