|
1 | 1 | import { MeshBasicNodeMaterial, NoToneMapping, StorageTexture } from 'three/webgpu'; |
2 | 2 | import { uv, varying, texture, vec4, toneMapping, uniform, wgslFn } from 'three/tsl'; |
| 3 | +import { wgslTagFn } from '../lib/nodes/WGSLTagFnNode.js'; |
| 4 | + |
| 5 | +// TODO: we could fall back to hardware-based filtering if available but it has to be specifically |
| 6 | +// requested and available on the renderer which we don't have access to immediately. It's possible this |
| 7 | +// can be detected during build time via a custom node to adjust the sample approach used? |
| 8 | +const sampleTexelFn = wgslFn( /* wgsl */` |
| 9 | + fn sampleTexel( tex: texture_2d<f32>, coord: vec2f ) -> vec4f { |
| 10 | +
|
| 11 | + // Manual bilinear filtering using textureLoad to support filterable float32 textures |
| 12 | + // on all devices |
| 13 | + let size = vec2f( textureDimensions( tex, 0 ) ); |
| 14 | + let pxCoord = coord * size - 0.5; |
| 15 | + let px = vec2i( floor( pxCoord ) ); |
| 16 | + let fr = fract( pxCoord ); |
| 17 | +
|
| 18 | + // get the four sibling samples |
| 19 | + let s00 = textureLoad( tex, clamp( px, vec2i( 0 ), vec2i( size ) - 1 ), 0 ); |
| 20 | + let s10 = textureLoad( tex, clamp( px + vec2i( 1, 0 ), vec2i( 0 ), vec2i( size ) - 1 ), 0 ); |
| 21 | + let s01 = textureLoad( tex, clamp( px + vec2i( 0, 1 ), vec2i( 0 ), vec2i( size ) - 1 ), 0 ); |
| 22 | + let s11 = textureLoad( tex, clamp( px + vec2i( 1, 1 ), vec2i( 0 ), vec2i( size ) - 1 ), 0 ); |
| 23 | +
|
| 24 | + // interpolate on the x axis |
| 25 | + let y0 = mix( s00, s10, fr.x ); |
| 26 | + let y1 = mix( s01, s11, fr.x ); |
| 27 | +
|
| 28 | + // then y |
| 29 | + return mix( y0, y1, fr.y ); |
| 30 | +
|
| 31 | + } |
| 32 | +` ); |
3 | 33 |
|
4 | 34 | // Material to apply tone mapping _before_ applying alpha blending |
5 | 35 | export class RenderToScreenNodeMaterial extends MeshBasicNodeMaterial { |
@@ -68,28 +98,41 @@ export class RenderToScreenNodeMaterial extends MeshBasicNodeMaterial { |
68 | 98 |
|
69 | 99 | super(); |
70 | 100 |
|
71 | | - const texNode = texture( new StorageTexture(), varying( uv() ) ); |
| 101 | + const texNode = texture( new StorageTexture() ); |
72 | 102 | this._texNode = texNode; |
73 | 103 |
|
74 | | - const fromTexNode = texture( new StorageTexture(), varying( uv() ) ); |
| 104 | + const fromTexNode = texture( new StorageTexture() ); |
75 | 105 | this._fromTexNode = fromTexNode; |
76 | 106 |
|
| 107 | + const texUV = varying( uv() ); |
| 108 | + |
77 | 109 | const transitionUniform = uniform( 1.0 ); |
78 | 110 | this._transitionUniform = transitionUniform; |
79 | 111 |
|
80 | | - const fadedColor = wgslFn( /* wgsl */` |
81 | | - fn fade( col0: vec4f, col1: vec4f, transition: f32 ) -> vec4f { |
| 112 | + // NOTE: varyings cannot be referenced directly and must be passed as arguments |
| 113 | + const getFadedColorFn = wgslTagFn/* wgsl */` |
| 114 | + fn fade( uv: vec2f ) -> vec4f { |
| 115 | +
|
| 116 | + if ( ${ transitionUniform } <= 0.0 ) { |
| 117 | +
|
| 118 | + return ${ sampleTexelFn }( ${ fromTexNode }, uv ); |
| 119 | +
|
| 120 | + } else if ( ${ transitionUniform } >= 1.0 ) { |
| 121 | +
|
| 122 | + return ${ sampleTexelFn }( ${ texNode }, uv ); |
| 123 | +
|
| 124 | + } else { |
| 125 | +
|
| 126 | + let col0 = ${ sampleTexelFn }( ${ fromTexNode }, uv ); |
| 127 | + let col1 = ${ sampleTexelFn }( ${ texNode }, uv ); |
| 128 | + return mix( col0, col1, ${ transitionUniform } ); |
82 | 129 |
|
83 | | - return mix( col0, col1, transition ); |
| 130 | + } |
84 | 131 |
|
85 | 132 | } |
86 | | - ` )( { |
87 | | - col0: fromTexNode, |
88 | | - col1: texNode, |
89 | | - transition: transitionUniform, |
90 | | - } ); |
| 133 | + `; |
91 | 134 |
|
92 | | - const toneMappingNode = toneMapping( NoToneMapping, 1.0, fadedColor ); |
| 135 | + const toneMappingNode = toneMapping( NoToneMapping, 1.0, getFadedColorFn( texUV ) ); |
93 | 136 | this._toneMapping = toneMappingNode; |
94 | 137 |
|
95 | 138 | // apply alpha _after_ applying tone mapping |
|
0 commit comments