Skip to content

Commit

Permalink
use fromCubeTexture
Browse files Browse the repository at this point in the history
  • Loading branch information
elalish committed Dec 23, 2024
1 parent 2c9e193 commit c825b82
Showing 1 changed file with 14 additions and 182 deletions.
196 changes: 14 additions & 182 deletions packages/model-viewer/src/three-components/TextureUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,10 @@
*/

import {GainMapDecoderMaterial, HDRJPGLoader, QuadRenderer} from '@monogrid/gainmap-js';
import {BackSide, BoxGeometry, CubeCamera, CubeTexture, DataTexture, EquirectangularReflectionMapping, HalfFloatType, LinearSRGBColorSpace, Loader, Mesh, NoBlending, NoToneMapping, RGBAFormat, Scene, ShaderMaterial, SRGBColorSpace, Texture, TextureLoader, Vector3, WebGLCubeRenderTarget, WebGLRenderer} from 'three';
import {CubeCamera, CubeTexture, DataTexture, EquirectangularReflectionMapping, HalfFloatType, LinearSRGBColorSpace, Loader, NoToneMapping, RGBAFormat, Scene, SRGBColorSpace, Texture, TextureLoader, WebGLCubeRenderTarget, WebGLRenderer} from 'three';
import {RGBELoader} from 'three/examples/jsm/loaders/RGBELoader.js';
import {WebGPURenderer} from 'three/webgpu';
//@ts-ignore
import {CubeRenderTarget, WebGPURenderer} from 'three/webgpu';

import {deserializeUrl, timePasses} from '../utilities.js';

Expand All @@ -28,9 +29,6 @@ export interface EnvironmentMapAndSkybox {
}

const GENERATED_SIGMA = 0.04;
// The maximum length of the blur for loop. Smaller sigmas will use fewer
// samples and exit early, but not recompile the shader.
const MAX_SAMPLES = 20;

const HDR_FILE_RE = /\.hdr(\.js)?$/;

Expand All @@ -47,9 +45,6 @@ export default class TextureUtils {

private skyboxCache = new Map<string, Promise<Texture>>();

private blurMaterial: ShaderMaterial|null = null;
private blurScene: Scene|null = null;

constructor(private threeRenderer: WebGPURenderer) {
}

Expand Down Expand Up @@ -224,16 +219,14 @@ export default class TextureUtils {
await timePasses();

const renderer = this.threeRenderer;
const cubeTarget = new WebGLCubeRenderTarget(256, {
const tmpTarget = new WebGLCubeRenderTarget(256, {
generateMipmaps: false,
type: HalfFloatType,
format: RGBAFormat,
colorSpace: LinearSRGBColorSpace,
depthBuffer: true
});
const cubeCamera = new CubeCamera(0.1, 100, cubeTarget);
const generatedEnvironmentMap = cubeCamera.renderTarget.texture;
generatedEnvironmentMap.name = name;
const cubeCamera = new CubeCamera(0.1, 100, tmpTarget);

const outputColorSpace = renderer.outputColorSpace;
const toneMapping = renderer.toneMapping;
Expand All @@ -242,11 +235,19 @@ export default class TextureUtils {

cubeCamera.update(renderer as unknown as WebGLRenderer, scene);

this.blurCubemap(cubeTarget, GENERATED_SIGMA);
const cubeTarget = new CubeRenderTarget(256);
(cubeTarget as any)
.fromCubeTexture(
this.threeRenderer, tmpTarget.texture, GENERATED_SIGMA);

const generatedEnvironmentMap = cubeTarget.texture;
generatedEnvironmentMap.name = name;

renderer.toneMapping = toneMapping;
renderer.outputColorSpace = outputColorSpace;

tmpTarget.dispose();

return generatedEnvironmentMap;
}

Expand Down Expand Up @@ -274,172 +275,6 @@ export default class TextureUtils {
return this.generatedEnvironmentMapAlt;
}

private blurCubemap(cubeTarget: WebGLCubeRenderTarget, sigma: number) {
if (this.blurMaterial == null) {
this.blurMaterial = this.getBlurShader(MAX_SAMPLES);
const box = new BoxGeometry();
const blurMesh = new Mesh(box, this.blurMaterial!);
this.blurScene = new Scene();
this.blurScene.add(blurMesh);
}
const tempTarget = cubeTarget.clone();
this.halfblur(cubeTarget, tempTarget, sigma, 'latitudinal');
this.halfblur(tempTarget, cubeTarget, sigma, 'longitudinal');
// Disposing this target after we're done with it somehow corrupts Safari's
// whole graphics driver. It's random, but occurs more frequently on
// lower-powered GPUs (macbooks with intel graphics, older iPhones). It goes
// beyond just messing up the PMREM, as it also occasionally causes
// visible corruption on the canvas and even on the rest of the page.
/** tempTarget.dispose(); */
}

private halfblur(
targetIn: WebGLCubeRenderTarget, targetOut: WebGLCubeRenderTarget,
sigmaRadians: number, direction: 'latitudinal'|'longitudinal') {
// Number of standard deviations at which to cut off the discrete
// approximation.
const STANDARD_DEVIATIONS = 3;

const pixels = targetIn.width;
const radiansPerPixel = isFinite(sigmaRadians) ?
Math.PI / (2 * pixels) :
2 * Math.PI / (2 * MAX_SAMPLES - 1);
const sigmaPixels = sigmaRadians / radiansPerPixel;
const samples = isFinite(sigmaRadians) ?
1 + Math.floor(STANDARD_DEVIATIONS * sigmaPixels) :
MAX_SAMPLES;

if (samples > MAX_SAMPLES) {
console.warn(`sigmaRadians, ${
sigmaRadians}, is too large and will clip, as it requested ${
samples} samples when the maximum is set to ${MAX_SAMPLES}`);
}

const weights = [];
let sum = 0;

for (let i = 0; i < MAX_SAMPLES; ++i) {
const x = i / sigmaPixels;
const weight = Math.exp(-x * x / 2);
weights.push(weight);

if (i == 0) {
sum += weight;

} else if (i < samples) {
sum += 2 * weight;
}
}

for (let i = 0; i < weights.length; i++) {
weights[i] = weights[i] / sum;
}

const blurUniforms = this.blurMaterial!.uniforms;
blurUniforms['envMap'].value = targetIn.texture;
blurUniforms['samples'].value = samples;
blurUniforms['weights'].value = weights;
blurUniforms['latitudinal'].value = direction === 'latitudinal';
blurUniforms['dTheta'].value = radiansPerPixel;

const cubeCamera = new CubeCamera(0.1, 100, targetOut);
cubeCamera.update(
this.threeRenderer as unknown as WebGLRenderer, this.blurScene!);
}

private getBlurShader(maxSamples: number) {
const weights = new Float32Array(maxSamples);
const poleAxis = new Vector3(0, 1, 0);
const shaderMaterial = new ShaderMaterial({

name: 'SphericalGaussianBlur',

defines: {'n': maxSamples},

uniforms: {
'envMap': {value: null},
'samples': {value: 1},
'weights': {value: weights},
'latitudinal': {value: false},
'dTheta': {value: 0},
'poleAxis': {value: poleAxis}
},

vertexShader: /* glsl */ `
varying vec3 vOutputDirection;
void main() {
vOutputDirection = vec3( position );
gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
}
`,

fragmentShader: /* glsl */ `
varying vec3 vOutputDirection;
uniform samplerCube envMap;
uniform int samples;
uniform float weights[ n ];
uniform bool latitudinal;
uniform float dTheta;
uniform vec3 poleAxis;
vec3 getSample( float theta, vec3 axis ) {
float cosTheta = cos( theta );
// Rodrigues' axis-angle rotation
vec3 sampleDirection = vOutputDirection * cosTheta
+ cross( axis, vOutputDirection ) * sin( theta )
+ axis * dot( axis, vOutputDirection ) * ( 1.0 - cosTheta );
return vec3( textureCube( envMap, sampleDirection ) );
}
void main() {
vec3 axis = latitudinal ? poleAxis : cross( poleAxis, vOutputDirection );
if ( all( equal( axis, vec3( 0.0 ) ) ) ) {
axis = vec3( vOutputDirection.z, 0.0, - vOutputDirection.x );
}
axis = normalize( axis );
gl_FragColor = vec4( 0.0, 0.0, 0.0, 1.0 );
gl_FragColor.rgb += weights[ 0 ] * getSample( 0.0, axis );
for ( int i = 1; i < n; i++ ) {
if ( i >= samples ) {
break;
}
float theta = dTheta * float( i );
gl_FragColor.rgb += weights[ i ] * getSample( -1.0 * theta, axis );
gl_FragColor.rgb += weights[ i ] * getSample( theta, axis );
}
}
`,

blending: NoBlending,
depthTest: false,
depthWrite: false,
side: BackSide

});

return shaderMaterial;
}

async dispose() {
for (const [, promise] of this.skyboxCache) {
const skybox = await promise;
Expand All @@ -453,8 +288,5 @@ export default class TextureUtils {
(await this.generatedEnvironmentMapAlt).dispose();
this.generatedEnvironmentMapAlt = null;
}
if (this.blurMaterial != null) {
this.blurMaterial.dispose();
}
}
}

0 comments on commit c825b82

Please sign in to comment.