Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WebGPURenderer: Transmission - Backdrop 3/3 #27880

Merged
merged 50 commits into from
Apr 12, 2024
Merged
Show file tree
Hide file tree
Changes from 40 commits
Commits
Show all changes
50 commits
Select commit Hold shift + click to select a range
2bde431
WebGPURenderer: Apply fog before tonemapping and encoding
sunag Feb 29, 2024
51a9567
TSL: Add `rendererReference`
sunag Mar 3, 2024
fa358a4
PassNode: Add `getViewZNode()`
sunag Mar 3, 2024
7669377
FogNode: Add `.getViewZNode()`, removed `mixAssign()`
sunag Mar 3, 2024
d0e4571
TSL: Add `toneMappingExposure`
sunag Mar 3, 2024
0d7d848
PostProcessing: Add `.renderAsync()`
sunag Mar 3, 2024
7fb4f28
Add `webgpu_custom_fog_background` example
sunag Mar 3, 2024
eaecbd2
cleanup
sunag Mar 3, 2024
35e49c1
cleanup
sunag Mar 3, 2024
a174963
improve description
sunag Mar 3, 2024
1e7f19f
update example
sunag Mar 5, 2024
6ee0c2d
Merge branch 'dev' into dev-postprocessing
sunag Mar 6, 2024
f0206bb
Transmission - WIP
sunag Mar 7, 2024
e468957
rename
sunag Mar 7, 2024
f5873dc
Revert
sunag Mar 7, 2024
9e7b47d
Merge branch 'dev' into dev-postprocessing
sunag Mar 13, 2024
b517944
Merge branch 'dev' into dev-postprocessing
sunag Mar 26, 2024
3ff31f4
Merge branch 'dev' into dev-postprocessing
sunag Mar 27, 2024
7ba9e25
Introduce singleViewportMipTexture for tests and revisions
sunag Mar 27, 2024
2255334
revision
sunag Mar 27, 2024
31628cd
cleanup
sunag Mar 27, 2024
723d286
fix circular deps
sunag Mar 27, 2024
2253e91
update screenshot `webgpu_skinning`
sunag Mar 27, 2024
cecf20f
Merge branch 'dev' into dev-postprocessing
sunag Mar 28, 2024
d0458d0
revision
sunag Mar 29, 2024
b5f3c8a
copyFramebufferToTexture(): Added support for multisampling
sunag Apr 2, 2024
47b8aca
revision
sunag Apr 2, 2024
6c6494b
revision
sunag Apr 3, 2024
23367a8
revision
sunag Apr 3, 2024
3a90200
cleanup
sunag Apr 3, 2024
9c209d0
Merge branch 'dev-postprocessing' of https://github.com/sunag/three.j…
sunag Apr 3, 2024
5f01967
Fix faceDirection in WebGLBackend if used BackSide
sunag Apr 3, 2024
f8f57e7
Added frontFaceCW
sunag Apr 3, 2024
3cdd80e
Added webgpu_materials_transmission
sunag Apr 3, 2024
def795d
cleanup
sunag Apr 3, 2024
94eea4d
update
sunag Apr 3, 2024
33c4083
revision
sunag Apr 4, 2024
b5742e6
update webgpu_skinning
sunag Apr 4, 2024
2fb6882
revision getVolumeTransmissionRay
sunag Apr 5, 2024
a5e48cd
revision
sunag Apr 5, 2024
be9d5d9
revision
sunag Apr 5, 2024
01936db
revision
sunag Apr 5, 2024
c8ac9b9
update screenshot
sunag Apr 5, 2024
f4bde63
cleanup
sunag Apr 5, 2024
c8f5bdf
Merge branch 'dev' into dev-postprocessing
sunag Apr 11, 2024
e07a6ee
update `webgpu_loader_gltf_anisotropy` example
sunag Apr 11, 2024
80a8d37
cleanup
sunag Apr 11, 2024
2a9c9cb
Update webgpu_compute_geometry.jpg
sunag Apr 11, 2024
1009652
update screenshots
sunag Apr 12, 2024
cfa2925
add material.*Node transmission properties
sunag Apr 12, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 2 additions & 0 deletions examples/files.json
Original file line number Diff line number Diff line change
Expand Up @@ -348,9 +348,11 @@
"webgpu_loader_gltf_compressed",
"webgpu_loader_gltf_iridescence",
"webgpu_loader_gltf_sheen",
"webgpu_loader_gltf_transmission",
"webgpu_loader_materialx",
"webgpu_materials",
"webgpu_materials_sss",
"webgpu_materials_transmission",
"webgpu_materials_video",
"webgpu_materialx_noise",
"webgpu_multiple_rendertargets",
Expand Down
56 changes: 56 additions & 0 deletions examples/jsm/nodes/accessors/MaterialNode.js
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,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

const roughnessNode = this.getFloat( scope );
Expand Down Expand Up @@ -241,6 +255,34 @@ 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 {

const outputType = this.getNodeType( builder );
Expand All @@ -261,6 +303,8 @@ MaterialNode.OPACITY = 'opacity';
MaterialNode.SHININESS = 'shininess';
MaterialNode.SPECULAR_COLOR = 'specular';
MaterialNode.SPECULAR_STRENGTH = 'specularStrength';
MaterialNode.SPECULAR_INTENSITY = 'specularIntensity';
MaterialNode.SPECULAR_COLOR2 = 'specularColor';
MaterialNode.REFLECTIVITY = 'reflectivity';
MaterialNode.ROUGHNESS = 'roughness';
MaterialNode.METALNESS = 'metalness';
Expand All @@ -275,6 +319,10 @@ MaterialNode.SHEEN_ROUGHNESS = 'sheenRoughness';
MaterialNode.IRIDESCENCE = 'iridescence';
MaterialNode.IRIDESCENCE_IOR = 'iridescenceIOR';
MaterialNode.IRIDESCENCE_THICKNESS = 'iridescenceThickness';
MaterialNode.TRANSMISSION = 'transmission';
MaterialNode.THICKNESS = 'thickness';
MaterialNode.ATTENUATION_DISTANCE = 'attenuationDistance';
MaterialNode.ATTENUATION_COLOR = 'attenuationColor';
MaterialNode.LINE_SCALE = 'scale';
MaterialNode.LINE_DASH_SIZE = 'dashSize';
MaterialNode.LINE_GAP_SIZE = 'gapSize';
Expand All @@ -290,6 +338,10 @@ export const materialShininess = nodeImmutable( MaterialNode, MaterialNode.SHINI
export const materialEmissive = nodeImmutable( MaterialNode, MaterialNode.EMISSIVE );
export const materialOpacity = nodeImmutable( MaterialNode, MaterialNode.OPACITY );
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 materialReflectivity = nodeImmutable( MaterialNode, MaterialNode.REFLECTIVITY );
export const materialRoughness = nodeImmutable( MaterialNode, MaterialNode.ROUGHNESS );
Expand All @@ -304,6 +356,10 @@ export const materialSheenRoughness = nodeImmutable( MaterialNode, MaterialNode.
export const materialIridescence = nodeImmutable( MaterialNode, MaterialNode.IRIDESCENCE );
export const materialIridescenceIOR = nodeImmutable( MaterialNode, MaterialNode.IRIDESCENCE_IOR );
export const materialIridescenceThickness = nodeImmutable( MaterialNode, MaterialNode.IRIDESCENCE_THICKNESS );
export const materialTransmission = nodeImmutable( MaterialNode, MaterialNode.TRANSMISSION );
export const materialThickness = nodeImmutable( MaterialNode, MaterialNode.THICKNESS );
export const attenuationDistance = nodeImmutable( MaterialNode, MaterialNode.ATTENUATION_DISTANCE );
export const attenuationColor = nodeImmutable( MaterialNode, MaterialNode.ATTENUATION_COLOR );
export const materialLineScale = nodeImmutable( MaterialNode, MaterialNode.LINE_SCALE );
export const materialLineDashSize = nodeImmutable( MaterialNode, MaterialNode.LINE_DASH_SIZE );
export const materialLineGapSize = nodeImmutable( MaterialNode, MaterialNode.LINE_GAP_SIZE );
Expand Down
2 changes: 1 addition & 1 deletion examples/jsm/nodes/accessors/TextureSizeNode.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ class TextureSizeNode extends Node {
const textureProperty = this.textureNode.build( builder, 'property' );
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 );

}

Expand Down
1 change: 0 additions & 1 deletion examples/jsm/nodes/core/NodeBuilder.js
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,6 @@ class NodeBuilder {
this.lightsNode = null;
this.environmentNode = null;
this.fogNode = null;
this.toneMappingNode = null;

this.clippingContext = null;

Expand Down
3 changes: 2 additions & 1 deletion examples/jsm/nodes/core/OutputStructNode.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,10 @@ class OutputStructNode extends Node {

super();

this.isOutputStructNode = true;
this.members = members;

this.isOutputStructNode = true;

}

setup( builder ) {
Expand Down
13 changes: 13 additions & 0 deletions examples/jsm/nodes/display/FrontFacingNode.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import Node, { addNodeClass } from '../core/Node.js';
import { nodeImmutable, float } from '../shadernode/ShaderNode.js';
import { BackSide, WebGLCoordinateSystem } from 'three';

class FrontFacingNode extends Node {

Expand All @@ -13,6 +14,18 @@ class FrontFacingNode extends Node {

generate( builder ) {

const { renderer, material } = builder;

if ( renderer.coordinateSystem === WebGLCoordinateSystem ) {

if ( material.side === BackSide ) {

return 'false';

}

}

return builder.getFrontFacing();

}
Expand Down
5 changes: 4 additions & 1 deletion examples/jsm/nodes/display/ViewportTextureNode.js
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,10 @@ class ViewportTextureNode extends TextureNode {

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;

}

Expand Down
171 changes: 165 additions & 6 deletions examples/jsm/nodes/functions/PhysicalLightingModel.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,140 @@
import BRDF_Sheen from './BSDF/BRDF_Sheen.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 { transformedNormalView, transformedClearcoatNormalView, transformedNormalWorld } from '../accessors/NormalNode.js';
import { positionViewDirection, positionWorld } from '../accessors/PositionNode.js';
import { tslFn, float, vec2, vec3, vec4, mat3, mat4, If } from '../shadernode/ShaderNode.js';
Fixed Show fixed Hide fixed
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 { materialReference } from '../accessors/MaterialReferenceNode.js';
import { viewportResolution } from '../display/ViewportNode.js';
import { viewportMipTexture } from '../display/ViewportTextureNode.js';
import { materialThickness, materialTransmission } from '../accessors/MaterialNode.js';

const materialIOR = materialReference( 'ior', 'float' );

//
// 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 sizeX = viewportResolution.x;

const lod = float( log2( float( sizeX ) ).mul( applyIorToRoughness( roughness, ior ) ) ).toVar();

return transmissionSample.bicubic( lod );

} );

const volumeAttenuation = tslFn( ( [ transmissionDistance, attenuationColor, attenuationDistance ] ) => {

If( attenuationDistance.notEqual( 0 ), () => {

// Compute light attenuation using Beer's law.
const attenuationCoefficient = vec3( log( attenuationColor ).negate().div( attenuationDistance ) );
const transmittance = vec3( 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 = vec3( getVolumeTransmissionRay( n, v, thickness, ior, modelMatrix ) ).toVar();
const refractedRayExit = vec3( 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 = vec4( getTransmissionSample( refractionCoords, roughness, ior ) );
const transmittance = vec3( diffuseColor.mul( volumeAttenuation( length( transmissionRay ), attenuationColor, attenuationDistance ) ) );
const attenuatedColor = vec3( transmittance.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 = float( transmittance.r.add( transmittance.g, transmittance.b ) ).div( 3.0 );

return vec4( F.oneMinus().mul( attenuatedColor ), transmittedLight.a.oneMinus().mul( transmittanceFactor ).oneMinus() );

} );

//
// Iridescence
Expand Down Expand Up @@ -172,13 +301,14 @@

class PhysicalLightingModel extends LightingModel {

constructor( clearcoat = false, sheen = false, iridescence = false ) {
constructor( clearcoat = false, sheen = false, iridescence = false, transmission = false ) {

super();

this.clearcoat = clearcoat;
this.sheen = sheen;
this.iridescence = iridescence;
this.transmission = transmission;

this.clearcoatRadiance = null;
this.clearcoatSpecularDirect = null;
Expand All @@ -190,7 +320,7 @@

}

start( /*context*/ ) {
start( context ) {

if ( this.clearcoat === true ) {

Expand Down Expand Up @@ -223,6 +353,35 @@

}

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,
float( 1 ), // specularF90
position, // positionWorld
modelWorldMatrix, // modelMatrix
cameraViewMatrix, // viewMatrix
cameraProjectionMatrix, // projMatrix
materialIOR, // ior
materialThickness, // thickness
materialReference( 'attenuationColor', 'color' ), // attenuationColor
materialReference( 'attenuationDistance', 'float' ) // attenuationDistance
);

context.backdropAlpha = materialTransmission;

diffuseColor.a.mulAssign( mix( 1, context.backdrop.a, materialTransmission ) );

}

}

// Fdez-Agüera's "Multiple-Scattering Microfacet Model for Real-Time Image Based Lighting"
Expand Down