未验证 提交 b64348cf 编写于 作者: W WestLangley 提交者: GitHub

Merge pull request #15611 from gkjohnson/add-material-stencil

Add Stencil Parameter to Materials
......@@ -93,6 +93,51 @@
[page:Constant AddOperation] adds the two colors.
</p>
<h2>Stencil Functions</h2>
<code>
THREE.NeverStencilFunc
THREE.LessStencilFunc
THREE.EqualStencilFunc
THREE.LessEqualStencilFunc
THREE.GreaterStencilFunc
THREE.NotEqualStencilFunc
THREE.GreaterEqualStencilFunc
THREE.AlwaysStencilFunc
</code>
<p>
Which stencil function the material uses to determine whether or not to perform a stencil operation.<br />
[page:Materials NeverStencilFunc] will never return true.<br />
[page:Materials LessStencilFunc] will return true if the stencil reference value is less than the current stencil value.<br />
[page:Materials EqualStencilFunc] will return true if the stencil reference value is equal to the current stencil value.<br />
[page:Materials LessEqualStencilFunc] will return true if the stencil reference value is less than or equal to the current stencil value.<br />
[page:Materials GreaterStencilFunc] will return true if the stencil reference value is greater than the current stencil value.<br />
[page:Materials NotEqualStencilFunc] will return true if the stencil reference value is not equal to the current stencil value.<br />
[page:Materials GreaterEqualStencilFunc] will return true if the stencil reference value is greater than or equal to the current stencil value.<br />
[page:Materials AlwaysStencilFunc] will always return true.<br />
</p>
<h2>Stencil Operations</h2>
<code>
THREE.ZeroStencilOp
THREE.KeepStencilOp
THREE.ReplaceStencilOp
THREE.IncrementStencilOp
THREE.DecrementStencilOp
THREE.IncrementWrapStencilOp
THREE.DecrementWrapStencilOp
THREE.InvertStencilOp
</code>
<p>
Which stencil operation the material will perform on the stencil buffer pixel if the provided stencil function passes.<br />
[page:Materials ZeroStencilOp] will set the stencil value to 0.<br />
[page:Materials KeepStencilOp] will not change the current stencil value.<br />
[page:Materials ReplaceStencilOp] will replace the stencil value with the specified stencil reference value.<br />
[page:Materials IncrementStencilOp] will increment the current stencil value by 1.<br />
[page:Materials DecrementStencilOp] will decrement the current stencil value by 1.<br />
[page:Materials IncrementWrapStencilOp] will increment the current stencil value by 1. If the value increments past 255 it will be set to 0.<br />
[page:Materials DecrementWrapStencilOp] will increment the current stencil value by 1. If the value decrements below 0 it will be set to 255.<br />
[page:Materials InvertStencilOp] will perform a bitwise iversion of the current stencil value.<br />
</p>
<h2>Source</h2>
......
......@@ -123,6 +123,41 @@
When drawing 2D overlays it can be useful to disable the depth writing in order to layer several things together without creating z-index artifacts.
</p>
<h3>[property:Boolean stencilWrite]</h3>
<p>
Whether rendering this material has any effect on the stencil buffer. Default is *false*.
</p>
<h3>[property:Boolean stencilFunc]</h3>
<p>
The stencil comparison function to use. Default is [page:Materials AlwaysStencilFunc]. See stencil function [page:Materials constants] for all possible values.
</p>
<h3>[property:Integer stencilRef]</h3>
<p>
The value to use when performing stencil comparisons or stencil operations. Default is *0*.
</p>
<h3>[property:Boolean stencilMask]</h3>
<p>
The bit mask to use when comparing against or writing to the stencil buffer. Default is *0xFF*.
</p>
<h3>[property:Integer stencilFail]</h3>
<p>
Which stencil operation to perform when the comparison function returns false. Default is [page:Materials KeepStencilOp]. See the stencil operations [page:Materials constants] for all possible values.
</p>
<h3>[property:Boolean stencilZFail]</h3>
<p>
Which stencil operation to perform when the comparison function returns true but the depth test fails. Default is [page:Materials KeepStencilOp]. See the stencil operations [page:Materials constants] for all possible values.
</p>
<h3>[property:Boolean stencilZPass]</h3>
<p>
Which stencil operation to perform when the comparison function returns true and the depth test passes. Default is [page:Materials KeepStencilOp]. See the stencil operations [page:Materials constants] for all possible values.
</p>
<h3>[property:Boolean flatShading]</h3>
<p>
Define whether the material is rendered with flat shading. Default is false.
......
......@@ -12,6 +12,7 @@ var files = {
"webgl_clipping",
"webgl_clipping_advanced",
"webgl_clipping_intersection",
"webgl_clipping_stencil",
"webgl_decals",
"webgl_depth_texture",
"webgl_effects_anaglyph",
......
<!DOCTYPE html>
<html lang="en">
<head>
<title>three.js webgl - clipping stencil</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
<style>
body {
color: #ffffff;
font-family:Monospace;
font-size:13px;
text-align:center;
font-weight: bold;
background-color: #000000;
margin: 0px;
overflow: hidden;
}
#info {
color: #fff;
position: absolute;
top: 0px; width: 100%;
padding: 5px;
}
</style>
</head>
<body>
<div id="info"><a href="http://threejs.org" target="_blank" rel="noopener">three.js</a> - solid geometry with clip planes and stencil materials</div>
<script type="module">
import * as THREE from '../build/three.module.js';
import { OrbitControls } from './jsm/controls/OrbitControls.js';
import { GUI } from './jsm/libs/dat.gui.module.js';
import Stats from './jsm/libs/stats.module.js';
var camera, scene, renderer, startTime, object, stats;
var planes, planeObjects, planeHelpers;
var clock;
var params = {
animate: true,
planeX: {
constant: 0,
negated: false,
displayHelper: false
},
planeY: {
constant: 0,
negated: false,
displayHelper: false
},
planeZ: {
constant: 0,
negated: false,
displayHelper: false
}
};
init();
animate();
function createPlaneStencilGroup( geometry, plane, renderOrder ) {
var group = new THREE.Group();
var baseMat = new THREE.MeshBasicMaterial();
baseMat.depthWrite = false;
baseMat.depthTest = false;
baseMat.colorWrite = false;
baseMat.stencilWrite = true;
baseMat.stencilFunc = THREE.AlwaysStencilFunc;
// back faces
var mat0 = baseMat.clone();
mat0.side = THREE.BackSide;
mat0.clippingPlanes = [ plane ];
mat0.stencilFail = THREE.IncrementWrapStencilOp;
mat0.stencilZFail = THREE.IncrementWrapStencilOp;
mat0.stencilZPass = THREE.IncrementWrapStencilOp;
var mesh0 = new THREE.Mesh( geometry, mat0 );
mesh0.renderOrder = renderOrder;
group.add( mesh0 );
// front faces
var mat1 = baseMat.clone();
mat1.side = THREE.FrontSide;
mat1.clippingPlanes = [ plane ];
mat1.stencilFail = THREE.DecrementWrapStencilOp;
mat1.stencilZFail = THREE.DecrementWrapStencilOp;
mat1.stencilZPass = THREE.DecrementWrapStencilOp;
var mesh1 = new THREE.Mesh( geometry, mat1 );
mesh1.renderOrder = renderOrder;
group.add( mesh1 );
return group;
}
function init() {
clock = new THREE.Clock();
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera( 36, window.innerWidth / window.innerHeight, 1, 100 );
camera.position.set( 2, 2, 2 );
scene.add( new THREE.AmbientLight( 0xffffff, 0.5 ) );
var dirLight = new THREE.DirectionalLight( 0xffffff, 1 );
dirLight.position.set( 5, 10, 7.5 );
dirLight.castShadow = true;
dirLight.shadow.camera.right = 2;
dirLight.shadow.camera.left = - 2;
dirLight.shadow.camera.top = 2;
dirLight.shadow.camera.bottom = - 2;
dirLight.shadow.mapSize.width = 1024;
dirLight.shadow.mapSize.height = 1024;
scene.add( dirLight );
planes = [
new THREE.Plane( new THREE.Vector3( - 1, 0, 0 ), 0 ),
new THREE.Plane( new THREE.Vector3( 0, - 1, 0 ), 0 ),
new THREE.Plane( new THREE.Vector3( 0, 0, - 1 ), 0 )
];
planeHelpers = planes.map( p => new THREE.PlaneHelper( p, 2, 0xffffff ) );
planeHelpers.forEach( ph => {
ph.visible = false;
scene.add( ph );
} );
var geometry = new THREE.TorusKnotBufferGeometry( 0.4, 0.15, 220, 60 );
object = new THREE.Group();
scene.add( object );
// Set up clip plane rendering
planeObjects = [];
var planeGeom = new THREE.PlaneBufferGeometry( 4, 4 );
for ( var i = 0; i < 3; i ++ ) {
var poGroup = new THREE.Group();
var plane = planes[ i ];
var stencilGroup = createPlaneStencilGroup( geometry, plane, i + 1 )
// plane is clipped by the other clipping planes
var planeMat =
new THREE.MeshStandardMaterial( {
color: 0xE91E63,
metalness: 0.1,
roughness: 0.75,
clippingPlanes: planes.filter( p => p !== plane ),
stencilWrite: true,
stencilRef: 0,
stencilFunc: THREE.NotEqualStencilFunc,
stencilFail: THREE.ReplaceStencilOp,
stencilZFail: THREE.ReplaceStencilOp,
stencilZPass: THREE.ReplaceStencilOp,
} );
var po = new THREE.Mesh( planeGeom, planeMat );
po.onAfterRender = function ( renderer ) {
renderer.clearStencil();
};
po.renderOrder = i + 1.1;
object.add( stencilGroup );
poGroup.add( po );
planeObjects.push( po );
scene.add( poGroup );
}
var material = new THREE.MeshStandardMaterial( {
color: 0xFFC107,
metalness: 0.1,
roughness: 0.75,
clippingPlanes: planes,
clipShadows: true,
shadowSide: THREE.DoubleSide,
} );
// add the color
var clippedColorFront = new THREE.Mesh( geometry, material );
clippedColorFront.castShadow = true;
clippedColorFront.renderOrder = 6;
object.add( clippedColorFront );
var ground = new THREE.Mesh(
new THREE.PlaneBufferGeometry( 9, 9, 1, 1 ),
new THREE.ShadowMaterial( { color: 0, opacity: 0.25, side: THREE.DoubleSide } )
);
ground.rotation.x = - Math.PI / 2; // rotates X/Y to X/Z
ground.position.y = - 1;
ground.receiveShadow = true;
scene.add( ground );
// Stats
stats = new Stats();
document.body.appendChild( stats.dom );
// Renderer
renderer = new THREE.WebGLRenderer( { antialias: true } );
renderer.shadowMap.enabled = true;
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
renderer.setClearColor( 0x263238 );
window.addEventListener( 'resize', onWindowResize, false );
document.body.appendChild( renderer.domElement );
renderer.localClippingEnabled = true;
// Controls
var controls = new OrbitControls( camera, renderer.domElement );
controls.minDistance = 2;
controls.maxDistance = 20;
controls.update();
// GUI
var gui = new GUI();
gui.add( params, 'animate' );
var planeX = gui.addFolder( 'planeX' );
planeX.add( params.planeX, 'displayHelper' ).onChange( v => planeHelpers[ 0 ].visible = v );
planeX.add( params.planeX, 'constant' ).min( - 1 ).max( 1 ).onChange( d => planes[ 0 ].constant = d );
planeX.add( params.planeX, 'negated' ).onChange( d => {
planes[ 0 ].negate();
params.planeX.constant = planes[ 0 ].constant;
} );
planeX.open();
var planeY = gui.addFolder( 'planeY' );
planeY.add( params.planeY, 'displayHelper' ).onChange( v => planeHelpers[ 1 ].visible = v );
planeY.add( params.planeY, 'constant' ).min( - 1 ).max( 1 ).onChange( d => planes[ 1 ].constant = d );
planeY.add( params.planeY, 'negated' ).onChange( d => {
planes[ 1 ].negate();
params.planeY.constant = planes[ 1 ].constant;
} );
planeY.open();
var planeZ = gui.addFolder( 'planeZ' );
planeZ.add( params.planeZ, 'displayHelper' ).onChange( v => planeHelpers[ 2 ].visible = v );
planeZ.add( params.planeZ, 'constant' ).min( - 1 ).max( 1 ).onChange( d => planes[ 2 ].constant = d );
planeZ.add( params.planeZ, 'negated' ).onChange( d => {
planes[ 2 ].negate();
params.planeZ.constant = planes[ 2 ].constant;
} );
planeZ.open();
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight );
}
function animate() {
var delta = clock.getDelta();
requestAnimationFrame( animate );
if ( params.animate ) {
object.rotation.x += delta * 0.5;
object.rotation.y += delta * 0.2;
}
for ( var i = 0; i < planeObjects.length; i ++ ) {
var plane = planes[ i ];
var po = planeObjects[ i ];
plane.coplanarPoint( po.position );
po.lookAt(
po.position.x - plane.normal.x,
po.position.y - plane.normal.y,
po.position.z - plane.normal.z,
);
}
stats.begin();
renderer.render( scene, camera );
stats.end();
}
</script>
</body>
</html>
......@@ -252,3 +252,25 @@ export const RGBADepthPacking: DepthPackingStrategies;
export enum NormalMapTypes {}
export const TangentSpaceNormalMap: NormalMapTypes;
export const ObjectSpaceNormalMap: NormalMapTypes;
// Stencil Op types
export enum StencilOp {}
export const ZeroStencilOp: StencilOp;
export const KeepStencilOp: StencilOp;
export const ReplaceStencilOp: StencilOp;
export const IncrementStencilOp: StencilOp;
export const DecrementStencilOp: StencilOp;
export const IncrementWrapStencilOp: StencilOp;
export const DecrementWrapStencilOp: StencilOp;
export const InvertStencilOp: StencilOp;
// Stencil Func types
export enum StencilFunc {}
export const NeverStencilFunc: StencilFunc;
export const LessStencilFunc: StencilFunc;
export const EqualStencilFunc: StencilFunc;
export const LessEqualStencilFunc: StencilFunc;
export const GreaterStencilFunc: StencilFunc;
export const NotEqualStencilFunc: StencilFunc;
export const GreaterEqualStencilFunc: StencilFunc;
export const AlwaysStencilFunc: StencilFunc;
......@@ -147,3 +147,21 @@ export var BasicDepthPacking = 3200;
export var RGBADepthPacking = 3201;
export var TangentSpaceNormalMap = 0;
export var ObjectSpaceNormalMap = 1;
export var ZeroStencilOp = 0;
export var KeepStencilOp = 7680;
export var ReplaceStencilOp = 7681;
export var IncrementStencilOp = 7682;
export var DecrementStencilOp = 7683;
export var IncrementWrapStencilOp = 34055;
export var DecrementWrapStencilOp = 34056;
export var InvertStencilOp = 5386;
export var NeverStencilFunc = 512;
export var LessStencilFunc = 513;
export var EqualStencilFunc = 514;
export var LessEqualStencilFunc = 515;
export var GreaterStencilFunc = 516;
export var NotEqualStencilFunc = 517;
export var GreaterEqualStencilFunc = 518;
export var AlwaysStencilFunc = 519;
......@@ -10,6 +10,8 @@ import {
DepthModes,
Side,
Colors,
StencilFunc,
StencilOp
} from '../constants';
// Materials //////////////////////////////////////////////////////////////////////////////////
......@@ -49,6 +51,13 @@ export interface MaterialParameters {
vertexColors?: Colors;
vertexTangents?: boolean;
visible?: boolean;
stencilWrite?: boolean;
stencilFunc?: StencilFunc;
stencilRef?: number;
stencilMask?: number;
stencilFail?: StencilOp;
stencilZFail?: StencilOp;
stencilZPass?: StencilOp;
}
/**
......@@ -144,6 +153,41 @@ export class Material extends EventDispatcher {
*/
id: number;
/**
* Whether rendering this material has any effect on the stencil buffer. Default is *false*.
*/
stencilWrite: boolean;
/**
* The stencil comparison function to use. Default is {@link AlwaysStencilFunc}. See stencil operation constants for all possible values.
*/
stencilFunc: StencilFunc;
/**
* The value to use when performing stencil comparisons or stencil operations. Default is *0*.
*/
stencilRef: number;
/**
* The bit mask to use when comparing against or writing to the stencil buffer. Default is *0xFF*.
*/
stencilMask: number;
/**
* Which stencil operation to perform when the comparison function returns false. Default is {@link KeepStencilOp}. See the stencil operation constants for all possible values.
*/
stencilFail: StencilOp;
/**
* Which stencil operation to perform when the comparison function returns true but the depth test fails. Default is {@link KeepStencilOp}. See the stencil operation constants for all possible values.
*/
stencilZFail: StencilOp;
/**
* Which stencil operation to perform when the comparison function returns true and the depth test passes. Default is {@link KeepStencilOp}. See the stencil operation constants for all possible values.
*/
stencilZPass: StencilOp;
/**
* Used to check whether this or derived classes are materials. Default is true.
* You should not change this, as it used internally for optimisation.
......
import { EventDispatcher } from '../core/EventDispatcher.js';
import { NoColors, FrontSide, FlatShading, NormalBlending, LessEqualDepth, AddEquation, OneMinusSrcAlphaFactor, SrcAlphaFactor } from '../constants.js';
import { NoColors, FrontSide, FlatShading, NormalBlending, LessEqualDepth, AddEquation, OneMinusSrcAlphaFactor, SrcAlphaFactor, AlwaysStencilFunc, KeepStencilOp } from '../constants.js';
import { _Math } from '../math/Math.js';
/**
......@@ -41,6 +41,14 @@ function Material() {
this.depthTest = true;
this.depthWrite = true;
this.stencilFunc = AlwaysStencilFunc;
this.stencilRef = 0;
this.stencilMask = 0xff;
this.stencilFail = KeepStencilOp;
this.stencilZFail = KeepStencilOp;
this.stencilZPass = KeepStencilOp;
this.stencilWrite = false;
this.clippingPlanes = null;
this.clipIntersection = false;
this.clipShadows = false;
......@@ -240,6 +248,14 @@ Material.prototype = Object.assign( Object.create( EventDispatcher.prototype ),
data.depthTest = this.depthTest;
data.depthWrite = this.depthWrite;
data.stencilWrite = this.stencilWrite;
data.stencilFunc = this.stencilFunc;
data.stencilRef = this.stencilRef;
data.stencilMask = this.stencilMask;
data.stencilFail = this.stencilFail;
data.stencilZFail = this.stencilZFail;
data.stencilZPass = this.stencilZPass;
// rotation (SpriteMaterial)
if ( this.rotation && this.rotation !== 0 ) data.rotation = this.rotation;
......@@ -333,6 +349,14 @@ Material.prototype = Object.assign( Object.create( EventDispatcher.prototype ),
this.depthTest = source.depthTest;
this.depthWrite = source.depthWrite;
this.stencilWrite = source.stencilWrite;
this.stencilFunc = source.stencilFunc;
this.stencilRef = source.stencilRef;
this.stencilMask = source.stencilMask;
this.stencilFail = source.stencilFail;
this.stencilZFail = source.stencilZFail;
this.stencilZPass = source.stencilZPass;
this.colorWrite = source.colorWrite;
this.precision = source.precision;
......
......@@ -677,6 +677,15 @@ function WebGLState( gl, extensions, utils, capabilities ) {
depthBuffer.setMask( material.depthWrite );
colorBuffer.setMask( material.colorWrite );
var stencilWrite = material.stencilWrite;
stencilBuffer.setTest( stencilWrite );
if ( stencilWrite ) {
stencilBuffer.setFunc( material.stencilFunc, material.stencilRef, material.stencilMask );
stencilBuffer.setOp( material.stencilFail, material.stencilZFail, material.stencilZPass );
}
setPolygonOffset( material.polygonOffset, material.polygonOffsetFactor, material.polygonOffsetUnits );
}
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册