Browse Source

WebGPURenderer: Transmission - Backdrop 3/3 (#27880)

* WebGPURenderer: Apply fog before tonemapping and encoding

* TSL: Add `rendererReference`

* PassNode: Add `getViewZNode()`

* FogNode: Add `.getViewZNode()`, removed `mixAssign()`

* TSL: Add `toneMappingExposure`

* PostProcessing: Add `.renderAsync()`

* Add `webgpu_custom_fog_background` example

* cleanup

* cleanup

* improve description

* update example

* Transmission - WIP

* rename

* Revert

* Introduce singleViewportMipTexture for tests and revisions

* revision

* cleanup

* fix circular deps

* update screenshot `webgpu_skinning`

* revision

* copyFramebufferToTexture(): Added support for multisampling

* revision

* revision

* revision

* cleanup

* Fix faceDirection in WebGLBackend if used BackSide

* Added frontFaceCW

* Added webgpu_materials_transmission

* cleanup

* update

* revision

* update webgpu_skinning

* revision getVolumeTransmissionRay

* revision

* revision

* revision

* update screenshot

* cleanup

* update `webgpu_loader_gltf_anisotropy` example

* cleanup

* Update webgpu_compute_geometry.jpg

* update screenshots

* add material.*Node transmission properties
sunag 1 year ago
parent
commit
500f452242
50 changed files with 828 additions and 138 deletions
  1. 2 0
      examples/files.json
  2. 62 0
      examples/jsm/nodes/accessors/MaterialNode.js
  3. 1 1
      examples/jsm/nodes/accessors/TextureSizeNode.js
  4. 0 1
      examples/jsm/nodes/core/NodeBuilder.js
  5. 2 1
      examples/jsm/nodes/core/OutputStructNode.js
  6. 6 0
      examples/jsm/nodes/core/PropertyNode.js
  7. 4 1
      examples/jsm/nodes/display/ViewportTextureNode.js
  8. 161 8
      examples/jsm/nodes/functions/PhysicalLightingModel.js
  9. 5 1
      examples/jsm/nodes/lighting/AnalyticLightNode.js
  10. 11 1
      examples/jsm/nodes/lighting/LightsNode.js
  11. 0 2
      examples/jsm/nodes/materials/MeshNormalNodeMaterial.js
  12. 37 4
      examples/jsm/nodes/materials/MeshPhysicalNodeMaterial.js
  13. 9 3
      examples/jsm/nodes/materials/MeshStandardNodeMaterial.js
  14. 12 36
      examples/jsm/nodes/materials/NodeMaterial.js
  15. 3 3
      examples/jsm/renderers/common/Background.js
  16. 1 1
      examples/jsm/renderers/common/RenderList.js
  17. 95 5
      examples/jsm/renderers/common/Renderer.js
  18. 1 1
      examples/jsm/renderers/common/Textures.js
  19. 1 6
      examples/jsm/renderers/common/extras/PMREMGenerator.js
  20. 31 47
      examples/jsm/renderers/common/nodes/Nodes.js
  21. 1 0
      examples/jsm/renderers/webgl/WebGLBackend.js
  22. 0 3
      examples/jsm/renderers/webgpu/WebGPUBackend.js
  23. 1 0
      examples/jsm/renderers/webgpu/nodes/WGSLNodeBuilder.js
  24. BIN
      examples/screenshots/webgpu_backdrop.jpg
  25. BIN
      examples/screenshots/webgpu_backdrop_area.jpg
  26. BIN
      examples/screenshots/webgpu_compute_geometry.jpg
  27. BIN
      examples/screenshots/webgpu_compute_particles.jpg
  28. BIN
      examples/screenshots/webgpu_compute_particles_rain.jpg
  29. BIN
      examples/screenshots/webgpu_custom_fog_background.jpg
  30. BIN
      examples/screenshots/webgpu_instance_mesh.jpg
  31. BIN
      examples/screenshots/webgpu_instance_points.jpg
  32. BIN
      examples/screenshots/webgpu_lights_custom.jpg
  33. BIN
      examples/screenshots/webgpu_lines_fat.jpg
  34. BIN
      examples/screenshots/webgpu_loader_gltf.jpg
  35. BIN
      examples/screenshots/webgpu_loader_gltf_anisotropy.jpg
  36. BIN
      examples/screenshots/webgpu_loader_gltf_compressed.jpg
  37. BIN
      examples/screenshots/webgpu_loader_gltf_iridescence.jpg
  38. BIN
      examples/screenshots/webgpu_loader_gltf_transmission.jpg
  39. BIN
      examples/screenshots/webgpu_materials_transmission.jpg
  40. BIN
      examples/screenshots/webgpu_postprocessing_anamorphic.jpg
  41. BIN
      examples/screenshots/webgpu_skinning.jpg
  42. BIN
      examples/screenshots/webgpu_skinning_points.jpg
  43. 1 1
      examples/webgl_materials_physical_transmission.html
  44. 2 2
      examples/webgpu_backdrop.html
  45. 2 2
      examples/webgpu_backdrop_area.html
  46. 1 4
      examples/webgpu_loader_gltf_anisotropy.html
  47. 126 0
      examples/webgpu_loader_gltf_transmission.html
  48. 243 0
      examples/webgpu_materials_transmission.html
  49. 6 3
      examples/webgpu_skinning.html
  50. 1 1
      examples/webgpu_sprites.html

+ 2 - 0
examples/files.json

@@ -351,9 +351,11 @@
 		"webgpu_loader_gltf_compressed",
 		"webgpu_loader_gltf_compressed",
 		"webgpu_loader_gltf_iridescence",
 		"webgpu_loader_gltf_iridescence",
 		"webgpu_loader_gltf_sheen",
 		"webgpu_loader_gltf_sheen",
+		"webgpu_loader_gltf_transmission",
 		"webgpu_loader_materialx",
 		"webgpu_loader_materialx",
 		"webgpu_materials",
 		"webgpu_materials",
 		"webgpu_materials_sss",
 		"webgpu_materials_sss",
+		"webgpu_materials_transmission",
 		"webgpu_materials_video",
 		"webgpu_materials_video",
 		"webgpu_materialx_noise",
 		"webgpu_materialx_noise",
 		"webgpu_multiple_rendertargets",
 		"webgpu_multiple_rendertargets",

+ 62 - 0
examples/jsm/nodes/accessors/MaterialNode.js

@@ -99,6 +99,20 @@ class MaterialNode extends Node {
 
 
 			}
 			}
 
 
+		} else if ( scope === MaterialNode.SPECULAR_INTENSITY ) {
+
+			const specularIntensity = this.getFloat( scope );
+
+			if ( material.specularMap ) {
+
+				node = specularIntensity.mul( this.getTexture( scope ).a );
+
+			} else {
+
+				node = specularIntensity;
+
+			}
+
 		} else if ( scope === MaterialNode.ROUGHNESS ) { // TODO: cleanup similar branches
 		} else if ( scope === MaterialNode.ROUGHNESS ) { // TODO: cleanup similar branches
 
 
 			const roughnessNode = this.getFloat( scope );
 			const roughnessNode = this.getFloat( scope );
@@ -258,6 +272,38 @@ class MaterialNode extends Node {
 
 
 			}
 			}
 
 
+		} else if ( scope === MaterialNode.TRANSMISSION ) {
+
+			const transmissionNode = this.getFloat( scope );
+
+			if ( material.transmissionMap ) {
+
+				node = transmissionNode.mul( this.getTexture( scope ).r );
+
+			} else {
+
+				node = transmissionNode;
+
+			}
+
+		} else if ( scope === MaterialNode.THICKNESS ) {
+
+			const thicknessNode = this.getFloat( scope );
+
+			if ( material.thicknessMap ) {
+
+				node = thicknessNode.mul( this.getTexture( scope ).g );
+
+			} else {
+
+				node = thicknessNode;
+
+			}
+
+		} else if ( scope === MaterialNode.IOR ) {
+
+			node = this.getFloat( scope );
+
 		} else {
 		} else {
 
 
 			const outputType = this.getNodeType( builder );
 			const outputType = this.getNodeType( builder );
@@ -278,6 +324,8 @@ MaterialNode.OPACITY = 'opacity';
 MaterialNode.SHININESS = 'shininess';
 MaterialNode.SHININESS = 'shininess';
 MaterialNode.SPECULAR_COLOR = 'specular';
 MaterialNode.SPECULAR_COLOR = 'specular';
 MaterialNode.SPECULAR_STRENGTH = 'specularStrength';
 MaterialNode.SPECULAR_STRENGTH = 'specularStrength';
+MaterialNode.SPECULAR_INTENSITY = 'specularIntensity';
+MaterialNode.SPECULAR_COLOR2 = 'specularColor';
 MaterialNode.REFLECTIVITY = 'reflectivity';
 MaterialNode.REFLECTIVITY = 'reflectivity';
 MaterialNode.ROUGHNESS = 'roughness';
 MaterialNode.ROUGHNESS = 'roughness';
 MaterialNode.METALNESS = 'metalness';
 MaterialNode.METALNESS = 'metalness';
@@ -293,6 +341,11 @@ MaterialNode.ANISOTROPY = 'anisotropy';
 MaterialNode.IRIDESCENCE = 'iridescence';
 MaterialNode.IRIDESCENCE = 'iridescence';
 MaterialNode.IRIDESCENCE_IOR = 'iridescenceIOR';
 MaterialNode.IRIDESCENCE_IOR = 'iridescenceIOR';
 MaterialNode.IRIDESCENCE_THICKNESS = 'iridescenceThickness';
 MaterialNode.IRIDESCENCE_THICKNESS = 'iridescenceThickness';
+MaterialNode.IOR = 'ior';
+MaterialNode.TRANSMISSION = 'transmission';
+MaterialNode.THICKNESS = 'thickness';
+MaterialNode.ATTENUATION_DISTANCE = 'attenuationDistance';
+MaterialNode.ATTENUATION_COLOR = 'attenuationColor';
 MaterialNode.LINE_SCALE = 'scale';
 MaterialNode.LINE_SCALE = 'scale';
 MaterialNode.LINE_DASH_SIZE = 'dashSize';
 MaterialNode.LINE_DASH_SIZE = 'dashSize';
 MaterialNode.LINE_GAP_SIZE = 'gapSize';
 MaterialNode.LINE_GAP_SIZE = 'gapSize';
@@ -308,6 +361,10 @@ export const materialShininess = nodeImmutable( MaterialNode, MaterialNode.SHINI
 export const materialEmissive = nodeImmutable( MaterialNode, MaterialNode.EMISSIVE );
 export const materialEmissive = nodeImmutable( MaterialNode, MaterialNode.EMISSIVE );
 export const materialOpacity = nodeImmutable( MaterialNode, MaterialNode.OPACITY );
 export const materialOpacity = nodeImmutable( MaterialNode, MaterialNode.OPACITY );
 export const materialSpecularColor = nodeImmutable( MaterialNode, MaterialNode.SPECULAR_COLOR );
 export const materialSpecularColor = nodeImmutable( MaterialNode, MaterialNode.SPECULAR_COLOR );
+
+export const materialSpecularIntensity = nodeImmutable( MaterialNode, MaterialNode.SPECULAR_INTENSITY );
+export const materialSpecularColor2 = nodeImmutable( MaterialNode, MaterialNode.SPECULAR_COLOR2 );
+
 export const materialSpecularStrength = nodeImmutable( MaterialNode, MaterialNode.SPECULAR_STRENGTH );
 export const materialSpecularStrength = nodeImmutable( MaterialNode, MaterialNode.SPECULAR_STRENGTH );
 export const materialReflectivity = nodeImmutable( MaterialNode, MaterialNode.REFLECTIVITY );
 export const materialReflectivity = nodeImmutable( MaterialNode, MaterialNode.REFLECTIVITY );
 export const materialRoughness = nodeImmutable( MaterialNode, MaterialNode.ROUGHNESS );
 export const materialRoughness = nodeImmutable( MaterialNode, MaterialNode.ROUGHNESS );
@@ -323,6 +380,11 @@ export const materialAnisotropy = nodeImmutable( MaterialNode, MaterialNode.ANIS
 export const materialIridescence = nodeImmutable( MaterialNode, MaterialNode.IRIDESCENCE );
 export const materialIridescence = nodeImmutable( MaterialNode, MaterialNode.IRIDESCENCE );
 export const materialIridescenceIOR = nodeImmutable( MaterialNode, MaterialNode.IRIDESCENCE_IOR );
 export const materialIridescenceIOR = nodeImmutable( MaterialNode, MaterialNode.IRIDESCENCE_IOR );
 export const materialIridescenceThickness = nodeImmutable( MaterialNode, MaterialNode.IRIDESCENCE_THICKNESS );
 export const materialIridescenceThickness = nodeImmutable( MaterialNode, MaterialNode.IRIDESCENCE_THICKNESS );
+export const materialTransmission = nodeImmutable( MaterialNode, MaterialNode.TRANSMISSION );
+export const materialThickness = nodeImmutable( MaterialNode, MaterialNode.THICKNESS );
+export const materialIOR = nodeImmutable( MaterialNode, MaterialNode.IOR );
+export const materialAttenuationDistance = nodeImmutable( MaterialNode, MaterialNode.ATTENUATION_DISTANCE );
+export const materialAttenuationColor = nodeImmutable( MaterialNode, MaterialNode.ATTENUATION_COLOR );
 export const materialLineScale = nodeImmutable( MaterialNode, MaterialNode.LINE_SCALE );
 export const materialLineScale = nodeImmutable( MaterialNode, MaterialNode.LINE_SCALE );
 export const materialLineDashSize = nodeImmutable( MaterialNode, MaterialNode.LINE_DASH_SIZE );
 export const materialLineDashSize = nodeImmutable( MaterialNode, MaterialNode.LINE_DASH_SIZE );
 export const materialLineGapSize = nodeImmutable( MaterialNode, MaterialNode.LINE_GAP_SIZE );
 export const materialLineGapSize = nodeImmutable( MaterialNode, MaterialNode.LINE_GAP_SIZE );

+ 1 - 1
examples/jsm/nodes/accessors/TextureSizeNode.js

@@ -20,7 +20,7 @@ class TextureSizeNode extends Node {
 		const textureProperty = this.textureNode.build( builder, 'property' );
 		const textureProperty = this.textureNode.build( builder, 'property' );
 		const levelNode = this.levelNode.build( builder, 'int' );
 		const levelNode = this.levelNode.build( builder, 'int' );
 
 
-		return builder.format( `${builder.getMethod( 'textureDimensions' )}( ${textureProperty}, ${levelNode} )`, this.getNodeType( builder ), output );
+		return builder.format( `${ builder.getMethod( 'textureDimensions' ) }( ${ textureProperty }, ${ levelNode } )`, this.getNodeType( builder ), output );
 
 
 	}
 	}
 
 

+ 0 - 1
examples/jsm/nodes/core/NodeBuilder.js

@@ -72,7 +72,6 @@ class NodeBuilder {
 		this.lightsNode = null;
 		this.lightsNode = null;
 		this.environmentNode = null;
 		this.environmentNode = null;
 		this.fogNode = null;
 		this.fogNode = null;
-		this.toneMappingNode = null;
 
 
 		this.clippingContext = null;
 		this.clippingContext = null;
 
 

+ 2 - 1
examples/jsm/nodes/core/OutputStructNode.js

@@ -8,9 +8,10 @@ class OutputStructNode extends Node {
 
 
 		super();
 		super();
 
 
-		this.isOutputStructNode = true;
 		this.members = members;
 		this.members = members;
 
 
+		this.isOutputStructNode = true;
+
 	}
 	}
 
 
 	setup( builder ) {
 	setup( builder ) {

+ 6 - 0
examples/jsm/nodes/core/PropertyNode.js

@@ -67,10 +67,16 @@ export const anisotropy = nodeImmutable( PropertyNode, 'float', 'Anisotropy' );
 export const anisotropyT = nodeImmutable( PropertyNode, 'vec3', 'AnisotropyT' );
 export const anisotropyT = nodeImmutable( PropertyNode, 'vec3', 'AnisotropyT' );
 export const anisotropyB = nodeImmutable( PropertyNode, 'vec3', 'AnisotropyB' );
 export const anisotropyB = nodeImmutable( PropertyNode, 'vec3', 'AnisotropyB' );
 export const specularColor = nodeImmutable( PropertyNode, 'color', 'SpecularColor' );
 export const specularColor = nodeImmutable( PropertyNode, 'color', 'SpecularColor' );
+export const specularF90 = nodeImmutable( PropertyNode, 'float', 'SpecularF90' );
 export const shininess = nodeImmutable( PropertyNode, 'float', 'Shininess' );
 export const shininess = nodeImmutable( PropertyNode, 'float', 'Shininess' );
 export const output = nodeImmutable( PropertyNode, 'vec4', 'Output' );
 export const output = nodeImmutable( PropertyNode, 'vec4', 'Output' );
 export const dashSize = nodeImmutable( PropertyNode, 'float', 'dashSize' );
 export const dashSize = nodeImmutable( PropertyNode, 'float', 'dashSize' );
 export const gapSize = nodeImmutable( PropertyNode, 'float', 'gapSize' );
 export const gapSize = nodeImmutable( PropertyNode, 'float', 'gapSize' );
 export const pointWidth = nodeImmutable( PropertyNode, 'float', 'pointWidth' );
 export const pointWidth = nodeImmutable( PropertyNode, 'float', 'pointWidth' );
+export const ior = nodeImmutable( PropertyNode, 'float', 'IOR' );
+export const transmission = nodeImmutable( PropertyNode, 'float', 'Transmission' );
+export const thickness = nodeImmutable( PropertyNode, 'float', 'Thickness' );
+export const attenuationDistance = nodeImmutable( PropertyNode, 'float', 'AttenuationDistance' );
+export const attenuationColor = nodeImmutable( PropertyNode, 'color', 'AttenuationColor' );
 
 
 addNodeClass( 'PropertyNode', PropertyNode );
 addNodeClass( 'PropertyNode', PropertyNode );

+ 4 - 1
examples/jsm/nodes/display/ViewportTextureNode.js

@@ -58,7 +58,10 @@ class ViewportTextureNode extends TextureNode {
 
 
 	clone() {
 	clone() {
 
 
-		return new this.constructor( this.uvNode, this.levelNode, this.value );
+		const viewportTextureNode = new this.constructor( this.uvNode, this.levelNode, this.value );
+		viewportTextureNode.generateMipmaps = this.generateMipmaps;
+
+		return viewportTextureNode;
 
 
 	}
 	}
 
 

+ 161 - 8
examples/jsm/nodes/functions/PhysicalLightingModel.js

@@ -6,12 +6,135 @@ import F_Schlick from './BSDF/F_Schlick.js';
 import Schlick_to_F0 from './BSDF/Schlick_to_F0.js';
 import Schlick_to_F0 from './BSDF/Schlick_to_F0.js';
 import BRDF_Sheen from './BSDF/BRDF_Sheen.js';
 import BRDF_Sheen from './BSDF/BRDF_Sheen.js';
 import LightingModel from '../core/LightingModel.js';
 import LightingModel from '../core/LightingModel.js';
-import { diffuseColor, specularColor, roughness, clearcoat, clearcoatRoughness, sheen, sheenRoughness, iridescence, iridescenceIOR, iridescenceThickness } from '../core/PropertyNode.js';
-import { transformedNormalView, transformedClearcoatNormalView } from '../accessors/NormalNode.js';
-import { positionViewDirection } from '../accessors/PositionNode.js';
-import { tslFn, float, vec3, mat3 } from '../shadernode/ShaderNode.js';
+import { diffuseColor, specularColor, specularF90, roughness, clearcoat, clearcoatRoughness, sheen, sheenRoughness, iridescence, iridescenceIOR, iridescenceThickness, ior, thickness, transmission, attenuationDistance, attenuationColor } from '../core/PropertyNode.js';
+import { transformedNormalView, transformedClearcoatNormalView, transformedNormalWorld } from '../accessors/NormalNode.js';
+import { positionViewDirection, positionWorld } from '../accessors/PositionNode.js';
+import { tslFn, float, vec2, vec3, vec4, mat3, If } from '../shadernode/ShaderNode.js';
 import { cond } from '../math/CondNode.js';
 import { cond } from '../math/CondNode.js';
-import { mix, smoothstep } from '../math/MathNode.js';
+import { mix, normalize, refract, length, clamp, log2, log, exp, smoothstep } from '../math/MathNode.js';
+import { div } from '../math/OperatorNode.js';
+import { cameraPosition, cameraProjectionMatrix, cameraViewMatrix } from '../accessors/CameraNode.js';
+import { modelWorldMatrix } from '../accessors/ModelNode.js';
+import { viewportResolution } from '../display/ViewportNode.js';
+import { viewportMipTexture } from '../display/ViewportTextureNode.js';
+
+//
+// Transmission
+//
+
+const getVolumeTransmissionRay = tslFn( ( [ n, v, thickness, ior, modelMatrix ] ) => {
+
+	// Direction of refracted light.
+	const refractionVector = vec3( refract( v.negate(), normalize( n ), div( 1.0, ior ) ) );
+
+	// Compute rotation-independant scaling of the model matrix.
+	const modelScale = vec3(
+		length( modelMatrix[ 0 ].xyz ),
+		length( modelMatrix[ 1 ].xyz ),
+		length( modelMatrix[ 2 ].xyz )
+	);
+
+	// The thickness is specified in local space.
+	return normalize( refractionVector ).mul( thickness.mul( modelScale ) );
+
+} ).setLayout( {
+	name: 'getVolumeTransmissionRay',
+	type: 'vec3',
+	inputs: [
+		{ name: 'n', type: 'vec3' },
+		{ name: 'v', type: 'vec3' },
+		{ name: 'thickness', type: 'float' },
+		{ name: 'ior', type: 'float' },
+		{ name: 'modelMatrix', type: 'mat4' }
+	]
+} );
+
+const applyIorToRoughness = tslFn( ( [ roughness, ior ] ) => {
+
+	// Scale roughness with IOR so that an IOR of 1.0 results in no microfacet refraction and
+	// an IOR of 1.5 results in the default amount of microfacet refraction.
+	return roughness.mul( clamp( ior.mul( 2.0 ).sub( 2.0 ), 0.0, 1.0 ) );
+
+} ).setLayout( {
+	name: 'applyIorToRoughness',
+	type: 'float',
+	inputs: [
+		{ name: 'roughness', type: 'float' },
+		{ name: 'ior', type: 'float' }
+	]
+} );
+
+const singleViewportMipTexture = viewportMipTexture();
+
+const getTransmissionSample = tslFn( ( [ fragCoord, roughness, ior ] ) => {
+
+	const transmissionSample = singleViewportMipTexture.uv( fragCoord );
+	//const transmissionSample = viewportMipTexture( fragCoord );
+
+	const lod = log2( float( viewportResolution.x ) ).mul( applyIorToRoughness( roughness, ior ) );
+
+	return transmissionSample.bicubic( lod );
+
+} );
+
+const volumeAttenuation = tslFn( ( [ transmissionDistance, attenuationColor, attenuationDistance ] ) => {
+
+	If( attenuationDistance.notEqual( 0 ), () => {
+
+		// Compute light attenuation using Beer's law.
+		const attenuationCoefficient = log( attenuationColor ).negate().div( attenuationDistance );
+		const transmittance = exp( attenuationCoefficient.negate().mul( transmissionDistance ) );
+
+		return transmittance;
+
+	} );
+
+	// Attenuation distance is +∞, i.e. the transmitted color is not attenuated at all.
+	return vec3( 1.0 );
+
+} ).setLayout( {
+	name: 'volumeAttenuation',
+	type: 'vec3',
+	inputs: [
+		{ name: 'transmissionDistance', type: 'float' },
+		{ name: 'attenuationColor', type: 'vec3' },
+		{ name: 'attenuationDistance', type: 'float' }
+	]
+} );
+
+const getIBLVolumeRefraction = tslFn( ( [ n, v, roughness, diffuseColor, specularColor, specularF90, position, modelMatrix, viewMatrix, projMatrix, ior, thickness, attenuationColor, attenuationDistance ] ) => {
+
+	const transmissionRay = getVolumeTransmissionRay( n, v, thickness, ior, modelMatrix );
+	const refractedRayExit = position.add( transmissionRay );
+
+	// Project refracted vector on the framebuffer, while mapping to normalized device coordinates.
+	const ndcPos = projMatrix.mul( viewMatrix.mul( vec4( refractedRayExit, 1.0 ) ) );
+	const refractionCoords = vec2( ndcPos.xy.div( ndcPos.w ) ).toVar();
+	refractionCoords.addAssign( 1.0 );
+	refractionCoords.divAssign( 2.0 );
+	refractionCoords.assign( vec2( refractionCoords.x, refractionCoords.y.oneMinus() ) ); // webgpu
+
+	// Sample framebuffer to get pixel the refracted ray hits.
+	const transmittedLight = getTransmissionSample( refractionCoords, roughness, ior );
+	const transmittance = diffuseColor.mul( volumeAttenuation( length( transmissionRay ), attenuationColor, attenuationDistance ) );
+	const attenuatedColor = transmittance.rgb.mul( transmittedLight.rgb );
+	const dotNV = n.dot( v ).clamp();
+
+	// Get the specular component.
+	const F = vec3( EnvironmentBRDF( { // n, v, specularColor, specularF90, roughness
+		dotNV,
+		specularColor,
+		specularF90,
+		roughness
+	} ) );
+
+	// As less light is transmitted, the opacity should be increased. This simple approximation does a decent job
+	// of modulating a CSS background, and has no effect when the buffer is opaque, due to a solid object or clear color.
+	const transmittanceFactor = transmittance.r.add( transmittance.g, transmittance.b ).div( 3.0 );
+
+	return vec4( F.oneMinus().mul( attenuatedColor ), transmittedLight.a.oneMinus().mul( transmittanceFactor ).oneMinus() );
+
+} );
 
 
 //
 //
 // Iridescence
 // Iridescence
@@ -172,7 +295,7 @@ const clearcoatF90 = vec3( 1 );
 
 
 class PhysicalLightingModel extends LightingModel {
 class PhysicalLightingModel extends LightingModel {
 
 
-	constructor( clearcoat = false, sheen = false, iridescence = false, anisotropy = false ) {
+	constructor( clearcoat = false, sheen = false, iridescence = false, anisotropy = false, transmission = false ) {
 
 
 		super();
 		super();
 
 
@@ -180,6 +303,7 @@ class PhysicalLightingModel extends LightingModel {
 		this.sheen = sheen;
 		this.sheen = sheen;
 		this.iridescence = iridescence;
 		this.iridescence = iridescence;
 		this.anisotropy = anisotropy;
 		this.anisotropy = anisotropy;
+		this.transmission = transmission;
 
 
 		this.clearcoatRadiance = null;
 		this.clearcoatRadiance = null;
 		this.clearcoatSpecularDirect = null;
 		this.clearcoatSpecularDirect = null;
@@ -191,7 +315,7 @@ class PhysicalLightingModel extends LightingModel {
 
 
 	}
 	}
 
 
-	start( /*context*/ ) {
+	start( context ) {
 
 
 		if ( this.clearcoat === true ) {
 		if ( this.clearcoat === true ) {
 
 
@@ -224,6 +348,35 @@ class PhysicalLightingModel extends LightingModel {
 
 
 		}
 		}
 
 
+		if ( this.transmission === true ) {
+
+			const position = positionWorld;
+			const v = cameraPosition.sub( positionWorld ).normalize(); // TODO: Create Node for this, same issue in MaterialX
+			const n = transformedNormalWorld;
+
+			context.backdrop = getIBLVolumeRefraction(
+				n,
+				v,
+				roughness,
+				diffuseColor,
+				specularColor,
+				specularF90, // specularF90
+				position, // positionWorld
+				modelWorldMatrix, // modelMatrix
+				cameraViewMatrix, // viewMatrix
+				cameraProjectionMatrix, // projMatrix
+				ior,
+				thickness,
+				attenuationColor,
+				attenuationDistance
+			);
+
+			context.backdropAlpha = transmission;
+
+			diffuseColor.a.mulAssign( mix( 1, context.backdrop.a, transmission ) );
+
+		}
+
 	}
 	}
 
 
 	// Fdez-Agüera's "Multiple-Scattering Microfacet Model for Real-Time Image Based Lighting"
 	// Fdez-Agüera's "Multiple-Scattering Microfacet Model for Real-Time Image Based Lighting"
@@ -319,7 +472,7 @@ class PhysicalLightingModel extends LightingModel {
 		const multiScattering = vec3().temp( 'multiScattering' );
 		const multiScattering = vec3().temp( 'multiScattering' );
 		const cosineWeightedIrradiance = iblIrradiance.mul( 1 / Math.PI );
 		const cosineWeightedIrradiance = iblIrradiance.mul( 1 / Math.PI );
 
 
-		this.computeMultiscattering( singleScattering, multiScattering );
+		this.computeMultiscattering( singleScattering, multiScattering, specularF90 );
 
 
 		const totalScattering = singleScattering.add( multiScattering );
 		const totalScattering = singleScattering.add( multiScattering );
 
 

+ 5 - 1
examples/jsm/nodes/lighting/AnalyticLightNode.js

@@ -10,7 +10,7 @@ import { normalWorld } from '../accessors/NormalNode.js';
 import { WebGPUCoordinateSystem } from 'three';
 import { WebGPUCoordinateSystem } from 'three';
 //import { add } from '../math/OperatorNode.js';
 //import { add } from '../math/OperatorNode.js';
 
 
-import { Color, DepthTexture, NearestFilter, LessCompare } from 'three';
+import { Color, DepthTexture, NearestFilter, LessCompare, NoToneMapping } from 'three';
 
 
 let overrideMaterial = null;
 let overrideMaterial = null;
 
 
@@ -183,6 +183,7 @@ class AnalyticLightNode extends LightingNode {
 
 
 		light.shadow.updateMatrices( light );
 		light.shadow.updateMatrices( light );
 
 
+		const currentToneMapping = renderer.toneMapping;
 		const currentRenderTarget = renderer.getRenderTarget();
 		const currentRenderTarget = renderer.getRenderTarget();
 		const currentRenderObjectFunction = renderer.getRenderObjectFunction();
 		const currentRenderObjectFunction = renderer.getRenderObjectFunction();
 
 
@@ -197,12 +198,15 @@ class AnalyticLightNode extends LightingNode {
 		} );
 		} );
 
 
 		renderer.setRenderTarget( rtt );
 		renderer.setRenderTarget( rtt );
+		renderer.toneMapping = NoToneMapping;
 
 
 		renderer.render( scene, light.shadow.camera );
 		renderer.render( scene, light.shadow.camera );
 
 
 		renderer.setRenderTarget( currentRenderTarget );
 		renderer.setRenderTarget( currentRenderTarget );
 		renderer.setRenderObjectFunction( currentRenderObjectFunction );
 		renderer.setRenderObjectFunction( currentRenderObjectFunction );
 
 
+		renderer.toneMapping = currentToneMapping;
+
 		scene.overrideMaterial = currentOverrideMaterial;
 		scene.overrideMaterial = currentOverrideMaterial;
 
 
 	}
 	}

+ 11 - 1
examples/jsm/nodes/lighting/LightsNode.js

@@ -95,7 +95,17 @@ class LightsNode extends Node {
 
 
 			if ( backdrop !== null ) {
 			if ( backdrop !== null ) {
 
 
-				totalDiffuse = vec3( backdropAlpha !== null ? backdropAlpha.mix( totalDiffuse, backdrop ) : backdrop );
+				if ( backdropAlpha !== null ) {
+
+					totalDiffuse = vec3( backdropAlpha.mix( totalDiffuse, backdrop ) );
+
+				} else {
+
+					totalDiffuse = vec3( backdrop );
+
+				}
+
+				context.material.transparent = true;
 
 
 			}
 			}
 
 

+ 0 - 2
examples/jsm/nodes/materials/MeshNormalNodeMaterial.js

@@ -17,8 +17,6 @@ class MeshNormalNodeMaterial extends NodeMaterial {
 
 
 		this.isMeshNormalNodeMaterial = true;
 		this.isMeshNormalNodeMaterial = true;
 
 
-		this.colorSpaced = false;
-
 		this.setDefaultValues( defaultValues );
 		this.setDefaultValues( defaultValues );
 
 
 		this.setValues( parameters );
 		this.setValues( parameters );

+ 37 - 4
examples/jsm/nodes/materials/MeshPhysicalNodeMaterial.js

@@ -1,12 +1,12 @@
 import { addNodeMaterial } from './NodeMaterial.js';
 import { addNodeMaterial } from './NodeMaterial.js';
 import { transformedClearcoatNormalView } from '../accessors/NormalNode.js';
 import { transformedClearcoatNormalView } from '../accessors/NormalNode.js';
-import { roughness, clearcoat, clearcoatRoughness, sheen, sheenRoughness, iridescence, iridescenceIOR, iridescenceThickness, anisotropy, alphaT, anisotropyT, anisotropyB } from '../core/PropertyNode.js';
-import { materialAnisotropy, materialClearcoat, materialClearcoatRoughness, materialClearcoatNormal, materialSheen, materialSheenRoughness, materialIridescence, materialIridescenceIOR, materialIridescenceThickness } from '../accessors/MaterialNode.js';
+import { clearcoat, clearcoatRoughness, sheen, sheenRoughness, iridescence, iridescenceIOR, iridescenceThickness, specularColor, specularF90, diffuseColor, metalness, roughness, anisotropy, alphaT, anisotropyT, anisotropyB, ior, transmission, thickness, attenuationDistance, attenuationColor } from '../core/PropertyNode.js';
+import { materialClearcoat, materialClearcoatRoughness, materialClearcoatNormal, materialSheen, materialSheenRoughness, materialIridescence, materialIridescenceIOR, materialIridescenceThickness, materialSpecularIntensity, materialSpecularColor2, materialAnisotropy, materialIOR, materialTransmission, materialThickness, materialAttenuationDistance, materialAttenuationColor } from '../accessors/MaterialNode.js';
 import { float, vec2, vec3, If } from '../shadernode/ShaderNode.js';
 import { float, vec2, vec3, If } from '../shadernode/ShaderNode.js';
 import { TBNViewMatrix } from '../accessors/AccessorsUtils.js';
 import { TBNViewMatrix } from '../accessors/AccessorsUtils.js';
 import PhysicalLightingModel from '../functions/PhysicalLightingModel.js';
 import PhysicalLightingModel from '../functions/PhysicalLightingModel.js';
 import MeshStandardNodeMaterial from './MeshStandardNodeMaterial.js';
 import MeshStandardNodeMaterial from './MeshStandardNodeMaterial.js';
-
+import { mix, pow2, min } from '../math/MathNode.js';
 import { MeshPhysicalMaterial } from 'three';
 import { MeshPhysicalMaterial } from 'three';
 
 
 const defaultValues = new MeshPhysicalMaterial();
 const defaultValues = new MeshPhysicalMaterial();
@@ -33,6 +33,7 @@ class MeshPhysicalNodeMaterial extends MeshStandardNodeMaterial {
 		this.specularIntensityNode = null;
 		this.specularIntensityNode = null;
 		this.specularColorNode = null;
 		this.specularColorNode = null;
 
 
+		this.iorNode = null;
 		this.transmissionNode = null;
 		this.transmissionNode = null;
 		this.thicknessNode = null;
 		this.thicknessNode = null;
 		this.attenuationDistanceNode = null;
 		this.attenuationDistanceNode = null;
@@ -70,9 +71,25 @@ class MeshPhysicalNodeMaterial extends MeshStandardNodeMaterial {
 
 
 	}
 	}
 
 
+	get useTransmission() {
+
+		return this.transmission > 0 || this.transmissionNode !== null;
+
+	}
+
+	setupSpecular() {
+
+		const iorNode = this.iorNode ? float( this.iorNode ) : materialIOR;
+
+		ior.assign( iorNode );
+		specularColor.assign( mix( min( pow2( ior.sub( 1.0 ).div( ior.add( 1.0 ) ) ).mul( materialSpecularColor2 ), vec3( 1.0 ) ).mul( materialSpecularIntensity ), diffuseColor.rgb, metalness ) );
+		specularF90.assign( mix( materialSpecularIntensity, 1.0, metalness ) );
+
+	}
+
 	setupLightingModel( /*builder*/ ) {
 	setupLightingModel( /*builder*/ ) {
 
 
-		return new PhysicalLightingModel( this.useClearcoat, this.useSheen, this.useIridescence, this.useAnisotropy );
+		return new PhysicalLightingModel( this.useClearcoat, this.useSheen, this.useIridescence, this.useAnisotropy, this.useTransmission );
 
 
 	}
 	}
 
 
@@ -145,6 +162,22 @@ class MeshPhysicalNodeMaterial extends MeshStandardNodeMaterial {
 
 
 		}
 		}
 
 
+		// TRANSMISSION
+
+		if ( this.useTransmission ) {
+
+			const transmissionNode = this.transmissionNode ? float( this.transmissionNode ) : materialTransmission;
+			const thicknessNode = this.thicknessNode ? float( this.thicknessNode ) : materialThickness;
+			const attenuationDistanceNode = this.attenuationDistanceNode ? float( this.attenuationDistanceNode ) : materialAttenuationDistance;
+			const attenuationColorNode = this.attenuationColorNode ? vec3( this.attenuationColorNode ) : materialAttenuationColor;
+
+			transmission.assign( transmissionNode );
+			thickness.assign( thicknessNode );
+			attenuationDistance.assign( attenuationDistanceNode );
+			attenuationColor.assign( attenuationColorNode );
+
+		}
+
 	}
 	}
 
 
 	setupNormal( builder ) {
 	setupNormal( builder ) {

+ 9 - 3
examples/jsm/nodes/materials/MeshStandardNodeMaterial.js

@@ -35,6 +35,14 @@ class MeshStandardNodeMaterial extends NodeMaterial {
 
 
 	}
 	}
 
 
+	setupSpecular() {
+
+		const specularColorNode = mix( vec3( 0.04 ), diffuseColor.rgb, metalness );
+
+		specularColor.assign( specularColorNode );
+
+	}
+
 	setupVariants() {
 	setupVariants() {
 
 
 		// METALNESS
 		// METALNESS
@@ -52,9 +60,7 @@ class MeshStandardNodeMaterial extends NodeMaterial {
 
 
 		// SPECULAR COLOR
 		// SPECULAR COLOR
 
 
-		const specularColorNode = mix( vec3( 0.04 ), diffuseColor.rgb, metalnessNode );
-
-		specularColor.assign( specularColorNode );
+		this.setupSpecular();
 
 
 		// DIFFUSE COLOR
 		// DIFFUSE COLOR
 
 

+ 12 - 36
examples/jsm/nodes/materials/NodeMaterial.js

@@ -1,4 +1,4 @@
-import { Material, ShaderMaterial, NoColorSpace, LinearSRGBColorSpace } from 'three';
+import { Material, ShaderMaterial } from 'three';
 import { getNodeChildren, getCacheKey } from '../core/NodeUtils.js';
 import { getNodeChildren, getCacheKey } from '../core/NodeUtils.js';
 import { attribute } from '../core/AttributeNode.js';
 import { attribute } from '../core/AttributeNode.js';
 import { output, diffuseColor, varyingProperty } from '../core/PropertyNode.js';
 import { output, diffuseColor, varyingProperty } from '../core/PropertyNode.js';
@@ -42,8 +42,6 @@ class NodeMaterial extends ShaderMaterial {
 		this.lights = true;
 		this.lights = true;
 		this.normals = true;
 		this.normals = true;
 
 
-		this.colorSpaced = true;
-
 		this.lightsNode = null;
 		this.lightsNode = null;
 		this.envNode = null;
 		this.envNode = null;
 		this.aoNode = null;
 		this.aoNode = null;
@@ -126,7 +124,15 @@ class NodeMaterial extends ShaderMaterial {
 
 
 		} else {
 		} else {
 
 
-			resultNode = this.setupOutput( builder, this.fragmentNode );
+			let fragmentNode = this.fragmentNode;
+
+			if ( fragmentNode.isOutputStructNode !== true ) {
+
+				fragmentNode = vec4( fragmentNode );
+
+			}
+
+			resultNode = this.setupOutput( builder, fragmentNode );
 
 
 		}
 		}
 
 
@@ -403,41 +409,11 @@ class NodeMaterial extends ShaderMaterial {
 
 
 	setupOutput( builder, outputNode ) {
 	setupOutput( builder, outputNode ) {
 
 
-		const renderer = builder.renderer;
-
 		// FOG
 		// FOG
 
 
-		if ( this.fog === true ) {
-
-			const fogNode = builder.fogNode;
-
-			if ( fogNode ) outputNode = vec4( fogNode.mix( outputNode.rgb, fogNode.colorNode ), outputNode.a );
-
-		}
-
-		// TONE MAPPING
-
-		const toneMappingNode = builder.toneMappingNode;
+		const fogNode = builder.fogNode;
 
 
-		if ( this.toneMapped === true && toneMappingNode ) {
-
-			outputNode = vec4( toneMappingNode.context( { color: outputNode.rgb } ), outputNode.a );
-
-		}
-
-		// ENCODING
-
-		if ( this.colorSpaced === true ) {
-
-			const outputColorSpace = renderer.currentColorSpace;
-
-			if ( outputColorSpace !== LinearSRGBColorSpace && outputColorSpace !== NoColorSpace ) {
-
-				outputNode = outputNode.linearToColorSpace( outputColorSpace );
-
-			}
-
-		}
+		if ( fogNode ) outputNode = vec4( fogNode.mix( outputNode.rgb, fogNode.colorNode ), outputNode.a );
 
 
 		return outputNode;
 		return outputNode;
 
 

+ 3 - 3
examples/jsm/renderers/common/Background.js

@@ -1,6 +1,6 @@
 import DataMap from './DataMap.js';
 import DataMap from './DataMap.js';
 import Color4 from './Color4.js';
 import Color4 from './Color4.js';
-import { Mesh, SphereGeometry, BackSide } from 'three';
+import { Mesh, SphereGeometry, BackSide, LinearSRGBColorSpace } from 'three';
 import { vec4, context, normalWorld, backgroundBlurriness, backgroundIntensity, NodeMaterial, modelViewProjection } from '../../nodes/Nodes.js';
 import { vec4, context, normalWorld, backgroundBlurriness, backgroundIntensity, NodeMaterial, modelViewProjection } from '../../nodes/Nodes.js';
 
 
 const _clearColor = new Color4();
 const _clearColor = new Color4();
@@ -27,14 +27,14 @@ class Background extends DataMap {
 
 
 			// no background settings, use clear color configuration from the renderer
 			// no background settings, use clear color configuration from the renderer
 
 
-			renderer._clearColor.getRGB( _clearColor, this.renderer.currentColorSpace );
+			renderer._clearColor.getRGB( _clearColor, LinearSRGBColorSpace );
 			_clearColor.a = renderer._clearColor.a;
 			_clearColor.a = renderer._clearColor.a;
 
 
 		} else if ( background.isColor === true ) {
 		} else if ( background.isColor === true ) {
 
 
 			// background is an opaque color
 			// background is an opaque color
 
 
-			background.getRGB( _clearColor, this.renderer.currentColorSpace );
+			background.getRGB( _clearColor, LinearSRGBColorSpace );
 			_clearColor.a = 1;
 			_clearColor.a = 1;
 
 
 			forceClear = true;
 			forceClear = true;

+ 1 - 1
examples/jsm/renderers/common/RenderList.js

@@ -123,7 +123,7 @@ class RenderList {
 
 
 		if ( object.occlusionTest === true ) this.occlusionQueryCount ++;
 		if ( object.occlusionTest === true ) this.occlusionQueryCount ++;
 
 
-		( material.transparent === true ? this.transparent : this.opaque ).push( renderItem );
+		( material.transparent === true || material.transmission > 0 ? this.transparent : this.opaque ).push( renderItem );
 
 
 	}
 	}
 
 

+ 95 - 5
examples/jsm/renderers/common/Renderer.js

@@ -12,7 +12,9 @@ import Background from './Background.js';
 import Nodes from './nodes/Nodes.js';
 import Nodes from './nodes/Nodes.js';
 import Color4 from './Color4.js';
 import Color4 from './Color4.js';
 import ClippingContext from './ClippingContext.js';
 import ClippingContext from './ClippingContext.js';
-import { Scene, Frustum, Matrix4, Vector2, Vector3, Vector4, DoubleSide, BackSide, FrontSide, SRGBColorSpace, NoToneMapping } from 'three';
+import { Scene, Frustum, Matrix4, Vector2, Vector3, Vector4, DoubleSide, BackSide, FrontSide, SRGBColorSpace, NoColorSpace, NoToneMapping, LinearFilter, LinearSRGBColorSpace, RenderTarget, HalfFloatType, RGBAFormat } from 'three';
+import { NodeMaterial } from '../../nodes/Nodes.js';
+import QuadMesh from '../../objects/QuadMesh.js';
 
 
 const _scene = new Scene();
 const _scene = new Scene();
 const _drawingBufferSize = new Vector2();
 const _drawingBufferSize = new Vector2();
@@ -20,6 +22,7 @@ const _screen = new Vector4();
 const _frustum = new Frustum();
 const _frustum = new Frustum();
 const _projScreenMatrix = new Matrix4();
 const _projScreenMatrix = new Matrix4();
 const _vector3 = new Vector3();
 const _vector3 = new Vector3();
+const _quad = new QuadMesh( new NodeMaterial() );
 
 
 class Renderer {
 class Renderer {
 
 
@@ -63,6 +66,10 @@ class Renderer {
 
 
 		this.info = new Info();
 		this.info = new Info();
 
 
+		// nodes
+
+		this.toneMappingNode = null;
+
 		// internals
 		// internals
 
 
 		this._pixelRatio = 1;
 		this._pixelRatio = 1;
@@ -90,6 +97,7 @@ class Renderer {
 		this._opaqueSort = null;
 		this._opaqueSort = null;
 		this._transparentSort = null;
 		this._transparentSort = null;
 
 
+		this._frameBufferTarget = null;
 
 
 		const alphaClear = this.alpha === true ? 0 : 1;
 		const alphaClear = this.alpha === true ? 0 : 1;
 
 
@@ -332,7 +340,56 @@ class Renderer {
 
 
 	}
 	}
 
 
-	_renderScene( scene, camera ) {
+	_getFrameBufferTarget() {
+
+		const { currentColorSpace } = this;
+
+		const useToneMapping = this._renderTarget === null && ( this.toneMapping !== NoToneMapping || this.toneMappingNode !== null );
+		const useColorSpace = currentColorSpace !== LinearSRGBColorSpace && currentColorSpace !== NoColorSpace;
+
+		if ( useToneMapping === false && useColorSpace === false ) return null;
+
+		const { width, height } = this.getDrawingBufferSize( _drawingBufferSize );
+		const { depth, stencil } = this;
+
+		let frameBufferTarget = this._frameBufferTarget;
+
+		if ( frameBufferTarget === null ) {
+
+			frameBufferTarget = new RenderTarget( width, height, {
+				depthBuffer: depth,
+				stencilBuffer: stencil,
+				type: HalfFloatType, // FloatType
+				format: RGBAFormat,
+				colorSpace: LinearSRGBColorSpace,
+				generateMipmaps: false,
+				minFilter: LinearFilter,
+				magFilter: LinearFilter,
+				samples: this.backend.parameters.antialias ? 4 : 0
+			} );
+
+			frameBufferTarget.isPostProcessingRenderTarget = true;
+
+			this._frameBufferTarget = frameBufferTarget;
+
+		}
+
+		frameBufferTarget.depthBuffer = depth;
+		frameBufferTarget.stencilBuffer = stencil;
+		frameBufferTarget.setSize( width, height );
+		frameBufferTarget.viewport.copy( this._viewport );
+		frameBufferTarget.scissor.copy( this._scissor );
+		frameBufferTarget.viewport.multiplyScalar( this._pixelRatio );
+		frameBufferTarget.scissor.multiplyScalar( this._pixelRatio );
+		frameBufferTarget.scissorTest = this._scissorTest;
+
+		return frameBufferTarget;
+
+	}
+
+	_renderScene( scene, camera, useFrameBufferTarget = true ) {
+
+		const frameBufferTarget = useFrameBufferTarget ? this._getFrameBufferTarget() : null;
 
 
 		// preserve render tree
 		// preserve render tree
 
 
@@ -346,11 +403,31 @@ class Renderer {
 
 
 		const sceneRef = ( scene.isScene === true ) ? scene : _scene;
 		const sceneRef = ( scene.isScene === true ) ? scene : _scene;
 
 
-		const renderTarget = this._renderTarget;
-		const renderContext = this._renderContexts.get( scene, camera, renderTarget );
+		const outputRenderTarget = this._renderTarget;
+
 		const activeCubeFace = this._activeCubeFace;
 		const activeCubeFace = this._activeCubeFace;
 		const activeMipmapLevel = this._activeMipmapLevel;
 		const activeMipmapLevel = this._activeMipmapLevel;
 
 
+		//
+
+		let renderTarget;
+
+		if ( frameBufferTarget !== null ) {
+
+			renderTarget = frameBufferTarget;
+
+			this.setRenderTarget( renderTarget );
+
+		} else {
+
+			renderTarget = outputRenderTarget;
+
+		}
+
+		//
+
+		const renderContext = this._renderContexts.get( scene, camera, renderTarget );
+
 		this._currentRenderContext = renderContext;
 		this._currentRenderContext = renderContext;
 		this._currentRenderObjectFunction = this._renderObjectFunction || this.renderObject;
 		this._currentRenderObjectFunction = this._renderObjectFunction || this.renderObject;
 
 
@@ -505,6 +582,18 @@ class Renderer {
 
 
 		//
 		//
 
 
+		if ( frameBufferTarget !== null ) {
+
+			this.setRenderTarget( outputRenderTarget, activeCubeFace, activeMipmapLevel );
+
+			_quad.material.fragmentNode = this._nodes.getOutputNode( renderTarget.texture );
+
+			this._renderScene( _quad, _quad.camera, false );
+
+		}
+
+		//
+
 		sceneRef.onAfterRender( this, scene, camera, renderTarget );
 		sceneRef.onAfterRender( this, scene, camera, renderTarget );
 
 
 		//
 		//
@@ -769,8 +858,9 @@ class Renderer {
 
 
 		}
 		}
 
 
+		const renderTarget = this._renderTarget || this._getFrameBufferTarget();
+
 		let renderTargetData = null;
 		let renderTargetData = null;
-		const renderTarget = this._renderTarget;
 
 
 		if ( renderTarget !== null ) {
 		if ( renderTarget !== null ) {
 
 

+ 1 - 1
examples/jsm/renderers/common/Textures.js

@@ -38,7 +38,7 @@ class Textures extends DataMap {
 
 
 			depthTexture = new DepthTexture();
 			depthTexture = new DepthTexture();
 			depthTexture.format = renderTarget.stencilBuffer ? DepthStencilFormat : DepthFormat;
 			depthTexture.format = renderTarget.stencilBuffer ? DepthStencilFormat : DepthFormat;
-			depthTexture.type = renderTarget.stencilBuffer ? UnsignedInt248Type : UnsignedIntType;
+			depthTexture.type = renderTarget.stencilBuffer ? UnsignedInt248Type : UnsignedIntType; // FloatType
 			depthTexture.image.width = mipWidth;
 			depthTexture.image.width = mipWidth;
 			depthTexture.image.height = mipHeight;
 			depthTexture.image.height = mipHeight;
 
 

+ 1 - 6
examples/jsm/renderers/common/extras/PMREMGenerator.js

@@ -20,7 +20,6 @@ import {
 	CubeRefractionMapping,
 	CubeRefractionMapping,
 	CubeUVReflectionMapping,
 	CubeUVReflectionMapping,
 	LinearFilter,
 	LinearFilter,
-	NoToneMapping,
 	NoBlending,
 	NoBlending,
 	RGBAFormat,
 	RGBAFormat,
 	HalfFloatType,
 	HalfFloatType,
@@ -338,10 +337,9 @@ class PMREMGenerator {
 		const renderer = this._renderer;
 		const renderer = this._renderer;
 
 
 		const originalAutoClear = renderer.autoClear;
 		const originalAutoClear = renderer.autoClear;
-		const toneMapping = renderer.toneMapping;
+
 		renderer.getClearColor( _clearColor );
 		renderer.getClearColor( _clearColor );
 
 
-		renderer.toneMapping = NoToneMapping;
 		renderer.autoClear = false;
 		renderer.autoClear = false;
 
 
 		let backgroundBox = this._backgroundBox;
 		let backgroundBox = this._backgroundBox;
@@ -418,7 +416,6 @@ class PMREMGenerator {
 
 
 		}
 		}
 
 
-		renderer.toneMapping = toneMapping;
 		renderer.autoClear = originalAutoClear;
 		renderer.autoClear = originalAutoClear;
 		scene.background = background;
 		scene.background = background;
 
 
@@ -709,8 +706,6 @@ function _setViewport( target, x, y, width, height ) {
 function _getMaterial() {
 function _getMaterial() {
 
 
 	const material = new NodeMaterial();
 	const material = new NodeMaterial();
-	material.colorSpaced = false;
-	material.toneMapped = false;
 	material.depthTest = false;
 	material.depthTest = false;
 	material.depthWrite = false;
 	material.depthWrite = false;
 	material.blending = NoBlending;
 	material.blending = NoBlending;

+ 31 - 47
examples/jsm/renderers/common/nodes/Nodes.js

@@ -1,8 +1,8 @@
 import DataMap from '../DataMap.js';
 import DataMap from '../DataMap.js';
 import ChainMap from '../ChainMap.js';
 import ChainMap from '../ChainMap.js';
 import NodeBuilderState from './NodeBuilderState.js';
 import NodeBuilderState from './NodeBuilderState.js';
-import { NoToneMapping, EquirectangularReflectionMapping, EquirectangularRefractionMapping } from 'three';
-import { NodeFrame, objectGroup, renderGroup, frameGroup, cubeTexture, texture, rangeFog, densityFog, reference, toneMapping, viewportBottomLeft, normalWorld, pmremTexture } from '../../../nodes/Nodes.js';
+import { EquirectangularReflectionMapping, EquirectangularRefractionMapping, NoToneMapping, SRGBColorSpace } from 'three';
+import { NodeFrame, vec4, objectGroup, renderGroup, frameGroup, cubeTexture, texture, rangeFog, densityFog, reference, viewportBottomLeft, normalWorld, pmremTexture, viewportTopLeft } from '../../../nodes/Nodes.js';
 
 
 class Nodes extends DataMap {
 class Nodes extends DataMap {
 
 
@@ -113,7 +113,6 @@ class Nodes extends DataMap {
 				nodeBuilder.lightsNode = renderObject.lightsNode;
 				nodeBuilder.lightsNode = renderObject.lightsNode;
 				nodeBuilder.environmentNode = this.getEnvironmentNode( renderObject.scene );
 				nodeBuilder.environmentNode = this.getEnvironmentNode( renderObject.scene );
 				nodeBuilder.fogNode = this.getFogNode( renderObject.scene );
 				nodeBuilder.fogNode = this.getFogNode( renderObject.scene );
-				nodeBuilder.toneMappingNode = this.getToneMappingNode();
 				nodeBuilder.clippingContext = renderObject.clippingContext;
 				nodeBuilder.clippingContext = renderObject.clippingContext;
 				nodeBuilder.build();
 				nodeBuilder.build();
 
 
@@ -206,14 +205,6 @@ class Nodes extends DataMap {
 
 
 	}
 	}
 
 
-	getToneMappingNode() {
-
-		if ( this.isToneMappingState === false ) return null;
-
-		return this.renderer.toneMappingNode || this.get( this.renderer ).toneMappingNode || null;
-
-	}
-
 	getCacheKey( scene, lightsNode ) {
 	getCacheKey( scene, lightsNode ) {
 
 
 		const chain = [ scene, lightsNode ];
 		const chain = [ scene, lightsNode ];
@@ -225,14 +216,12 @@ class Nodes extends DataMap {
 
 
 			const environmentNode = this.getEnvironmentNode( scene );
 			const environmentNode = this.getEnvironmentNode( scene );
 			const fogNode = this.getFogNode( scene );
 			const fogNode = this.getFogNode( scene );
-			const toneMappingNode = this.getToneMappingNode();
 
 
 			const cacheKey = [];
 			const cacheKey = [];
 
 
 			if ( lightsNode ) cacheKey.push( lightsNode.getCacheKey() );
 			if ( lightsNode ) cacheKey.push( lightsNode.getCacheKey() );
 			if ( environmentNode ) cacheKey.push( environmentNode.getCacheKey() );
 			if ( environmentNode ) cacheKey.push( environmentNode.getCacheKey() );
 			if ( fogNode ) cacheKey.push( fogNode.getCacheKey() );
 			if ( fogNode ) cacheKey.push( fogNode.getCacheKey() );
-			if ( toneMappingNode ) cacheKey.push( toneMappingNode.getCacheKey() );
 
 
 			cacheKeyData = {
 			cacheKeyData = {
 				callId,
 				callId,
@@ -252,45 +241,12 @@ class Nodes extends DataMap {
 		this.updateEnvironment( scene );
 		this.updateEnvironment( scene );
 		this.updateFog( scene );
 		this.updateFog( scene );
 		this.updateBackground( scene );
 		this.updateBackground( scene );
-		this.updateToneMapping();
 
 
 	}
 	}
 
 
 	get isToneMappingState() {
 	get isToneMappingState() {
 
 
-		const renderer = this.renderer;
-		const renderTarget = renderer.getRenderTarget();
-
-		return renderTarget && renderTarget.isCubeRenderTarget ? false : true;
-
-	}
-
-	updateToneMapping() {
-
-		const renderer = this.renderer;
-		const rendererData = this.get( renderer );
-		const rendererToneMapping = renderer.toneMapping;
-
-		if ( this.isToneMappingState && rendererToneMapping !== NoToneMapping ) {
-
-			if ( rendererData.toneMapping !== rendererToneMapping ) {
-
-				const rendererToneMappingNode = rendererData.rendererToneMappingNode || toneMapping( rendererToneMapping );
-				rendererToneMappingNode.toneMapping = rendererToneMapping;
-
-				rendererData.rendererToneMappingNode = rendererToneMappingNode;
-				rendererData.toneMappingNode = rendererToneMappingNode;
-				rendererData.toneMapping = rendererToneMapping;
-
-			}
-
-		} else {
-
-			// Don't delete rendererData.rendererToneMappingNode
-			delete rendererData.toneMappingNode;
-			delete rendererData.toneMapping;
-
-		}
+		return this.renderer.getRenderTarget() ? false : true;
 
 
 	}
 	}
 
 
@@ -430,6 +386,34 @@ class Nodes extends DataMap {
 
 
 	}
 	}
 
 
+	getOutputNode( outputTexture ) {
+
+		let output = texture( outputTexture, viewportTopLeft );
+
+		if ( this.isToneMappingState ) {
+
+			if ( this.renderer.toneMappingNode ) {
+
+				output = vec4( this.renderer.toneMappingNode.context( { color: output.rgb } ), output.a );
+
+			} else if ( this.renderer.toneMapping !== NoToneMapping ) {
+
+				output = output.toneMapping( this.renderer.toneMapping );
+
+			}
+
+		}
+
+		if ( this.renderer.currentColorSpace === SRGBColorSpace ) {
+
+			output = output.linearToColorSpace( this.renderer.currentColorSpace );
+
+		}
+
+		return output;
+
+	}
+
 	updateBefore( renderObject ) {
 	updateBefore( renderObject ) {
 
 
 		const nodeFrame = this.getNodeFrameForRender( renderObject );
 		const nodeFrame = this.getNodeFrameForRender( renderObject );

+ 1 - 0
examples/jsm/renderers/webgl/WebGLBackend.js

@@ -173,6 +173,7 @@ class WebGLBackend extends Backend {
 		//
 		//
 
 
 		//
 		//
+
 		this.initTimestampQuery( renderContext );
 		this.initTimestampQuery( renderContext );
 
 
 		renderContextData.previousContext = this._currentContext;
 		renderContextData.previousContext = this._currentContext;

+ 0 - 3
examples/jsm/renderers/webgpu/WebGPUBackend.js

@@ -1272,9 +1272,6 @@ class WebGPUBackend extends Backend {
 
 
 	}
 	}
 
 
-
-
-
 	copyFramebufferToTexture( texture, renderContext ) {
 	copyFramebufferToTexture( texture, renderContext ) {
 
 
 		const renderContextData = this.get( renderContext );
 		const renderContextData = this.get( renderContext );

+ 1 - 0
examples/jsm/renderers/webgpu/nodes/WGSLNodeBuilder.js

@@ -916,6 +916,7 @@ ${ flowData.code }
 
 
 			this.vertexShader = this._getWGSLVertexCode( shadersData.vertex );
 			this.vertexShader = this._getWGSLVertexCode( shadersData.vertex );
 			this.fragmentShader = this._getWGSLFragmentCode( shadersData.fragment );
 			this.fragmentShader = this._getWGSLFragmentCode( shadersData.fragment );
+			//console.log( this.fragmentShader );
 
 
 		} else {
 		} else {
 
 

BIN
examples/screenshots/webgpu_backdrop.jpg


BIN
examples/screenshots/webgpu_backdrop_area.jpg


BIN
examples/screenshots/webgpu_compute_geometry.jpg


BIN
examples/screenshots/webgpu_compute_particles.jpg


BIN
examples/screenshots/webgpu_compute_particles_rain.jpg


BIN
examples/screenshots/webgpu_custom_fog_background.jpg


BIN
examples/screenshots/webgpu_instance_mesh.jpg


BIN
examples/screenshots/webgpu_instance_points.jpg


BIN
examples/screenshots/webgpu_lights_custom.jpg


BIN
examples/screenshots/webgpu_lines_fat.jpg


BIN
examples/screenshots/webgpu_loader_gltf.jpg


BIN
examples/screenshots/webgpu_loader_gltf_anisotropy.jpg


BIN
examples/screenshots/webgpu_loader_gltf_compressed.jpg


BIN
examples/screenshots/webgpu_loader_gltf_iridescence.jpg


BIN
examples/screenshots/webgpu_loader_gltf_transmission.jpg


BIN
examples/screenshots/webgpu_materials_transmission.jpg


BIN
examples/screenshots/webgpu_postprocessing_anamorphic.jpg


BIN
examples/screenshots/webgpu_skinning.jpg


BIN
examples/screenshots/webgpu_skinning_points.jpg


+ 1 - 1
examples/webgl_materials_physical_transmission.html

@@ -252,4 +252,4 @@
 
 
 		</script>
 		</script>
 	</body>
 	</body>
-</html>
+</html>

+ 2 - 2
examples/webgpu_backdrop.html

@@ -56,7 +56,7 @@
 				camera.position.set( 1, 2, 3 );
 				camera.position.set( 1, 2, 3 );
 
 
 				scene = new THREE.Scene();
 				scene = new THREE.Scene();
-				scene.background = new THREE.Color( 'lightblue' );
+				scene.backgroundNode = viewportTopLeft.y.mix( color( 0x66bbff ), color( 0x4466ff ) );
 				camera.lookAt( 0, 1, 0 );
 				camera.lookAt( 0, 1, 0 );
 
 
 				clock = new THREE.Clock();
 				clock = new THREE.Clock();
@@ -134,7 +134,7 @@
 				renderer.setPixelRatio( window.devicePixelRatio );
 				renderer.setPixelRatio( window.devicePixelRatio );
 				renderer.setSize( window.innerWidth, window.innerHeight );
 				renderer.setSize( window.innerWidth, window.innerHeight );
 				renderer.setAnimationLoop( animate );
 				renderer.setAnimationLoop( animate );
-				renderer.toneMappingNode = toneMapping( THREE.LinearToneMapping, .15 );
+				renderer.toneMappingNode = toneMapping( THREE.LinearToneMapping, .3 );
 				document.body.appendChild( renderer.domElement );
 				document.body.appendChild( renderer.domElement );
 
 
 				const controls = new OrbitControls( camera, renderer.domElement );
 				const controls = new OrbitControls( camera, renderer.domElement );

+ 2 - 2
examples/webgpu_backdrop_area.html

@@ -57,7 +57,7 @@
 				camera.position.set( 3, 2, 3 );
 				camera.position.set( 3, 2, 3 );
 
 
 				scene = new THREE.Scene();
 				scene = new THREE.Scene();
-				scene.background = new THREE.Color( 0x333333 );
+				scene.background = new THREE.Color( 0x777777 );
 				camera.lookAt( 0, 1, 0 );
 				camera.lookAt( 0, 1, 0 );
 
 
 				clock = new THREE.Clock();
 				clock = new THREE.Clock();
@@ -127,7 +127,7 @@
 				renderer.setPixelRatio( window.devicePixelRatio );
 				renderer.setPixelRatio( window.devicePixelRatio );
 				renderer.setSize( window.innerWidth, window.innerHeight );
 				renderer.setSize( window.innerWidth, window.innerHeight );
 				renderer.setAnimationLoop( animate );
 				renderer.setAnimationLoop( animate );
-				renderer.toneMappingNode = toneMapping( THREE.LinearToneMapping, .15 );
+				renderer.toneMappingNode = toneMapping( THREE.LinearToneMapping, .2 );
 				document.body.appendChild( renderer.domElement );
 				document.body.appendChild( renderer.domElement );
 
 
 				const controls = new OrbitControls( camera, renderer.domElement );
 				const controls = new OrbitControls( camera, renderer.domElement );

+ 1 - 4
examples/webgpu_loader_gltf_anisotropy.html

@@ -42,6 +42,7 @@
 				renderer = new WebGPURenderer( { antialias: true } );
 				renderer = new WebGPURenderer( { antialias: true } );
 				renderer.setPixelRatio( window.devicePixelRatio );
 				renderer.setPixelRatio( window.devicePixelRatio );
 				renderer.setSize( window.innerWidth, window.innerHeight );
 				renderer.setSize( window.innerWidth, window.innerHeight );
+				renderer.setAnimationLoop( render );
 				renderer.toneMapping = THREE.ACESFilmicToneMapping;
 				renderer.toneMapping = THREE.ACESFilmicToneMapping;
 				renderer.toneMappingExposure = 1.35;
 				renderer.toneMappingExposure = 1.35;
 				document.body.appendChild( renderer.domElement );
 				document.body.appendChild( renderer.domElement );
@@ -78,8 +79,6 @@
 
 
 				scene.add( gltf.scene );
 				scene.add( gltf.scene );
 
 
-				render();
-
 				window.addEventListener( 'resize', onWindowResize );
 				window.addEventListener( 'resize', onWindowResize );
 
 
 			}
 			}
@@ -92,8 +91,6 @@
 
 
 				renderer.setSize( window.innerWidth, window.innerHeight );
 				renderer.setSize( window.innerWidth, window.innerHeight );
 
 
-				render();
-
 			}
 			}
 
 
 			function render() {
 			function render() {

+ 126 - 0
examples/webgpu_loader_gltf_transmission.html

@@ -0,0 +1,126 @@
+<!DOCTYPE html>
+<html lang="en">
+	<head>
+		<title>three.js webgpu - GLTFloader + transmission</title>
+		<meta charset="utf-8">
+		<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
+		<link type="text/css" rel="stylesheet" href="main.css">
+	</head>
+	<body>
+
+		<div id="info">
+			<a href="https://threejs.org" target="_blank" rel="noopener">three.js</a> webgpu - GLTFLoader + <a href="https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_transmission" target="_blank" rel="noopener">KHR_materials_transmission</a><br />
+			Iridescent Dish With Olives by <a href="https://github.com/echadwick-wayfair" target="_blank" rel="noopener">Eric Chadwick</a><br />
+			<a href="https://hdrihaven.com/hdri/?h=royal_esplanade" target="_blank" rel="noopener">Royal Esplanade</a> from <a href="https://hdrihaven.com/" target="_blank" rel="noopener">HDRI Haven</a>
+		</div>
+
+		<script type="importmap">
+			{
+				"imports": {
+					"three": "../build/three.module.js",
+					"three/addons/": "./jsm/"
+				}
+			}
+		</script>
+
+		<script type="module">
+
+			import * as THREE from 'three';
+
+			import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
+			import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
+			import { RGBELoader } from 'three/addons/loaders/RGBELoader.js';
+
+			import { DRACOLoader } from 'three/addons/loaders/DRACOLoader.js';
+
+			import WebGPURenderer from 'three/addons/renderers/webgpu/WebGPURenderer.js';
+
+			let camera, scene, renderer, controls, clock, mixer;
+
+			init();
+
+			function init() {
+
+				clock = new THREE.Clock();
+
+				const container = document.createElement( 'div' );
+				document.body.appendChild( container );
+
+				camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 0.25, 20 );
+				camera.position.set( 0, 0.4, 0.7 );
+
+				scene = new THREE.Scene();
+
+				new RGBELoader()
+					.setPath( 'textures/equirectangular/' )
+					.load( 'royal_esplanade_1k.hdr', function ( texture ) {
+
+						texture.mapping = THREE.EquirectangularReflectionMapping;
+
+						scene.background = texture;
+						scene.backgroundBlurriness = 0.35;
+
+						scene.environment = texture;
+
+						// model
+
+						new GLTFLoader()
+							.setPath( 'models/gltf/' )
+							.setDRACOLoader( new DRACOLoader().setDecoderPath( 'jsm/libs/draco/gltf/' ) )
+							.load( 'IridescentDishWithOlives.glb', function ( gltf ) {
+
+								mixer = new THREE.AnimationMixer( gltf.scene );
+								mixer.clipAction( gltf.animations[ 0 ] ).play();
+
+								scene.add( gltf.scene );
+
+							} );
+
+					} );
+
+				renderer = new WebGPURenderer( { antialias: true } );
+				renderer.setAnimationLoop( render );
+				renderer.setPixelRatio( window.devicePixelRatio );
+				renderer.setSize( window.innerWidth, window.innerHeight );
+				renderer.toneMapping = THREE.ACESFilmicToneMapping;
+				renderer.toneMappingExposure = 1;
+				container.appendChild( renderer.domElement );
+
+				controls = new OrbitControls( camera, renderer.domElement );
+				controls.autoRotate = true;
+				controls.autoRotateSpeed = - 0.75;
+				controls.enableDamping = true;
+				controls.minDistance = 0.5;
+				controls.maxDistance = 1;
+				controls.target.set( 0, 0.1, 0 );
+				controls.update();
+
+				window.addEventListener( 'resize', onWindowResize );
+
+			}
+
+			function onWindowResize() {
+
+				camera.aspect = window.innerWidth / window.innerHeight;
+				camera.updateProjectionMatrix();
+
+				renderer.setSize( window.innerWidth, window.innerHeight );
+
+			}
+
+			//
+
+			function render() {
+
+				if ( mixer ) mixer.update( clock.getDelta() );
+
+				controls.update();
+
+				renderer.render( scene, camera );
+
+			}
+
+		</script>
+
+	</body>
+</html>

+ 243 - 0
examples/webgpu_materials_transmission.html

@@ -0,0 +1,243 @@
+<!DOCTYPE html>
+<html lang="en">
+	<head>
+		<title>threejs webgpu - materials - transmission</title>
+		<meta charset="utf-8">
+		<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
+		<link type="text/css" rel="stylesheet" href="main.css">
+	</head>
+	<body>
+
+		<div id="container"></div>
+		<div id="info"><a href="https://threejs.org" target="_blank" rel="noopener">threejs</a> webgpu - transmission</div>
+
+		<script type="importmap">
+			{
+				"imports": {
+					"three": "../build/three.module.js",
+					"three/addons/": "./jsm/"
+				}
+			}
+		</script>
+
+		<script type="module">
+
+			import * as THREE from 'three';
+
+			import { GUI } from 'three/addons/libs/lil-gui.module.min.js';
+			import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
+			import { RGBELoader } from 'three/addons/loaders/RGBELoader.js';
+
+			import WebGPURenderer from 'three/addons/renderers/webgpu/WebGPURenderer.js';
+
+			const params = {
+				color: 0xffffff,
+				transmission: 1,
+				opacity: 1,
+				metalness: 0,
+				roughness: 0,
+				ior: 1.5,
+				thickness: 0.01,
+				specularIntensity: 1,
+				specularColor: 0xffffff,
+				envMapIntensity: 1,
+				lightIntensity: 1,
+				exposure: 1
+			};
+
+			let camera, scene, renderer;
+
+			let mesh;
+
+			const hdrEquirect = new RGBELoader()
+				.setPath( 'textures/equirectangular/' )
+				.load( 'royal_esplanade_1k.hdr', function () {
+
+					hdrEquirect.mapping = THREE.EquirectangularReflectionMapping;
+
+					init();
+					render();
+
+				} );
+
+			function init() {
+
+				renderer = new WebGPURenderer( { antialias: true } );
+				renderer.setPixelRatio( window.devicePixelRatio );
+				renderer.setSize( window.innerWidth, window.innerHeight );
+				renderer.setAnimationLoop( render );
+				document.body.appendChild( renderer.domElement );
+
+				renderer.toneMapping = THREE.ACESFilmicToneMapping;
+				renderer.toneMappingExposure = params.exposure;
+
+
+				scene = new THREE.Scene();
+
+				camera = new THREE.PerspectiveCamera( 40, window.innerWidth / window.innerHeight, 1, 2000 );
+				camera.position.set( 0, 0, 120 );
+
+				//
+
+				scene.background = hdrEquirect;
+
+				//
+
+				const geometry = new THREE.SphereGeometry( 20, 64, 32 );
+
+				const texture = new THREE.CanvasTexture( generateTexture() );
+				texture.magFilter = THREE.NearestFilter;
+				texture.wrapT = THREE.RepeatWrapping;
+				texture.wrapS = THREE.RepeatWrapping;
+				texture.repeat.set( 1, 3.5 );
+
+				const material = new THREE.MeshPhysicalMaterial( {
+					color: params.color,
+					metalness: params.metalness,
+					roughness: params.roughness,
+					ior: params.ior,
+					alphaMap: texture,
+					envMap: hdrEquirect,
+					envMapIntensity: params.envMapIntensity,
+					transmission: params.transmission, // use material.transmission for glass materials
+					specularIntensity: params.specularIntensity,
+					specularColor: params.specularColor,
+					opacity: params.opacity,
+					side: THREE.DoubleSide,
+					transparent: true
+				} );
+
+				mesh = new THREE.Mesh( geometry, material );
+				scene.add( mesh );
+
+				//
+
+				const controls = new OrbitControls( camera, renderer.domElement );
+				controls.minDistance = 10;
+				controls.maxDistance = 150;
+
+				window.addEventListener( 'resize', onWindowResize );
+
+				//
+
+				const gui = new GUI();
+
+				gui.addColor( params, 'color' )
+					.onChange( function () {
+
+						material.color.set( params.color );
+
+					} );
+
+				gui.add( params, 'transmission', 0, 1, 0.01 )
+					.onChange( function () {
+
+						material.transmission = params.transmission;
+
+					} );
+
+				gui.add( params, 'opacity', 0, 1, 0.01 )
+					.onChange( function () {
+
+						material.opacity = params.opacity;
+
+					} );
+
+				gui.add( params, 'metalness', 0, 1, 0.01 )
+					.onChange( function () {
+
+						material.metalness = params.metalness;
+
+					} );
+
+				gui.add( params, 'roughness', 0, 1, 0.01 )
+					.onChange( function () {
+
+						material.roughness = params.roughness;
+
+					} );
+
+				gui.add( params, 'ior', 1, 2, 0.01 )
+					.onChange( function () {
+
+						material.ior = params.ior;
+
+					} );
+
+				gui.add( params, 'thickness', 0, 5, 0.01 )
+					.onChange( function () {
+
+						material.thickness = params.thickness;
+
+					} );
+
+				gui.add( params, 'specularIntensity', 0, 1, 0.01 )
+					.onChange( function () {
+
+						material.specularIntensity = params.specularIntensity;
+
+					} );
+
+				gui.addColor( params, 'specularColor' )
+					.onChange( function () {
+
+						material.specularColor.set( params.specularColor );
+
+					} );
+
+				gui.add( params, 'envMapIntensity', 0, 1, 0.01 )
+					.name( 'envMap intensity' )
+					.onChange( function () {
+
+						material.envMapIntensity = params.envMapIntensity;
+
+					} );
+
+				gui.add( params, 'exposure', 0, 1, 0.01 )
+					.onChange( function () {
+
+						renderer.toneMappingExposure = params.exposure;
+
+					} );
+
+				gui.open();
+
+			}
+
+			function onWindowResize() {
+
+				const width = window.innerWidth;
+				const height = window.innerHeight;
+
+				camera.aspect = width / height;
+				camera.updateProjectionMatrix();
+
+				renderer.setSize( width, height );
+
+			}
+
+			//
+
+			function generateTexture() {
+
+				const canvas = document.createElement( 'canvas' );
+				canvas.width = 2;
+				canvas.height = 2;
+
+				const context = canvas.getContext( '2d' );
+				context.fillStyle = 'white';
+				context.fillRect( 0, 1, 2, 1 );
+
+				return canvas;
+
+			}
+
+			function render() {
+
+				renderer.renderAsync( scene, camera );
+
+			}
+
+		</script>
+	</body>
+</html>

+ 6 - 3
examples/webgpu_skinning.html

@@ -25,7 +25,7 @@
 		<script type="module">
 		<script type="module">
 
 
 			import * as THREE from 'three';
 			import * as THREE from 'three';
-			import { toneMapping } from 'three/nodes';
+			import { toneMapping, color, viewportTopLeft } from 'three/nodes';
 
 
 			import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
 			import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
 
 
@@ -54,7 +54,7 @@
 				camera.position.set( 1, 2, 3 );
 				camera.position.set( 1, 2, 3 );
 
 
 				scene = new THREE.Scene();
 				scene = new THREE.Scene();
-				scene.background = new THREE.Color( 'lightblue' );
+				scene.backgroundNode = viewportTopLeft.y.mix( color( 0x66bbff ), color( 0x4466ff ) );
 				camera.lookAt( 0, 1, 0 );
 				camera.lookAt( 0, 1, 0 );
 
 
 				clock = new THREE.Clock();
 				clock = new THREE.Clock();
@@ -66,6 +66,9 @@
 				camera.add( light );
 				camera.add( light );
 				scene.add( camera );
 				scene.add( camera );
 
 
+				const ambient = new THREE.AmbientLight( 0x4466ff, 1 );
+				scene.add( ambient );
+
 				const loader = new GLTFLoader();
 				const loader = new GLTFLoader();
 				loader.load( 'models/gltf/Michelle.glb', function ( gltf ) {
 				loader.load( 'models/gltf/Michelle.glb', function ( gltf ) {
 
 
@@ -85,7 +88,7 @@
 				renderer.setPixelRatio( window.devicePixelRatio );
 				renderer.setPixelRatio( window.devicePixelRatio );
 				renderer.setSize( window.innerWidth, window.innerHeight );
 				renderer.setSize( window.innerWidth, window.innerHeight );
 				renderer.setAnimationLoop( animate );
 				renderer.setAnimationLoop( animate );
-				renderer.toneMappingNode = toneMapping( THREE.LinearToneMapping, .15 );
+				renderer.toneMappingNode = toneMapping( THREE.LinearToneMapping, .4 );
 				document.body.appendChild( renderer.domElement );
 				document.body.appendChild( renderer.domElement );
 
 
 				window.addEventListener( 'resize', onWindowResize );
 				window.addEventListener( 'resize', onWindowResize );

+ 1 - 1
examples/webgpu_sprites.html

@@ -79,7 +79,7 @@
 				const textureNode = texture( map );
 				const textureNode = texture( map );
 
 
 				const material = new SpriteNodeMaterial();
 				const material = new SpriteNodeMaterial();
-				material.colorNode = textureNode.mul( uv() ).mul( 2 );
+				material.colorNode = textureNode.mul( uv() ).mul( 2 ).saturate();
 				material.opacityNode = textureNode.a;
 				material.opacityNode = textureNode.a;
 				material.rotationNode = userData( 'rotation', 'float' ); // get value of: sprite.userData.rotation
 				material.rotationNode = userData( 'rotation', 'float' ); // get value of: sprite.userData.rotation
 				material.transparent = true;
 				material.transparent = true;