├── .gitignore
├── .vscode
└── launch.json
├── LICENSE
├── README.md
├── dist
└── index.html
├── package-lock.json
├── package.json
├── screenshot01.png
├── src
└── index.ts
├── tsconfig.json
└── webpack.config.js
/.gitignore:
--------------------------------------------------------------------------------
1 |
2 | .cache/
3 | coverage/
4 | dist/*
5 | !dist/glb
6 | !dist/textures
7 | !dist/index.html
8 | node_modules/
9 | *.log
10 |
11 | # OS generated files
12 | .DS_Store
13 | .DS_Store?
14 | ._*
15 | .Spotlight-V100
16 | .Trashes
17 | ehthumbs.db
18 | Thumbs.db
--------------------------------------------------------------------------------
/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | {
2 | // Use IntelliSense to learn about possible attributes.
3 | // Hover to view descriptions of existing attributes.
4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
5 | "version": "0.2.0",
6 | "configurations": [
7 | {
8 | "type": "chrome",
9 | "request": "launch",
10 | "name": "Launch Chrome against localhost",
11 | "url": "http://localhost:8080",
12 | "webRoot": "${workspaceFolder}"
13 | }
14 | ]
15 | }
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2022 tamani-coding
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # threejs-stencil-buffer-example
2 | threejs-stencil-buffer-example
3 |
4 |
5 | Try the [Stackblitz](https://stackblitz.com/github/tamani-coding/threejs-stencil-buffer-example)
6 |
7 | 
8 |
9 | Use `npm install` and then `npm run start`.
10 |
--------------------------------------------------------------------------------
/dist/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | three.js example
7 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "threejs-example",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "index.js",
6 | "scripts": {
7 | "start": "npm run build && npm run serve",
8 | "build": "webpack",
9 | "serve": "webpack serve"
10 | },
11 | "keywords": [],
12 | "author": "",
13 | "license": "ISC",
14 | "devDependencies": {
15 | "npm-run-all": "^4.1.5",
16 | "ts-loader": "^9.4.2",
17 | "typescript": "^4.9.4",
18 | "webpack": "5.75.0",
19 | "webpack-cli": "5.0.1",
20 | "webpack-dev-server": "4.11.1"
21 | },
22 | "dependencies": {
23 | "@types/three": "^0.146.0",
24 | "dat.gui": "^0.7.9",
25 | "three": "^0.148.0"
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/screenshot01.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tamani-coding/threejs-stencil-buffer-example/e321c93848aea9d0210228cc093955ade7b22f6a/screenshot01.png
--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------
1 | import * as THREE from 'three';
2 | import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls';
3 | import { GUI } from 'dat.gui'
4 |
5 |
6 | const params = {
7 |
8 | plane01: {
9 |
10 | constant: 1,
11 | negated: false,
12 | displayHelper: true
13 |
14 | },
15 |
16 | stencilMesh: {
17 | z: 0
18 | },
19 |
20 | };
21 |
22 | const clock = new THREE.Clock();
23 |
24 | const scene = new THREE.Scene();
25 |
26 | const camera = new THREE.PerspectiveCamera(36, window.innerWidth / window.innerHeight, 1, 100);
27 | camera.position.set(4, 5, 4);
28 |
29 | // LIGHTS
30 | scene.add(new THREE.AmbientLight(0xffffff, 0.9));
31 |
32 | const dirLight = new THREE.DirectionalLight(0xffffff, 1);
33 | dirLight.position.set(5, 10, 7.5);
34 | dirLight.castShadow = true;
35 | dirLight.shadow.camera.right = 2;
36 | dirLight.shadow.camera.left = - 2;
37 | dirLight.shadow.camera.top = 2;
38 | dirLight.shadow.camera.bottom = - 2;
39 | dirLight.shadow.mapSize.width = 1024;
40 | dirLight.shadow.mapSize.height = 1024;
41 | scene.add(dirLight);
42 |
43 | // RENDERER
44 | const renderer = new THREE.WebGLRenderer({ antialias: true });
45 | renderer.shadowMap.enabled = true;
46 | renderer.setPixelRatio(window.devicePixelRatio);
47 | renderer.setSize(window.innerWidth, window.innerHeight);
48 | renderer.setClearColor(0x453C67);
49 | window.addEventListener('resize', onWindowResize);
50 | document.body.appendChild(renderer.domElement);
51 |
52 |
53 | // CONTROLS
54 | const controls = new OrbitControls(camera, renderer.domElement);
55 | controls.minDistance = 2;
56 | controls.maxDistance = 20;
57 | controls.update();
58 |
59 | // GUI
60 | const gui = new GUI();
61 |
62 | // initPlanes();
63 | initCube();
64 | animate();
65 |
66 | function initCube() {
67 |
68 | const addCubeFace = (objectGeom: THREE.BufferGeometry, objectColor: string,
69 | stencilRef: number, planePos: THREE.Vector3, planeRot: THREE.Vector3 ) => {
70 |
71 | // CUBE FACE
72 | const planeGeom = new THREE.PlaneGeometry();
73 | const stencilMat = new THREE.MeshPhongMaterial({ color: 'white' });
74 | stencilMat.depthWrite = false;
75 | stencilMat.stencilWrite = true;
76 | stencilMat.stencilRef = stencilRef;
77 | stencilMat.stencilFunc = THREE.AlwaysStencilFunc;
78 | stencilMat.stencilZPass = THREE.ReplaceStencilOp;
79 | const stencilMesh = new THREE.Mesh(planeGeom, stencilMat);
80 | stencilMesh.position.copy(planePos);
81 | stencilMesh.rotation.x = planeRot.x;
82 | stencilMesh.rotation.y = planeRot.y;
83 | stencilMesh.rotation.z = planeRot.z;
84 | stencilMesh.scale.multiplyScalar(0.9);
85 | scene.add(stencilMesh);
86 |
87 | // OBJECT INSIDE CUBE
88 | const objectMat = new THREE.MeshPhongMaterial({ color: objectColor});
89 | objectMat.stencilWrite = true;
90 | objectMat.stencilRef = stencilRef;
91 | objectMat.stencilFunc = THREE.EqualStencilFunc;
92 | const object = new THREE.Mesh(objectGeom, objectMat);
93 | scene.add(object);
94 | }
95 |
96 | addCubeFace(new THREE.ConeGeometry(0.25, 0.5, 4), 'red', 1, new THREE.Vector3(0,0,0.5), new THREE.Vector3(0,0,0));
97 | addCubeFace(new THREE.CylinderGeometry(0.15, 0.15, 0.5), 'yellow', 2, new THREE.Vector3(0,0.5,0), new THREE.Vector3(- Math.PI / 2,0,0));
98 | addCubeFace(new THREE.OctahedronGeometry(0.25), 'green', 3, new THREE.Vector3(0,-0.5,0), new THREE.Vector3( Math.PI / 2,0,0));
99 | addCubeFace(new THREE.TorusGeometry(0.25, 0.1), 'blue', 4, new THREE.Vector3(0,0,-0.5), new THREE.Vector3( Math.PI,0,0));
100 | addCubeFace(new THREE.ConeGeometry(0.25, 0.5), 'orange', 5, new THREE.Vector3(-0.5,0,0), new THREE.Vector3( 0, -Math.PI / 2,0));
101 | addCubeFace(new THREE.BoxGeometry(0.5, 0.5, 0.5), 'brown', 6, new THREE.Vector3(0.5,0,0), new THREE.Vector3( 0, Math.PI / 2,0));
102 |
103 | const boxBorderMat = new THREE.MeshPhongMaterial({ color: 0x1A120B });
104 | boxBorderMat.stencilWrite = true;
105 | boxBorderMat.stencilRef = 0;
106 | boxBorderMat.stencilFunc = THREE.EqualStencilFunc;
107 | const boxBorderGeom = new THREE.BoxGeometry();
108 | scene.add(new THREE.Mesh(boxBorderGeom, boxBorderMat));
109 | }
110 |
111 | function initPlanes () {
112 | const planeGeom = new THREE.PlaneGeometry();
113 |
114 | const stencilMat = new THREE.MeshPhongMaterial({ color: 'green' });
115 | stencilMat.colorWrite = false;
116 | stencilMat.depthWrite = false;
117 | stencilMat.stencilWrite = true;
118 | stencilMat.stencilRef = 1;
119 | stencilMat.stencilFunc = THREE.AlwaysStencilFunc;
120 | // stencilMat.stencilZFail = THREE.ReplaceStencilOp;
121 | // stencilMat.stencilFail = THREE.ReplaceStencilOp;
122 | stencilMat.stencilZPass = THREE.ReplaceStencilOp;
123 | const stencilMesh = new THREE.Mesh(planeGeom, stencilMat);
124 | const stencilHelper = new THREE.BoxHelper(stencilMesh, 'white');
125 | scene.add(stencilHelper);
126 | scene.add(stencilMesh);
127 |
128 | const blueMat = new THREE.MeshPhongMaterial({ color: 'blue' });
129 | blueMat.stencilWrite = true;
130 | blueMat.stencilRef = 1;
131 | blueMat.stencilFunc = THREE.NotEqualStencilFunc;
132 | const blueMesh = new THREE.Mesh(planeGeom, blueMat);
133 | blueMesh.position.x = -0.5;
134 | blueMesh.position.y = 0.5;
135 | scene.add(blueMesh);
136 |
137 | const redMat = new THREE.MeshPhongMaterial({ color: 'red' });
138 | redMat.stencilWrite = true;
139 | redMat.stencilRef = 1;
140 | redMat.stencilFunc = THREE.EqualStencilFunc;
141 | const redMesh = new THREE.Mesh(planeGeom, redMat);
142 | redMesh.position.x = 0.5;
143 | redMesh.position.y = -0.5;
144 | scene.add(redMesh);
145 |
146 | const stencilParams = gui.addFolder('stencilParams');
147 | stencilParams.add(params.stencilMesh, 'z').min(- 1).max(1).onChange(d => {
148 | stencilMesh.position.z = d
149 | stencilHelper.update();
150 | });
151 | stencilParams.open();
152 | }
153 |
154 | function onWindowResize() {
155 |
156 | camera.aspect = window.innerWidth / window.innerHeight;
157 | camera.updateProjectionMatrix();
158 |
159 | renderer.setSize(window.innerWidth, window.innerHeight);
160 |
161 | }
162 |
163 | function animate() {
164 |
165 | const delta = clock.getDelta();
166 |
167 | requestAnimationFrame(animate);
168 |
169 | renderer.render(scene, camera);
170 |
171 | }
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "outDir": "./dist/",
4 | "sourceMap": true,
5 | "noImplicitAny": false,
6 | "module": "CommonJS",
7 | "target": "es5",
8 | "allowJs": true
9 | }
10 | }
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 |
3 | const src = path.resolve(__dirname, 'src');
4 | const dist = path.resolve(__dirname, 'dist');
5 |
6 | module.exports = {
7 | mode: 'development',
8 | entry: './src/index.ts',
9 | devtool: 'inline-source-map',
10 | module: {
11 | rules: [
12 | {
13 | test: /\.tsx?$/,
14 | use: 'ts-loader',
15 | exclude: /node_modules/
16 | }
17 | ]
18 | },
19 | resolve: {
20 | extensions: ['.tsx', '.ts', '.js']
21 | },
22 | output: {
23 | filename: 'index.js',
24 | path: dist
25 | },
26 | devServer: {
27 | static: dist,
28 | },
29 | }
--------------------------------------------------------------------------------