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: hardware clipping support. #28578

Open
wants to merge 1 commit into
base: dev
Choose a base branch
from
Open
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
28 changes: 28 additions & 0 deletions examples/jsm/nodes/accessors/BuiltinNode.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import Node, { addNodeClass } from '../core/Node.js';
import { nodeProxy } from '../shadernode/ShaderNode.js';

class BuiltinNode extends Node {

constructor( name ) {

super( 'float' );

this.name = name;

this.isBuiltinNode = true;

}

generate( /* builder */ ) {

return this.name;

}

}

export default BuiltinNode;

export const builtin = nodeProxy( BuiltinNode );

addNodeClass( 'BuiltinhNode', BuiltinNode );
30 changes: 30 additions & 0 deletions examples/jsm/nodes/accessors/ClippingNode.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { tslFn } from '../shadernode/ShaderNode.js';
import { loop } from '../utils/LoopNode.js';
import { smoothstep } from '../math/MathNode.js';
import { uniforms } from './UniformsNode.js';
import { builtin } from './BuiltinNode.js';

class ClippingNode extends Node {

Expand All @@ -32,6 +33,10 @@ class ClippingNode extends Node {

return this.setupAlphaToCoverage( clippingContext.planes, numClippingPlanes, numUnionClippingPlanes );

} else if ( this.scope === ClippingNode.HARDWARE ) {

return this.setupHardwareClipping( clippingContext.planes, numUnionClippingPlanes, builder );

} else {

return this.setupDefault( clippingContext.planes, numClippingPlanes, numUnionClippingPlanes );
Expand Down Expand Up @@ -133,13 +138,38 @@ class ClippingNode extends Node {

}

setupHardwareClipping( planes, numUnionClippingPlanes, builder ) {

return tslFn( () => {

const clippingPlanes = uniforms( planes );
let plane;

const hw_clip_distances = builtin( builder.getClipDistance() );

for ( let i = 0; i < numUnionClippingPlanes; i ++ ) {

plane = clippingPlanes.element( i );

const distance = positionView.dot( plane.xyz ).sub( plane.w ).negate();
hw_clip_distances.element( i ).assign( distance );

}

} )();

}

}

ClippingNode.ALPHA_TO_COVERAGE = 'alphaToCoverage';
ClippingNode.DEFAULT = 'default';
ClippingNode.HARDWARE = 'hardware';

export default ClippingNode;

export const clipping = () => nodeObject( new ClippingNode() );

export const clippingAlpha = () => nodeObject( new ClippingNode( ClippingNode.ALPHA_TO_COVERAGE ) );

export const hardwareClipping = () => nodeObject( new ClippingNode( ClippingNode.HARDWARE ) );
29 changes: 27 additions & 2 deletions examples/jsm/nodes/materials/NodeMaterial.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import EnvironmentNode from '../lighting/EnvironmentNode.js';
import IrradianceNode from '../lighting/IrradianceNode.js';
import { depthPixel } from '../display/ViewportDepthNode.js';
import { cameraLogDepth } from '../accessors/CameraNode.js';
import { clipping, clippingAlpha } from '../accessors/ClippingNode.js';
import { clipping, clippingAlpha, hardwareClipping } from '../accessors/ClippingNode.js';
import { faceDirection } from '../display/FrontFacingNode.js';

const NodeMaterials = new Map();
Expand All @@ -42,6 +42,7 @@ class NodeMaterial extends Material {
this.fog = true;
this.lights = true;
this.normals = true;
this.hardwareClipping = false;

this.lightsNode = null;
this.envNode = null;
Expand Down Expand Up @@ -146,7 +147,7 @@ class NodeMaterial extends Material {

setupClipping( builder ) {

if ( builder.clippingContext === null ) return null;
if ( builder.clippingContext === null || this.hardwareClipping === true ) return null;

const { globalClippingCount, localClippingCount } = builder.clippingContext;

Expand All @@ -171,6 +172,28 @@ class NodeMaterial extends Material {

}

setupHardwareClipping( builder ) {

if ( builder.clippingContext === null ) return null;

const { localClipIntersection } = builder.clippingContext;

if ( ! localClipIntersection && ! this.alphaToCoverage && builder.enableHardwareClipping() ) {

builder.stack.add( hardwareClipping() );

this.hardwareClipping = true;

} else {

this.hardwareClipping = false;

}

return;

}

setupDepth( builder ) {

const { renderer } = builder;
Expand Down Expand Up @@ -244,6 +267,8 @@ class NodeMaterial extends Material {

}

this.setupHardwareClipping( builder );

const mvp = modelViewProjection();

builder.context.vertex = builder.removeStack();
Expand Down
1 change: 1 addition & 0 deletions examples/jsm/renderers/common/ClippingContext.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ class ClippingContext {

this.parentVersion = 0;
this.viewNormalMatrix = new Matrix3();
this.hardwareClippingPlanes = 0;

}

Expand Down
6 changes: 6 additions & 0 deletions examples/jsm/renderers/common/RenderObject.js
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,12 @@ export default class RenderObject {

}

get hardwareClippingPlanes() {

return this.clippingContext.hardwareClippingPlanes;

}

getNodeBuilderState() {

return this._nodeBuilderState || ( this._nodeBuilderState = this._nodes.getForRender( this ) );
Expand Down
4 changes: 2 additions & 2 deletions examples/jsm/renderers/webgl/WebGLBackend.js
Original file line number Diff line number Diff line change
Expand Up @@ -569,7 +569,7 @@ class WebGLBackend extends Backend {

draw( renderObject, info ) {

const { object, pipeline, material, context } = renderObject;
const { object, pipeline, material, context, hardwareClippingPlanes } = renderObject;
const { programGPU } = this.get( pipeline );

const { gl, state } = this;
Expand All @@ -582,7 +582,7 @@ class WebGLBackend extends Backend {

const frontFaceCW = ( object.isMesh && object.matrixWorld.determinant() < 0 );

state.setMaterial( material, frontFaceCW );
state.setMaterial( material, frontFaceCW, hardwareClippingPlanes );

gl.useProgram( programGPU );

Expand Down
31 changes: 31 additions & 0 deletions examples/jsm/renderers/webgl/nodes/GLSLNodeBuilder.js
Original file line number Diff line number Diff line change
Expand Up @@ -549,6 +549,12 @@ ${ flowData.code }

}

getClipDistance() {

return 'gl_ClipDistance';

}

isAvailable( name ) {

return supports[ name ] === true;
Expand All @@ -561,6 +567,29 @@ ${ flowData.code }

}

enableHardwareClipping() {

const renderer = this.renderer;
const { localClippingCount, globalClippingCount } = this.clippingContext;
const planeCount = localClippingCount + globalClippingCount;

if ( planeCount === 0 ) return false;

const gl = renderer.getContext();
const ext = gl.getExtension( 'WEBGL_clip_cull_distance' );

if ( ext && planeCount <= gl.getParameter( ext.MAX_CLIP_DISTANCES_WEBGL ) ) {

this.clippingContext.hardwareClippingPlanes = planeCount;

return true;

}

return false;

}

registerTransform( varyingName, attributeNode ) {

this.transforms.push( { varyingName, attributeNode } );
Expand Down Expand Up @@ -602,6 +631,8 @@ ${vars}

${ this.getSignature() }

${ this.hardwareClippingPlanes !== 0 ? '#extension GL_ANGLE_clip_cull_distance : enable' : '' }

// precision
${ defaultPrecisions }

Expand Down
28 changes: 27 additions & 1 deletion examples/jsm/renderers/webgl/utils/WebGLState.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ class WebGLState {
this.currentStencilZPass = null;
this.currentStencilMask = null;
this.currentLineWidth = null;
this.currentClippingPlanes = 0;

this.currentBoundFramebuffers = {};
this.currentDrawbuffers = new WeakMap();
Expand Down Expand Up @@ -477,7 +478,7 @@ class WebGLState {

}

setMaterial( material, frontFaceCW ) {
setMaterial( material, frontFaceCW, hardwareClippingPlanes ) {

const { gl } = this;

Expand Down Expand Up @@ -515,6 +516,31 @@ class WebGLState {
? this.enable( gl.SAMPLE_ALPHA_TO_COVERAGE )
: this.disable( gl.SAMPLE_ALPHA_TO_COVERAGE );


if ( hardwareClippingPlanes > 0 ) {

if ( this.currentClippingPlanes !== hardwareClippingPlanes ) {

const CLIP_DISTANCE0_WEBGL = 0x3000;

for ( let i = 0; i < 8; i ++ ) {

if ( i < hardwareClippingPlanes ) {

this.enable( CLIP_DISTANCE0_WEBGL + i );

} else {

this.disable( CLIP_DISTANCE0_WEBGL + i );

}

}

}

}

}

setPolygonOffset( polygonOffset, factor, units ) {
Expand Down
24 changes: 23 additions & 1 deletion examples/jsm/renderers/webgpu/nodes/WGSLNodeBuilder.js
Original file line number Diff line number Diff line change
Expand Up @@ -594,12 +594,34 @@ ${ flowData.code }

}

getClipDistance() {

return 'hw_clip_distances';

}

isFlipY() {

return false;

}

enableHardwareClipping() {

const { localClippingCount, globalClippingCount } = this.clippingContext;
const planeCount = localClippingCount + globalClippingCount;

if ( planeCount > 0 && planeCount <= 8 && this.renderer.backend.hasFeature( 'clip-distances' ) ) {

this.getBuiltin( 'clip_distances', 'hw_clip_distances', `array<f32, ${ planeCount } >`, 'vertex' );
return true;

}

return false;

}

getBuiltins( shaderStage ) {

const snippets = [];
Expand Down Expand Up @@ -689,7 +711,7 @@ ${ flowData.code }

snippets.push( snippet );

snippets.push( `\nvar<private> output : ${ name };\n\n`);
snippets.push( `\nvar<private> output : ${ name };\n\n` );

}

Expand Down
Loading