Skip to content

webGPU clip issue #32298

@menghuaa

Description

@menghuaa

Description

Hello, I am trying to replicate the WebGL clipping-cap example and draw the clipping cap using WebGPU (with forceWebGL = true). However, just like in the provided example, the clipped object itself cannot be rendered — only the clipping cap is drawn. When I set the clipped object's renderOrder to the minimum value, or when I comment out the clipping cap’s onAfterRender callback, the clipped object can be rendered. I don’t know what is causing this issue.

When I use the Spector frame-capture tool, unfortunately, the captured frames do render the clipped object, which makes it even harder for me to investigate the root cause. Additionally, I found that when I set forceWebGL to false, the clipped object can be rendered, but the clipping cap is drawn incorrectly.

Reproduction steps

1.Running the code
2. Set the clipped object's renderOrder to the minimum value Or Comment out the clipping cap’s onAfterRender callback
3.Set forceWebGL to false

Code

	import * as THREE from 'three/webgpu';

	import { Inspector } from 'three/addons/inspector/Inspector.js';

	import { OrbitControls } from 'three/addons/controls/OrbitControls.js';

	let camera, scene, renderer, startTime, object;

	init();

	function init() {

		camera = new THREE.PerspectiveCamera( 60, window.innerWidth / window.innerHeight, 0.25, 16 );

		camera.position.set( 5, 1.3, 3 );

		scene = new THREE.Scene();

		// Lights

		scene.add( new THREE.AmbientLight( 0xcccccc ) );

		const spotLight = new THREE.SpotLight( 0xffffff, 60 );
		spotLight.angle = Math.PI / 5;
		spotLight.penumbra = 0.2;
		spotLight.position.set( 2, 3, 3 );
		spotLight.castShadow = true;
		spotLight.shadow.camera.near = 3;
		spotLight.shadow.camera.far = 10;
		spotLight.shadow.mapSize.width = 2048;
		spotLight.shadow.mapSize.height = 2048;
		spotLight.shadow.bias = - 0.002;
		spotLight.shadow.radius = 4;
		scene.add( spotLight );

		const dirLight = new THREE.DirectionalLight( 0x55505a, 3 );
		dirLight.position.set( 0, 3, 0 );
		dirLight.castShadow = true;
		dirLight.shadow.camera.near = 1;
		dirLight.shadow.camera.far = 10;

		dirLight.shadow.camera.right = 1;
		dirLight.shadow.camera.left = - 1;
		dirLight.shadow.camera.top	= 1;
		dirLight.shadow.camera.bottom = - 1;

		dirLight.shadow.mapSize.width = 1024;
		dirLight.shadow.mapSize.height = 1024;
		scene.add( dirLight );

		// Clipping planes

		const localPlane1 = new THREE.Plane( new THREE.Vector3( 0, 1, 0 ), 0 );

		// Clipping Groups
		const knotClippingGroup = new THREE.ClippingGroup();
		knotClippingGroup.clippingPlanes = [ localPlane1, ];
		scene.add( knotClippingGroup );

		// Geometry

		const material = new THREE.MeshPhongNodeMaterial( {
			color: 0x80ee10,
			shininess: 0,
			side: THREE.DoubleSide,
			// ***** Clipping setup (material): *****
			alphaToCoverage: true
		} );

		const geometry = new THREE.CylinderGeometry( 2, 2 ,2, 32);

		object = new THREE.Mesh( geometry, material );
		object.castShadow = true;
		object.renderOrder = 5;
		knotClippingGroup.add( object );
		createPlaneStencilGroup( geometry,1, knotClippingGroup);
		const planeGeom = new THREE.PlaneGeometry( 4, 4 );
		const planematerial = new THREE.MeshStandardMaterial( {

			color: 0xE91E63,
			metalness: 0.1,
			roughness: 0.75,

			stencilWrite: true,
			stencilRef: 0,
			stencilFunc: THREE.NotEqualStencilFunc,
			stencilFail: THREE.ReplaceStencilOp,
			stencilZFail: THREE.ReplaceStencilOp,
			stencilZPass: THREE.ReplaceStencilOp,

		} );
		const po = new THREE.Mesh(planeGeom,planematerial);
		po.renderOrder = 2.5;
		po.onAfterRender = function ( renderer ) {

			renderer.clearStencil();

		};
		// Renderer
		localPlane1.coplanarPoint( po.position );
		po.lookAt(
			po.position.x - localPlane1.normal.x,
			po.position.y - localPlane1.normal.y,
			po.position.z - localPlane1.normal.z,
		);
		let group = new THREE.ClippingGroup();
		scene.add(group);
		group.add(po);
		renderer = new THREE.WebGPURenderer( { antialias: true,forceWebGL:true,stencil:true } );
		renderer.shadowMap.enabled = true;
		renderer.setPixelRatio( window.devicePixelRatio );
		renderer.setSize( window.innerWidth, window.innerHeight );
		renderer.setAnimationLoop( animate );
		renderer.inspector = new Inspector();
		window.addEventListener( 'resize', onWindowResize );
		document.body.appendChild( renderer.domElement );

		// Controls

		const controls = new OrbitControls( camera, renderer.domElement );
		controls.target.set( 0, 1, 0 );
		controls.update();

	}

	function onWindowResize() {

		camera.aspect = window.innerWidth / window.innerHeight;
		camera.updateProjectionMatrix();

		renderer.setSize( window.innerWidth, window.innerHeight );

	}

	function animate() {
		renderer.render( scene, camera );
	}
	function createPlaneStencilGroup( geometry,renderOrder, group) {


		const baseMat = new THREE.MeshBasicNodeMaterial();
		baseMat.depthWrite = false;
		baseMat.depthTest = false;
		baseMat.colorWrite = false;
		baseMat.stencilWrite = true;
		baseMat.stencilFunc = THREE.AlwaysStencilFunc;

		// back faces
		const mat0 = baseMat.clone();
		mat0.side = THREE.BackSide;
		mat0.stencilFail = THREE.IncrementWrapStencilOp;
		mat0.stencilZFail = THREE.IncrementWrapStencilOp;
		mat0.stencilZPass = THREE.IncrementWrapStencilOp;

		const mesh0 = new THREE.Mesh( geometry, mat0 );
		mesh0.renderOrder = renderOrder;
		group.add( mesh0 );

		// front faces
		const mat1 = baseMat.clone();
		mat1.side = THREE.FrontSide;
		mat1.stencilFail = THREE.DecrementWrapStencilOp;
		mat1.stencilZFail = THREE.DecrementWrapStencilOp;
		mat1.stencilZPass = THREE.DecrementWrapStencilOp;

		const mesh1 = new THREE.Mesh( geometry, mat1 );
		mesh1.renderOrder = renderOrder;

		group.add( mesh1 );

		return group;

	}

Live example

https://jsfiddle.net/ewd9nsqa/

Screenshots

Image

Version

r181

Device

Desktop

Browser

Chrome

OS

Windows

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions