Skip to content

Commit

Permalink
[gl-cpp] Fix depth/stencil buffers not working correctly with three.js (
Browse files Browse the repository at this point in the history
#7543)

# Why

Fixes #7502 

# How

It turned out that Three.js uses `gl.DEPTH_STENCIL` constant when calling `renderbufferStorage` method used as part of lights rendering. This constant is valid in WebGL, but is invalid in OpenGL ES 3.0, so we need to fall back it to `GL_DEPTH24_STENCIL8` which is valid in OpenGL and has the same effect.

# Test Plan

Added new example to NCL which is almost the same as the one provided in the issue. Also received a feedback that JS workaround doing the same thing works as expected.
  • Loading branch information
tsapeta committed Mar 31, 2020
1 parent 8cdd886 commit 3cd0a4d
Show file tree
Hide file tree
Showing 10 changed files with 143 additions and 10 deletions.
4 changes: 2 additions & 2 deletions apps/native-component-list/package.json
Expand Up @@ -101,7 +101,7 @@
"react-navigation-stack": "1.6.0-alpha.1",
"react-navigation-tabs": "^2.4.1",
"regl": "^1.3.0",
"three": "^0.109.0",
"three": "^0.115.0",
"url": "^0.11.0",
"uuid": "^3.1.0",
"victory-native": "^30.4.0"
Expand All @@ -120,4 +120,4 @@
"expo-module-scripts": "~1.2.0",
"expo-yarn-workspaces": "^1.2.1"
}
}
}
13 changes: 9 additions & 4 deletions apps/native-component-list/src/screens/GL/GLScreens.tsx
Expand Up @@ -15,6 +15,7 @@ import GLMaskScreen from './GLMaskScreen';
import GLSnapshotsScreen from './GLSnapshotsScreen';
import GLHeadlessRenderingScreen from './GLHeadlessRenderingScreen';
import ProcessingWrap from './ProcessingWrap';
import GLThreeDepthStencilBuffer from './GLThreeDepthStencilBuffer';

function optionalRequire(requirer: () => { default: React.ComponentType }) {
try {
Expand Down Expand Up @@ -175,6 +176,10 @@ const GLScreens: Screens = {
}),
},

THREEDepthStencilBuffer: {
screen: GLThreeDepthStencilBuffer,
},

THREEGlitchFilm: {
screen: GLWrap('three.js glitch and film effects', async gl => {
const scene = new THREE.Scene();
Expand All @@ -189,10 +194,10 @@ const GLScreens: Screens = {
renderer.setSize(gl.drawingBufferWidth, gl.drawingBufferHeight);
renderer.setClearColor(0xffffff);

const composer = new EffectComposer( renderer );
composer.addPass( new RenderPass( scene, camera ) );
const composer = new EffectComposer(renderer);
composer.addPass(new RenderPass(scene, camera));
const glitchPass = new GlitchPass();
composer.addPass( glitchPass );
composer.addPass(glitchPass);

const geometry = new THREE.BoxGeometry(1, 1, 1);
const material = new THREE.MeshBasicMaterial({
Expand Down Expand Up @@ -410,7 +415,7 @@ const GLScreens: Screens = {
...(GLCameraScreen && {
GLCamera: {
screen: GLCameraScreen,
}
},
}),

// WebGL 2.0 sample - http://webglsamples.org/WebGL2Samples/#transform_feedback_separated_2
Expand Down
@@ -0,0 +1,121 @@
import React from 'react';
import * as THREE from 'three';
import { StyleSheet, View } from 'react-native';
import { GLView, ExpoWebGLRenderingContext } from 'expo-gl';

export default class GLThreeDepthStencilBuffer extends React.PureComponent {
static title = 'three.js depth and stencil buffer';

animationFrameId: number = -1;

componentWillUnmount() {
if (this.animationFrameId >= 0) {
cancelAnimationFrame(this.animationFrameId);
}
}

onContextCreate = async (gl: ExpoWebGLRenderingContext) => {
const renderer = new THREE.WebGLRenderer({
context: gl,
});
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.BasicShadowMap;

renderer.setSize(gl.drawingBufferWidth, gl.drawingBufferHeight);
renderer.setClearColor(0xffffff, 1.0);
renderer.shadowMap.enabled = true;

// Standard Camera
const camera = new THREE.PerspectiveCamera(
70,
gl.drawingBufferWidth / gl.drawingBufferHeight,
0.1,
1000
);
camera.position.set(10, 10, 0);
camera.lookAt(0, 0, 0);

const scene = new THREE.Scene();

scene.add(new THREE.AmbientLight(0xffffff, 0.5));

// Three's lights use depth and stencil buffers.
const light = new THREE.DirectionalLight(0xffffff, 0.5);
light.position.set(0, 6, 0);
light.castShadow = true;
light.shadow.camera.left = -1;
light.shadow.camera.right = 1;
light.shadow.camera.top = -1;
light.shadow.camera.bottom = 1;
scene.add(light);

const shadowHelper = new THREE.DirectionalLightHelper(light, 2, 0x0000ff);
scene.add(shadowHelper);

// Create a plane that receives shadows (but does not cast them).
const planeGeometry = new THREE.PlaneBufferGeometry(10, 10, 32, 32);
const planeMaterial = new THREE.MeshStandardMaterial({
color: 0x00ff00,
side: THREE.DoubleSide,
});

const plane = new THREE.Mesh(planeGeometry, planeMaterial);
plane.receiveShadow = true;
plane.rotation.x = Math.PI / 2;
plane.position.y = -2;
scene.add(plane);

const cube = new THREE.Mesh(
new THREE.BoxGeometry(1.2, 1.2, 1.2),
new THREE.MeshPhongMaterial({
color: 0xffff00,
})
);
cube.castShadow = true;
cube.receiveShadow = true;
cube.renderOrder = 3;
scene.add(cube);

const another = new THREE.Mesh(
new THREE.BoxGeometry(1.4, 1.4, 1.4),
new THREE.MeshPhongMaterial({
color: 0xff0000,
})
);
another.position.set(0, 2, 0);
another.castShadow = true;
another.receiveShadow = true;
another.renderOrder = 1;
scene.add(another);

const helper = new THREE.CameraHelper(light.shadow.camera);
scene.add(helper);

const animate = () => {
this.animationFrameId = requestAnimationFrame(animate);

cube.rotation.x += 0.01;
cube.rotation.y += 0.01;
renderer.render(scene, camera);
gl.endFrameEXP();
};

animate();
renderer.render(scene, camera);
gl.endFrameEXP();
};

render() {
return (
<View style={styles.flex}>
<GLView style={styles.flex} onContextCreate={this.onContextCreate} />
</View>
);
}
}

const styles = StyleSheet.create({
flex: {
flex: 1,
},
});
Binary file modified packages/expo-gl-cpp/android/dist/arm64-v8a/libexpo-gl.so
Binary file not shown.
Binary file modified packages/expo-gl-cpp/android/dist/armeabi-v7a/libexpo-gl.so
Binary file not shown.
Binary file modified packages/expo-gl-cpp/android/dist/x86/libexpo-gl.so
Binary file not shown.
Binary file modified packages/expo-gl-cpp/android/dist/x86_64/libexpo-gl.so
Binary file not shown.
5 changes: 5 additions & 0 deletions packages/expo-gl-cpp/cpp/EXGLNativeMethods.cpp
Expand Up @@ -534,6 +534,11 @@ _WRAP_METHOD_IS_OBJECT(Renderbuffer)

_WRAP_METHOD(renderbufferStorage, 4) {
EXJS_UNPACK_ARGV(GLenum target, GLint internalformat, GLsizei width, GLsizei height);

// WebGL allows `GL_DEPTH_STENCIL` flag to be passed here,
// however OpenGL ES seems to require sized format, so we fall back to `GL_DEPTH24_STENCIL8`.
internalformat = internalformat == GL_DEPTH_STENCIL ? GL_DEPTH24_STENCIL8 : internalformat;

addToNextBatch([=] {
glRenderbufferStorage(target, internalformat, width, height);
});
Expand Down
2 changes: 2 additions & 0 deletions packages/expo-gl/CHANGELOG.md
Expand Up @@ -7,3 +7,5 @@
### 🎉 New features

### 🐛 Bug fixes

- Fix depth/stencil buffers not working correctly with `three.js`. ([#7543](https://github.com/expo/expo/pull/7543) by [@tsapeta](https://github.com/tsapeta))
8 changes: 4 additions & 4 deletions yarn.lock
Expand Up @@ -18108,10 +18108,10 @@ thenify-all@^1.0.0:
dependencies:
any-promise "^1.0.0"

three@^0.109.0:
version "0.109.0"
resolved "https://registry.yarnpkg.com/three/-/three-0.109.0.tgz#611c9ef860d10bd107695cead6c6abcd2422e5fd"
integrity sha512-XT99T3Hvgh2CEvwPdHYEunNE+clLK6KiT1U8En7YOgIqTUw4MrLeIc8zxQAJ6wbP8hhJaY5+Cff3jwBPpBa0gA==
three@^0.115.0:
version "0.115.0"
resolved "https://registry.yarnpkg.com/three/-/three-0.115.0.tgz#540d800c381b9da2334c024f0fbe4d23f84eb05e"
integrity sha512-mAV2Ky3RdcbdSbR9capI+tKLvRldWYxd4151PZTT/o7+U2jh9Is3a4KmnYwzyUAhB2ZA3pXSgCd2DOY4Tj5kow==

throat@^4.0.0, throat@^4.1.0:
version "4.1.0"
Expand Down

0 comments on commit 3cd0a4d

Please sign in to comment.