1import { ExpoWebGLRenderingContext, GLView } from 'expo-gl';
2import * as React from 'react';
3import { StyleSheet, View } from 'react-native';
4import {
5  AmbientLight,
6  BasicShadowMap,
7  BoxGeometry,
8  CameraHelper,
9  DirectionalLight,
10  DirectionalLightHelper,
11  DoubleSide,
12  Mesh,
13  MeshPhongMaterial,
14  MeshStandardMaterial,
15  PerspectiveCamera,
16  PlaneBufferGeometry,
17  Scene,
18  WebGLRenderer,
19} from 'three';
20
21export default function GLThreeDepthStencilBuffer() {
22  const animationFrameId = React.useRef(-1);
23
24  React.useEffect(() => {
25    return () => {
26      if (animationFrameId.current >= 0) {
27        cancelAnimationFrame(animationFrameId.current);
28      }
29    };
30  }, []);
31
32  const onContextCreate = React.useCallback(async (gl: ExpoWebGLRenderingContext) => {
33    const renderer = new WebGLRenderer({
34      context: gl,
35    });
36    renderer.shadowMap.enabled = true;
37    renderer.shadowMap.type = BasicShadowMap;
38
39    renderer.setSize(gl.drawingBufferWidth, gl.drawingBufferHeight);
40    renderer.setClearColor(0xffffff, 1.0);
41    renderer.shadowMap.enabled = true;
42
43    // Standard Camera
44    const camera = new PerspectiveCamera(
45      70,
46      gl.drawingBufferWidth / gl.drawingBufferHeight,
47      0.1,
48      1000
49    );
50    camera.position.set(10, 10, 0);
51    camera.lookAt(0, 0, 0);
52
53    const scene = new Scene();
54
55    scene.add(new AmbientLight(0xffffff, 0.5));
56
57    // Three's lights use depth and stencil buffers.
58    const light = new DirectionalLight(0xffffff, 0.5);
59    light.position.set(0, 6, 0);
60    light.castShadow = true;
61    light.shadow.camera.left = -1;
62    light.shadow.camera.right = 1;
63    light.shadow.camera.top = -1;
64    light.shadow.camera.bottom = 1;
65    scene.add(light);
66
67    const shadowHelper = new DirectionalLightHelper(light, 2, 0x0000ff);
68    scene.add(shadowHelper);
69
70    // Create a plane that receives shadows (but does not cast them).
71    const planeGeometry = new PlaneBufferGeometry(10, 10, 32, 32);
72    const planeMaterial = new MeshStandardMaterial({
73      color: 0x00ff00,
74      side: DoubleSide,
75    });
76
77    const plane = new Mesh(planeGeometry, planeMaterial);
78    plane.receiveShadow = true;
79    plane.rotation.x = Math.PI / 2;
80    plane.position.y = -2;
81    scene.add(plane);
82
83    const cube = new Mesh(
84      new BoxGeometry(1.2, 1.2, 1.2),
85      new MeshPhongMaterial({
86        color: 0xffff00,
87      })
88    );
89    cube.castShadow = true;
90    cube.receiveShadow = true;
91    cube.renderOrder = 3;
92    scene.add(cube);
93
94    const another = new Mesh(
95      new BoxGeometry(1.4, 1.4, 1.4),
96      new MeshPhongMaterial({
97        color: 0xff0000,
98      })
99    );
100    another.position.set(0, 2, 0);
101    another.castShadow = true;
102    another.receiveShadow = true;
103    another.renderOrder = 1;
104    scene.add(another);
105
106    const helper = new CameraHelper(light.shadow.camera);
107    scene.add(helper);
108
109    const animate = () => {
110      animationFrameId.current = requestAnimationFrame(animate);
111
112      cube.rotation.x += 0.01;
113      cube.rotation.y += 0.01;
114      renderer.render(scene, camera);
115      gl.endFrameEXP();
116    };
117
118    animate();
119    renderer.render(scene, camera);
120    gl.endFrameEXP();
121  }, []);
122
123  return (
124    <View style={styles.flex}>
125      <GLView style={styles.flex} onContextCreate={onContextCreate} />
126    </View>
127  );
128}
129
130GLThreeDepthStencilBuffer.title = 'three.js depth and stencil buffer';
131
132const styles = StyleSheet.create({
133  flex: {
134    flex: 1,
135  },
136});
137