Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions examples/webgpu_caustics.html
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,7 @@
renderer.setSize( window.innerWidth, window.innerHeight );
renderer.setAnimationLoop( animate );
renderer.shadowMap.enabled = true;
renderer.shadowMap.color = true;
renderer.inspector = new Inspector();
document.body.appendChild( renderer.domElement );

Expand Down
17 changes: 1 addition & 16 deletions examples/webgpu_shadowmap.html
Original file line number Diff line number Diff line change
Expand Up @@ -107,22 +107,7 @@

const discardNode = hash( vertexIndex ).greaterThan( 0.5 );

materialCustomShadow.colorNode = Fn( () => {

discardNode.discard();

return materialColor;

} )();


materialCustomShadow.castShadowNode = Fn( () => {

discardNode.discard();

return materialColor;

} )();
materialCustomShadow.maskNode = discardNode;

torusKnot = new THREE.Mesh( geometry, materialCustomShadow );
torusKnot.scale.multiplyScalar( 1 / 18 );
Expand Down
1 change: 1 addition & 0 deletions examples/webgpu_shadowmap_opacity.html
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@
renderer.toneMapping = THREE.AgXToneMapping;
renderer.toneMappingExposure = 1.5;
renderer.shadowMap.enabled = true;
renderer.shadowMap.color = true;
renderer.inspector = new Inspector();
container.appendChild( renderer.domElement );

Expand Down
23 changes: 7 additions & 16 deletions examples/webgpu_tsl_angular_slicing.html
Original file line number Diff line number Diff line change
Expand Up @@ -113,13 +113,16 @@
const sliceArc = uniform( 1.25 );
const sliceColor = uniform( color( '#b62f58' ) );

// output
// mask

slicedMaterial.outputNode = Fn( () => {
const mask = inAngle( positionLocal.xy, sliceStart, sliceArc ).not();

slicedMaterial.maskNode = mask;
//slicedMaterial.maskShadowNode = mask; // optional: custom mask shadows

// discard
// output

inAngle( positionLocal.xy, sliceStart, sliceArc ).discard();
slicedMaterial.outputNode = Fn( () => {

// backface color

Expand All @@ -134,18 +137,6 @@

} )();

// shadow

slicedMaterial.castShadowNode = Fn( () => {

// discard

inAngle( positionLocal.xy, sliceStart, sliceArc ).discard();

return vec4( 0, 0, 0, 1 );

} )();

// model

const dracoLoader = new DRACOLoader();
Expand Down
1 change: 1 addition & 0 deletions examples/webgpu_volume_caustics.html
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,7 @@

renderer = new THREE.WebGPURenderer( { antialias: true } );
renderer.shadowMap.enabled = true;
renderer.shadowMap.color = true;
renderer.inspector = new Inspector();
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
Expand Down
1 change: 1 addition & 0 deletions src/Three.TSL.js
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@ export const clamp = TSL.clamp;
export const clearcoat = TSL.clearcoat;
export const clearcoatNormalView = TSL.clearcoatNormalView;
export const clearcoatRoughness = TSL.clearcoatRoughness;
export const clipSpace = TSL.clipSpace;
export const code = TSL.code;
export const color = TSL.color;
export const colorSpaceToWorking = TSL.colorSpaceToWorking;
Expand Down
13 changes: 12 additions & 1 deletion src/materials/nodes/NodeMaterial.js
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,14 @@ class NodeMaterial extends Material {
*/
this.maskNode = null;

/**
* This node can be used to implement a shadow mask for the material.
*
* @type {?Node<bool>}
* @default null
*/
this.maskShadowNode = null;

/**
* The local vertex positions are computed based on multiple factors like the
* attribute data, morphing or skinning. This node property allows to overwrite
Expand Down Expand Up @@ -531,6 +539,8 @@ class NodeMaterial extends Material {

const vertexNode = this.vertexNode || mvp;

builder.context.clipSpace = vertexNode;

builder.stack.outputNode = vertexNode;

this.setupHardwareClipping( builder );
Expand Down Expand Up @@ -795,7 +805,7 @@ class NodeMaterial extends Material {

this.setupPosition( builder );

builder.context.vertex = builder.removeStack();
builder.context.position = builder.removeStack();

return modelViewProjection;

Expand Down Expand Up @@ -1339,6 +1349,7 @@ class NodeMaterial extends Material {
this.backdropAlphaNode = source.backdropAlphaNode;
this.alphaTestNode = source.alphaTestNode;
this.maskNode = source.maskNode;
this.maskShadowNode = source.maskShadowNode;

this.positionNode = source.positionNode;
this.geometryNode = source.geometryNode;
Expand Down
42 changes: 41 additions & 1 deletion src/nodes/accessors/Position.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,28 @@
import { attribute } from '../core/AttributeNode.js';
import { Fn, vec3 } from '../tsl/TSLCore.js';
import { Fn, vec3, vec4 } from '../tsl/TSLCore.js';
import { modelWorldMatrix } from './ModelNode.js';
import { cameraProjectionMatrixInverse, cameraWorldMatrix } from './Camera.js';
import { warnOnce } from '../../utils.js';

/**
* TSL object that represents the clip space position of the current rendered object.
*
* @tsl
* @type {VaryingNode<vec4>}
*/
export const clipSpace = /*@__PURE__*/ ( Fn( ( builder ) => {

if ( builder.shaderStage !== 'fragment' ) {

warnOnce( 'TSL: `clipSpace` is only available in fragment stage.' );

return vec4();

}

return builder.context.clipSpace.toVarying( 'v_clipSpace' );

} ).once() )();

/**
* TSL object that represents the position attribute of the current rendered object.
Expand Down Expand Up @@ -35,6 +57,14 @@ export const positionPrevious = /*@__PURE__*/ positionGeometry.toVarying( 'posit
*/
export const positionWorld = /*@__PURE__*/ ( Fn( ( builder ) => {

if ( builder.shaderStage === 'fragment' && builder.material.vertexNode ) {

// reconstruct world position from view position

return cameraWorldMatrix.mul( positionView ).xyz.toVar( 'positionWorld' );

}

return modelWorldMatrix.mul( positionLocal ).xyz.toVarying( builder.getSubBuildProperty( 'v_positionWorld' ) );

}, 'vec3' ).once( [ 'POSITION' ] ) )();
Expand All @@ -61,6 +91,16 @@ export const positionWorldDirection = /*@__PURE__*/ ( Fn( () => {
*/
export const positionView = /*@__PURE__*/ ( Fn( ( builder ) => {

if ( builder.shaderStage === 'fragment' && builder.material.vertexNode ) {

// reconstruct view position from clip space

const viewPos = cameraProjectionMatrixInverse.mul( clipSpace );

return viewPos.xyz.div( viewPos.w ).toVar( 'positionView' );

}

return builder.context.setupPositionView().toVarying( 'v_positionView' );

}, 'vec3' ).once( [ 'POSITION' ] ) )();
Expand Down
4 changes: 2 additions & 2 deletions src/nodes/core/NodeBuilder.js
Original file line number Diff line number Diff line change
Expand Up @@ -2916,9 +2916,9 @@ class NodeBuilder {

this.setBuildStage( buildStage );

if ( this.context.vertex && this.context.vertex.isNode ) {
if ( this.context.position && this.context.position.isNode ) {

this.flowNodeFromShaderStage( 'vertex', this.context.vertex );
this.flowNodeFromShaderStage( 'vertex', this.context.position );

}

Expand Down
50 changes: 36 additions & 14 deletions src/nodes/lighting/ShadowNode.js
Original file line number Diff line number Diff line change
Expand Up @@ -518,24 +518,40 @@ class ShadowNode extends ShadowBaseNode {

let shadowColor;

if ( shadowMap.texture.isCubeTexture ) {
if ( renderer.shadowMap.color === true ) {

// For cube shadow maps (point lights), use cubeTexture with vec3 coordinates
shadowColor = cubeTexture( shadowMap.texture, shadowCoord.xyz );
if ( shadowMap.texture.isCubeTexture ) {

} else {
// For cube shadow maps (point lights), use cubeTexture with vec3 coordinates
shadowColor = cubeTexture( shadowMap.texture, shadowCoord.xyz );

shadowColor = texture( shadowMap.texture, shadowCoord );
} else {

if ( depthTexture.isArrayTexture ) {
shadowColor = texture( shadowMap.texture, shadowCoord );

shadowColor = shadowColor.depth( this.depthLayer );
if ( depthTexture.isArrayTexture ) {

shadowColor = shadowColor.depth( this.depthLayer );

}

}

}

const shadowOutput = mix( 1, shadowNode.rgb.mix( shadowColor, 1 ), shadowIntensity.mul( shadowColor.a ) ).toVar();
//

let shadowOutput;

if ( shadowColor ) {

shadowOutput = mix( 1, shadowNode.rgb.mix( shadowColor, 1 ), shadowIntensity.mul( shadowColor.a ) ).toVar();

} else {

shadowOutput = mix( 1, shadowNode, shadowIntensity ).toVar();

}

this.shadowMap = shadowMap;
this.shadow.map = shadowMap;
Expand All @@ -544,17 +560,23 @@ class ShadowNode extends ShadowBaseNode {

const inspectName = `${ this.light.type } Shadow [ ${ this.light.name || 'ID: ' + this.light.id } ]`;

return shadowOutput.toInspector( `${ inspectName } / Color`, () => {
if ( shadowColor ) {

if ( this.shadowMap.texture.isCubeTexture ) {
shadowOutput.toInspector( `${ inspectName } / Color`, () => {

return cubeTexture( this.shadowMap.texture );
if ( this.shadowMap.texture.isCubeTexture ) {

}
return cubeTexture( this.shadowMap.texture );

return texture( this.shadowMap.texture );
}

return texture( this.shadowMap.texture );

} );

}

} ).toInspector( `${ inspectName } / Depth`, () => {
return shadowOutput.toInspector( `${ inspectName } / Depth`, () => {

// TODO: Use linear depth

Expand Down
27 changes: 25 additions & 2 deletions src/renderers/common/Renderer.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ import { Vector4 } from '../../math/Vector4.js';
import { RenderTarget } from '../../core/RenderTarget.js';
import { DoubleSide, BackSide, FrontSide, SRGBColorSpace, NoToneMapping, LinearFilter, HalfFloatType, RGBAFormat, PCFShadowMap } from '../../constants.js';

import { float, vec3, vec4 } from '../../nodes/tsl/TSLCore.js';
import { float, vec3, vec4, Fn } from '../../nodes/tsl/TSLCore.js';
import { reference } from '../../nodes/accessors/ReferenceNode.js';
import { highpModelNormalViewMatrix, highpModelViewMatrix } from '../../nodes/accessors/ModelNode.js';
import { context } from '../../nodes/core/ContextNode.js';
Expand Down Expand Up @@ -658,6 +658,7 @@ class Renderer {
* Shadow map configuration
* @typedef {Object} ShadowMapConfig
* @property {boolean} enabled - Whether to globally enable shadows or not.
* @property {boolean} color - Whether to include shadow color or not.
* @property {number} type - The shadow map type.
*/

Expand All @@ -668,6 +669,7 @@ class Renderer {
*/
this.shadowMap = {
enabled: false,
color: false,
type: PCFShadowMap
};

Expand Down Expand Up @@ -3035,12 +3037,13 @@ class Renderer {
const hasMap = material.map !== null;
const hasColorNode = material.colorNode && material.colorNode.isNode;
const hasCastShadowNode = material.castShadowNode && material.castShadowNode.isNode;
const hasMaskNode = ( material.maskShadowNode && material.maskShadowNode.isNode ) || ( material.maskNode && material.maskNode.isNode );

let positionNode = null;
let colorNode = null;
let depthNode = null;

if ( hasMap || hasColorNode || hasCastShadowNode ) {
if ( hasMap || hasColorNode || hasCastShadowNode || hasMaskNode ) {

let shadowRGB;
let shadowAlpha;
Expand All @@ -3050,6 +3053,12 @@ class Renderer {
shadowRGB = material.castShadowNode.rgb;
shadowAlpha = material.castShadowNode.a;

if ( this.shadowMap.color !== true ) {

warnOnce( 'Renderer: `shadowMap.color` needs to be enabled when using `material.castShadowNode`.' );

}

} else {

shadowRGB = vec3( 0 );
Expand All @@ -3071,6 +3080,20 @@ class Renderer {

colorNode = vec4( shadowRGB, shadowAlpha );

if ( hasMaskNode ) {

const maskNode = material.maskShadowNode || material.maskNode;

colorNode = Fn( ( [ color ] ) => {

maskNode.not().discard();

return color;

} )( colorNode );

}

}

if ( material.depthNode && material.depthNode.isNode ) {
Expand Down
4 changes: 2 additions & 2 deletions src/renderers/webgpu/nodes/WGSLNodeBuilder.js
Original file line number Diff line number Diff line change
Expand Up @@ -1663,7 +1663,7 @@ ${ flowData.code }

if ( shaderStage === 'vertex' ) {

this.getBuiltin( 'position', 'Vertex', 'vec4<f32>', 'vertex' );
this.getBuiltin( 'position', 'builtinClipSpace', 'vec4<f32>', 'vertex' );

}

Expand Down Expand Up @@ -1961,7 +1961,7 @@ ${ flowData.code }

if ( shaderStage === 'vertex' ) {

flow += `varyings.Vertex = ${ flowSlotData.result };`;
flow += `varyings.builtinClipSpace = ${ flowSlotData.result };`;

} else if ( shaderStage === 'fragment' ) {

Expand Down