最新代码
This commit is contained in:
193
public/sdk/three/jsm/nodes/pmrem/PMREMNode.js
Normal file
193
public/sdk/three/jsm/nodes/pmrem/PMREMNode.js
Normal file
@ -0,0 +1,193 @@
|
||||
import TempNode from '../core/TempNode.js';
|
||||
import { addNodeClass } from '../core/Node.js';
|
||||
import { texture } from '../accessors/TextureNode.js';
|
||||
import { textureCubeUV } from './PMREMUtils.js';
|
||||
import { uniform } from '../core/UniformNode.js';
|
||||
import { NodeUpdateType } from '../core/constants.js';
|
||||
import { nodeProxy, vec3 } from '../shadernode/ShaderNode.js';
|
||||
import { WebGLCoordinateSystem } from 'three';
|
||||
|
||||
let _generator = null;
|
||||
|
||||
const _cache = new WeakMap();
|
||||
|
||||
function _generateCubeUVSize( imageHeight ) {
|
||||
|
||||
const maxMip = Math.log2( imageHeight ) - 2;
|
||||
|
||||
const texelHeight = 1.0 / imageHeight;
|
||||
|
||||
const texelWidth = 1.0 / ( 3 * Math.max( Math.pow( 2, maxMip ), 7 * 16 ) );
|
||||
|
||||
return { texelWidth, texelHeight, maxMip };
|
||||
|
||||
}
|
||||
|
||||
function _getPMREMFromTexture( texture ) {
|
||||
|
||||
let cacheTexture = _cache.get( texture );
|
||||
|
||||
const pmremVersion = cacheTexture !== undefined ? cacheTexture.pmremVersion : - 1;
|
||||
|
||||
if ( pmremVersion !== texture.pmremVersion ) {
|
||||
|
||||
if ( texture.isCubeTexture ) {
|
||||
|
||||
if ( texture.source.data.some( ( texture ) => texture === undefined ) ) {
|
||||
|
||||
throw new Error( 'PMREMNode: Undefined texture in CubeTexture. Use onLoad callback or async loader' );
|
||||
|
||||
}
|
||||
|
||||
cacheTexture = _generator.fromCubemap( texture, cacheTexture );
|
||||
|
||||
} else {
|
||||
|
||||
if ( texture.image === undefined ) {
|
||||
|
||||
throw new Error( 'PMREMNode: Undefined image in Texture. Use onLoad callback or async loader' );
|
||||
|
||||
}
|
||||
|
||||
cacheTexture = _generator.fromEquirectangular( texture, cacheTexture );
|
||||
|
||||
}
|
||||
|
||||
cacheTexture.pmremVersion = texture.pmremVersion;
|
||||
|
||||
_cache.set( texture, cacheTexture );
|
||||
|
||||
}
|
||||
|
||||
return cacheTexture.texture;
|
||||
|
||||
}
|
||||
|
||||
class PMREMNode extends TempNode {
|
||||
|
||||
constructor( value, uvNode = null, levelNode = null ) {
|
||||
|
||||
super( 'vec3' );
|
||||
|
||||
this._value = value;
|
||||
this._pmrem = null;
|
||||
|
||||
this.uvNode = uvNode;
|
||||
this.levelNode = levelNode;
|
||||
|
||||
this._generator = null;
|
||||
this._texture = texture( null );
|
||||
this._width = uniform( 0 );
|
||||
this._height = uniform( 0 );
|
||||
this._maxMip = uniform( 0 );
|
||||
|
||||
this.updateBeforeType = NodeUpdateType.RENDER;
|
||||
|
||||
}
|
||||
|
||||
set value( value ) {
|
||||
|
||||
this._value = value;
|
||||
this._pmrem = null;
|
||||
|
||||
}
|
||||
|
||||
get value() {
|
||||
|
||||
return this._value;
|
||||
|
||||
}
|
||||
|
||||
updateFromTexture( texture ) {
|
||||
|
||||
const cubeUVSize = _generateCubeUVSize( texture.image.height );
|
||||
|
||||
this._texture.value = texture;
|
||||
this._width.value = cubeUVSize.texelWidth;
|
||||
this._height.value = cubeUVSize.texelHeight;
|
||||
this._maxMip.value = cubeUVSize.maxMip;
|
||||
|
||||
}
|
||||
|
||||
updateBefore() {
|
||||
|
||||
let pmrem = this._pmrem;
|
||||
|
||||
const pmremVersion = pmrem ? pmrem.pmremVersion : - 1;
|
||||
const texture = this._value;
|
||||
|
||||
if ( pmremVersion !== texture.pmremVersion ) {
|
||||
|
||||
if ( texture.isPMREMTexture === true ) {
|
||||
|
||||
pmrem = texture;
|
||||
|
||||
} else {
|
||||
|
||||
pmrem = _getPMREMFromTexture( texture );
|
||||
|
||||
}
|
||||
|
||||
this._pmrem = pmrem;
|
||||
|
||||
this.updateFromTexture( pmrem );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
setup( builder ) {
|
||||
|
||||
if ( _generator === null ) {
|
||||
|
||||
_generator = builder.createPMREMGenerator();
|
||||
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
this.updateBefore( builder );
|
||||
|
||||
//
|
||||
|
||||
let uvNode = this.uvNode;
|
||||
|
||||
if ( uvNode === null && builder.context.getUV ) {
|
||||
|
||||
uvNode = builder.context.getUV( this );
|
||||
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
const texture = this.value;
|
||||
|
||||
if ( builder.renderer.coordinateSystem === WebGLCoordinateSystem && texture.isPMREMTexture !== true && texture.isRenderTargetTexture === true ) {
|
||||
|
||||
uvNode = vec3( uvNode.x.negate(), uvNode.yz );
|
||||
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
let levelNode = this.levelNode;
|
||||
|
||||
if ( levelNode === null && builder.context.getTextureLevel ) {
|
||||
|
||||
levelNode = builder.context.getTextureLevel( this );
|
||||
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
return textureCubeUV( this._texture, uvNode, levelNode, this._width, this._height, this._maxMip );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export const pmremTexture = nodeProxy( PMREMNode );
|
||||
|
||||
addNodeClass( 'PMREMNode', PMREMNode );
|
||||
|
||||
export default PMREMNode;
|
288
public/sdk/three/jsm/nodes/pmrem/PMREMUtils.js
Normal file
288
public/sdk/three/jsm/nodes/pmrem/PMREMUtils.js
Normal file
@ -0,0 +1,288 @@
|
||||
import { tslFn, int, float, vec2, vec3, vec4, If } from '../shadernode/ShaderNode.js';
|
||||
import { cos, sin, abs, max, exp2, log2, clamp, fract, mix, floor, normalize, cross, all } from '../math/MathNode.js';
|
||||
import { mul } from '../math/OperatorNode.js';
|
||||
import { cond } from '../math/CondNode.js';
|
||||
import { loop, Break } from '../utils/LoopNode.js';
|
||||
|
||||
// These defines must match with PMREMGenerator
|
||||
|
||||
const cubeUV_r0 = float( 1.0 );
|
||||
const cubeUV_m0 = float( - 2.0 );
|
||||
const cubeUV_r1 = float( 0.8 );
|
||||
const cubeUV_m1 = float( - 1.0 );
|
||||
const cubeUV_r4 = float( 0.4 );
|
||||
const cubeUV_m4 = float( 2.0 );
|
||||
const cubeUV_r5 = float( 0.305 );
|
||||
const cubeUV_m5 = float( 3.0 );
|
||||
const cubeUV_r6 = float( 0.21 );
|
||||
const cubeUV_m6 = float( 4.0 );
|
||||
|
||||
const cubeUV_minMipLevel = float( 4.0 );
|
||||
const cubeUV_minTileSize = float( 16.0 );
|
||||
|
||||
// These shader functions convert between the UV coordinates of a single face of
|
||||
// a cubemap, the 0-5 integer index of a cube face, and the direction vector for
|
||||
// sampling a textureCube (not generally normalized ).
|
||||
|
||||
const getFace = tslFn( ( [ direction ] ) => {
|
||||
|
||||
const absDirection = vec3( abs( direction ) ).toVar();
|
||||
const face = float( - 1.0 ).toVar();
|
||||
|
||||
If( absDirection.x.greaterThan( absDirection.z ), () => {
|
||||
|
||||
If( absDirection.x.greaterThan( absDirection.y ), () => {
|
||||
|
||||
face.assign( cond( direction.x.greaterThan( 0.0 ), 0.0, 3.0 ) );
|
||||
|
||||
} ).else( () => {
|
||||
|
||||
face.assign( cond( direction.y.greaterThan( 0.0 ), 1.0, 4.0 ) );
|
||||
|
||||
} );
|
||||
|
||||
} ).else( () => {
|
||||
|
||||
If( absDirection.z.greaterThan( absDirection.y ), () => {
|
||||
|
||||
face.assign( cond( direction.z.greaterThan( 0.0 ), 2.0, 5.0 ) );
|
||||
|
||||
} ).else( () => {
|
||||
|
||||
face.assign( cond( direction.y.greaterThan( 0.0 ), 1.0, 4.0 ) );
|
||||
|
||||
} );
|
||||
|
||||
} );
|
||||
|
||||
return face;
|
||||
|
||||
} ).setLayout( {
|
||||
name: 'getFace',
|
||||
type: 'float',
|
||||
inputs: [
|
||||
{ name: 'direction', type: 'vec3' }
|
||||
]
|
||||
} );
|
||||
|
||||
// RH coordinate system; PMREM face-indexing convention
|
||||
const getUV = tslFn( ( [ direction, face ] ) => {
|
||||
|
||||
const uv = vec2().toVar();
|
||||
|
||||
If( face.equal( 0.0 ), () => {
|
||||
|
||||
uv.assign( vec2( direction.z, direction.y ).div( abs( direction.x ) ) ); // pos x
|
||||
|
||||
} ).elseif( face.equal( 1.0 ), () => {
|
||||
|
||||
uv.assign( vec2( direction.x.negate(), direction.z.negate() ).div( abs( direction.y ) ) ); // pos y
|
||||
|
||||
} ).elseif( face.equal( 2.0 ), () => {
|
||||
|
||||
uv.assign( vec2( direction.x.negate(), direction.y ).div( abs( direction.z ) ) ); // pos z
|
||||
|
||||
} ).elseif( face.equal( 3.0 ), () => {
|
||||
|
||||
uv.assign( vec2( direction.z.negate(), direction.y ).div( abs( direction.x ) ) ); // neg x
|
||||
|
||||
} ).elseif( face.equal( 4.0 ), () => {
|
||||
|
||||
uv.assign( vec2( direction.x.negate(), direction.z ).div( abs( direction.y ) ) ); // neg y
|
||||
|
||||
} ).else( () => {
|
||||
|
||||
uv.assign( vec2( direction.x, direction.y ).div( abs( direction.z ) ) ); // neg z
|
||||
|
||||
} );
|
||||
|
||||
return mul( 0.5, uv.add( 1.0 ) );
|
||||
|
||||
} ).setLayout( {
|
||||
name: 'getUV',
|
||||
type: 'vec2',
|
||||
inputs: [
|
||||
{ name: 'direction', type: 'vec3' },
|
||||
{ name: 'face', type: 'float' }
|
||||
]
|
||||
} );
|
||||
|
||||
const roughnessToMip = tslFn( ( [ roughness ] ) => {
|
||||
|
||||
const mip = float( 0.0 ).toVar();
|
||||
|
||||
If( roughness.greaterThanEqual( cubeUV_r1 ), () => {
|
||||
|
||||
mip.assign( cubeUV_r0.sub( roughness ).mul( cubeUV_m1.sub( cubeUV_m0 ) ).div( cubeUV_r0.sub( cubeUV_r1 ) ).add( cubeUV_m0 ) );
|
||||
|
||||
} ).elseif( roughness.greaterThanEqual( cubeUV_r4 ), () => {
|
||||
|
||||
mip.assign( cubeUV_r1.sub( roughness ).mul( cubeUV_m4.sub( cubeUV_m1 ) ).div( cubeUV_r1.sub( cubeUV_r4 ) ).add( cubeUV_m1 ) );
|
||||
|
||||
} ).elseif( roughness.greaterThanEqual( cubeUV_r5 ), () => {
|
||||
|
||||
mip.assign( cubeUV_r4.sub( roughness ).mul( cubeUV_m5.sub( cubeUV_m4 ) ).div( cubeUV_r4.sub( cubeUV_r5 ) ).add( cubeUV_m4 ) );
|
||||
|
||||
} ).elseif( roughness.greaterThanEqual( cubeUV_r6 ), () => {
|
||||
|
||||
mip.assign( cubeUV_r5.sub( roughness ).mul( cubeUV_m6.sub( cubeUV_m5 ) ).div( cubeUV_r5.sub( cubeUV_r6 ) ).add( cubeUV_m5 ) );
|
||||
|
||||
} ).else( () => {
|
||||
|
||||
mip.assign( float( - 2.0 ).mul( log2( mul( 1.16, roughness ) ) ) ); // 1.16 = 1.79^0.25
|
||||
|
||||
} );
|
||||
|
||||
return mip;
|
||||
|
||||
} ).setLayout( {
|
||||
name: 'roughnessToMip',
|
||||
type: 'float',
|
||||
inputs: [
|
||||
{ name: 'roughness', type: 'float' }
|
||||
]
|
||||
} );
|
||||
|
||||
// RH coordinate system; PMREM face-indexing convention
|
||||
export const getDirection = tslFn( ( [ uv_immutable, face ] ) => {
|
||||
|
||||
const uv = uv_immutable.toVar();
|
||||
uv.assign( mul( 2.0, uv ).sub( 1.0 ) );
|
||||
const direction = vec3( uv, 1.0 ).toVar();
|
||||
|
||||
If( face.equal( 0.0 ), () => {
|
||||
|
||||
direction.assign( direction.zyx ); // ( 1, v, u ) pos x
|
||||
|
||||
} ).elseif( face.equal( 1.0 ), () => {
|
||||
|
||||
direction.assign( direction.xzy );
|
||||
direction.xz.mulAssign( - 1.0 ); // ( -u, 1, -v ) pos y
|
||||
|
||||
} ).elseif( face.equal( 2.0 ), () => {
|
||||
|
||||
direction.x.mulAssign( - 1.0 ); // ( -u, v, 1 ) pos z
|
||||
|
||||
} ).elseif( face.equal( 3.0 ), () => {
|
||||
|
||||
direction.assign( direction.zyx );
|
||||
direction.xz.mulAssign( - 1.0 ); // ( -1, v, -u ) neg x
|
||||
|
||||
} ).elseif( face.equal( 4.0 ), () => {
|
||||
|
||||
direction.assign( direction.xzy );
|
||||
direction.xy.mulAssign( - 1.0 ); // ( -u, -1, v ) neg y
|
||||
|
||||
} ).elseif( face.equal( 5.0 ), () => {
|
||||
|
||||
direction.z.mulAssign( - 1.0 ); // ( u, v, -1 ) neg zS
|
||||
|
||||
} );
|
||||
|
||||
return direction;
|
||||
|
||||
} ).setLayout( {
|
||||
name: 'getDirection',
|
||||
type: 'vec3',
|
||||
inputs: [
|
||||
{ name: 'uv', type: 'vec2' },
|
||||
{ name: 'face', type: 'float' }
|
||||
]
|
||||
} );
|
||||
|
||||
//
|
||||
|
||||
export const textureCubeUV = tslFn( ( [ envMap, sampleDir_immutable, roughness_immutable, CUBEUV_TEXEL_WIDTH, CUBEUV_TEXEL_HEIGHT, CUBEUV_MAX_MIP ] ) => {
|
||||
|
||||
const roughness = float( roughness_immutable );
|
||||
const sampleDir = vec3( sampleDir_immutable );
|
||||
|
||||
const mip = clamp( roughnessToMip( roughness ), cubeUV_m0, CUBEUV_MAX_MIP );
|
||||
const mipF = fract( mip );
|
||||
const mipInt = floor( mip );
|
||||
const color0 = vec3( bilinearCubeUV( envMap, sampleDir, mipInt, CUBEUV_TEXEL_WIDTH, CUBEUV_TEXEL_HEIGHT, CUBEUV_MAX_MIP ) ).toVar();
|
||||
|
||||
If( mipF.notEqual( 0.0 ), () => {
|
||||
|
||||
const color1 = vec3( bilinearCubeUV( envMap, sampleDir, mipInt.add( 1.0 ), CUBEUV_TEXEL_WIDTH, CUBEUV_TEXEL_HEIGHT, CUBEUV_MAX_MIP ) ).toVar();
|
||||
|
||||
color0.assign( mix( color0, color1, mipF ) );
|
||||
|
||||
} );
|
||||
|
||||
return color0;
|
||||
|
||||
} );
|
||||
|
||||
const bilinearCubeUV = tslFn( ( [ envMap, direction_immutable, mipInt_immutable, CUBEUV_TEXEL_WIDTH, CUBEUV_TEXEL_HEIGHT, CUBEUV_MAX_MIP ] ) => {
|
||||
|
||||
const mipInt = float( mipInt_immutable ).toVar();
|
||||
const direction = vec3( direction_immutable );
|
||||
const face = float( getFace( direction ) ).toVar();
|
||||
const filterInt = float( max( cubeUV_minMipLevel.sub( mipInt ), 0.0 ) ).toVar();
|
||||
mipInt.assign( max( mipInt, cubeUV_minMipLevel ) );
|
||||
const faceSize = float( exp2( mipInt ) ).toVar();
|
||||
const uv = vec2( getUV( direction, face ).mul( faceSize.sub( 2.0 ) ).add( 1.0 ) ).toVar();
|
||||
|
||||
If( face.greaterThan( 2.0 ), () => {
|
||||
|
||||
uv.y.addAssign( faceSize );
|
||||
face.subAssign( 3.0 );
|
||||
|
||||
} );
|
||||
|
||||
uv.x.addAssign( face.mul( faceSize ) );
|
||||
uv.x.addAssign( filterInt.mul( mul( 3.0, cubeUV_minTileSize ) ) );
|
||||
uv.y.addAssign( mul( 4.0, exp2( CUBEUV_MAX_MIP ).sub( faceSize ) ) );
|
||||
uv.x.mulAssign( CUBEUV_TEXEL_WIDTH );
|
||||
uv.y.mulAssign( CUBEUV_TEXEL_HEIGHT );
|
||||
|
||||
return envMap.uv( uv ).grad( vec2(), vec2() ); // disable anisotropic filtering
|
||||
|
||||
} );
|
||||
|
||||
const getSample = tslFn( ( { envMap, mipInt, outputDirection, theta, axis, CUBEUV_TEXEL_WIDTH, CUBEUV_TEXEL_HEIGHT, CUBEUV_MAX_MIP } ) => {
|
||||
|
||||
const cosTheta = cos( theta );
|
||||
|
||||
// Rodrigues' axis-angle rotation
|
||||
const sampleDirection = outputDirection.mul( cosTheta )
|
||||
.add( axis.cross( outputDirection ).mul( sin( theta ) ) )
|
||||
.add( axis.mul( axis.dot( outputDirection ).mul( cosTheta.oneMinus() ) ) );
|
||||
|
||||
return bilinearCubeUV( envMap, sampleDirection, mipInt, CUBEUV_TEXEL_WIDTH, CUBEUV_TEXEL_HEIGHT, CUBEUV_MAX_MIP );
|
||||
|
||||
} );
|
||||
|
||||
export const blur = tslFn( ( { n, latitudinal, poleAxis, outputDirection, weights, samples, dTheta, mipInt, envMap, CUBEUV_TEXEL_WIDTH, CUBEUV_TEXEL_HEIGHT, CUBEUV_MAX_MIP } ) => {
|
||||
|
||||
const axis = vec3( cond( latitudinal, poleAxis, cross( poleAxis, outputDirection ) ) ).toVar();
|
||||
|
||||
If( all( axis.equals( vec3( 0.0 ) ) ), () => {
|
||||
|
||||
axis.assign( vec3( outputDirection.z, 0.0, outputDirection.x.negate() ) );
|
||||
|
||||
} );
|
||||
|
||||
axis.assign( normalize( axis ) );
|
||||
|
||||
const gl_FragColor = vec3().toVar();
|
||||
gl_FragColor.addAssign( weights.element( int( 0 ) ).mul( getSample( { theta: 0.0, axis, outputDirection, mipInt, envMap, CUBEUV_TEXEL_WIDTH, CUBEUV_TEXEL_HEIGHT, CUBEUV_MAX_MIP } ) ) );
|
||||
|
||||
loop( { start: int( 1 ), end: n }, ( { i } ) => {
|
||||
|
||||
If( i.greaterThanEqual( samples ), () => {
|
||||
|
||||
Break();
|
||||
|
||||
} );
|
||||
|
||||
const theta = float( dTheta.mul( float( i ) ) ).toVar();
|
||||
gl_FragColor.addAssign( weights.element( i ).mul( getSample( { theta: theta.mul( - 1.0 ), axis, outputDirection, mipInt, envMap, CUBEUV_TEXEL_WIDTH, CUBEUV_TEXEL_HEIGHT, CUBEUV_MAX_MIP } ) ) );
|
||||
gl_FragColor.addAssign( weights.element( i ).mul( getSample( { theta, axis, outputDirection, mipInt, envMap, CUBEUV_TEXEL_WIDTH, CUBEUV_TEXEL_HEIGHT, CUBEUV_MAX_MIP } ) ) );
|
||||
|
||||
} );
|
||||
|
||||
return vec4( gl_FragColor, 1 );
|
||||
|
||||
} );
|
Reference in New Issue
Block a user