Expo GL视图
一个提供 GLView 的库,GLView 可作为 OpenGL ES 渲染目标并提供 GLContext。适用于渲染 2D 和 3D 图形。
expo-gl 提供了一个 View,作为 OpenGL ES 渲染目标,可用于渲染 2D 和 3D 图形。挂载时,将创建一个 OpenGL ES 上下文。它的绘图缓冲区在每一帧都会作为 View 的内容呈现。
安装
🌐 Installation
- npx expo install expo-glIf you are installing this in an existing React Native app, make sure to install expo in your project.
用法
🌐 Usage
import { View } from 'react-native'; import { GLView } from 'expo-gl'; export default function App() { return ( <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}> <GLView style={{ width: 300, height: 300 }} onContextCreate={onContextCreate} /> </View> ); } function onContextCreate(gl) { gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight); gl.clearColor(0, 1, 1, 1); // Create vertex shader (shape & position) const vert = gl.createShader(gl.VERTEX_SHADER); gl.shaderSource( vert, ` void main(void) { gl_Position = vec4(0.0, 0.0, 0.0, 1.0); gl_PointSize = 150.0; } ` ); gl.compileShader(vert); // Create fragment shader (color) const frag = gl.createShader(gl.FRAGMENT_SHADER); gl.shaderSource( frag, ` void main(void) { gl_FragColor = vec4(0.0, 0.0, 0.0, 1.0); } ` ); gl.compileShader(frag); // Link together into a program const program = gl.createProgram(); gl.attachShader(program, vert); gl.attachShader(program, frag); gl.linkProgram(program); gl.useProgram(program); gl.clear(gl.COLOR_BUFFER_BIT); gl.drawArrays(gl.POINTS, 0, 1); gl.flush(); gl.endFrameEXP(); }
高级 API
🌐 High-level APIs
由于 WebGL API 属于较底层的接口,通过底层的 GLView 使用更高级的图形 API 进行渲染可能会更有帮助。以下库集成了流行的图形 API:
🌐 Since the WebGL API is quite low-level, it can be helpful to use higher-level graphics APIs rendering through a GLView underneath. The following libraries integrate popular graphics APIs:
任何支持 WebGL 的库,只要它期望一个 WebGLRenderingContext 都可以使用。有时这些库会假设在 web JavaScript 环境中运行(例如假设 document)。通常,这主要是用于资源加载或事件处理,而主要的渲染逻辑仍然只使用纯 WebGL。因此,这些库通常仍然可以使用,只需要一些变通方法。上面提到的 Expo 特定集成包含了一些流行库的变通方法。
🌐 Any WebGL-supporting library that expects a WebGLRenderingContext could be used. Some times such libraries assume a web JavaScript context (such as assuming document). Usually this is for resource loading or event handling, with the main rendering logic still only using pure WebGL. So these libraries can usually still be used with a couple workarounds. The Expo-specific integrations above include workarounds for some popular libraries.
与 Reanimated 工作集集成
🌐 Integration with Reanimated worklets
要在 Reanimated 工作集中使用此 API,你需要将 GL 上下文 ID 传递给工作集并重新创建 GL 对象,如下例所示。
🌐 To use this API inside Reanimated worklet, you need to pass the GL context ID to the worklet and recreate the GL object like in the example below.
import { View } from 'react-native'; import { runOnUI } from 'react-native-reanimated'; import { GLView } from 'expo-gl'; function render(gl) { 'worklet'; // add your WebGL code here } function onContextCreate(gl) { runOnUI((contextId: number) => { 'worklet'; const gl = GLView.getWorkletContext(contextId); render(gl); })(gl.contextId); } export default function App() { return ( <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}> <GLView style={{ width: 300, height: 300 }} enableExperimentalWorkletSupport onContextCreate={onContextCreate} /> </View> ); }
想要了解如何将 expo-gl 与 Reanimated 和 Gesture Handler 一起使用的更深入示例,你可以查看 这个示例。
🌐 For more in-depth example on how to use expo-gl with Reanimated and Gesture Handler you can check this example.
局限性
🌐 Limitations
Worklet 运行时对其内部运行的代码施加了一些限制,因此,如果你有现有的 WebGL 代码,则可能需要进行一些修改才能在 Worklet 线程内运行。
🌐 Worklet runtime is imposing some limitations on the code that runs inside it, so if you have existing WebGL code, it'll likely require some modifications to run inside a worklet thread.
- 像 Pixi.js 或 Three.js 这样的第三方库在 worklet 中无法使用,你只能使用在开头加了
'worklet'的函数。 - 如果你需要加载一些资源以传递给 WebGL 代码,这需要在主线程上完成,并通过某种引用传递给 worklet。如果你正在使用
expo-assets,你可以直接将Asset.fromModule返回的资源对象或从钩子useAssets获取的对象传递给runOnUI函数。 - 要实现渲染循环,你需要使用
requestAnimationFrame,像setTimeout这样的 API 不被支持。
查看 Reanimated 文档 以了解更多信息。
🌐 Check Reanimated documentation to learn more.
远程调试和 GLView
🌐 Remote debugging and GLView
启用远程调试时,此 API 无法按预期工作。React Native 调试器在你的电脑上运行 JavaScript,而不是在移动设备上。GLView 需要 Chrome 不支持的同步本地调用。
🌐 This API does not function as intended with remote debugging enabled. The React Native debugger runs JavaScript on your computer, not the mobile device. GLView requires synchronous native calls that are not supported in Chrome.
应用接口
🌐 API
import { GLView } from 'expo-gl';
Component
Type: React.Component<GLViewProps>
A View that acts as an OpenGL ES render target. On mounting, an OpenGL ES context is created. Its drawing buffer is presented as the contents of the View every frame.
boolean • Default: falseEnables support for interacting with a gl object from code running on the Reanimated worklet thread.
number • Default: 4GLView can enable iOS's built-in multisampling.
This prop specifies the number of samples to use. Setting this to 0 turns off multisampling.
(gl: ExpoWebGLRenderingContext) => voidA function that will be called when the OpenGL ES context is created.
The function is passed a single argument gl that extends a WebGLRenderingContext interface.
Static Methods
Imperative API that creates headless context which is devoid of underlying view. It's useful for headless rendering or in case you want to keep just one context per application and share it between multiple components. It is slightly faster than usual context as it doesn't swap framebuffers and doesn't present them on the canvas, however it may require you to take a snapshot in order to present its results. Also, keep in mind that you need to set up a viewport and create your own framebuffer and texture that you will be drawing to, before you take a snapshot.
Promise<ExpoWebGLRenderingContext>A promise that resolves to WebGL context object. See WebGL API for more details.
| Parameter | Type | Description |
|---|---|---|
| exgl(optional) | number | ExpoWebGLRenderingContext | WebGL context to destroy. |
Destroys given context.
Promise<boolean>A promise that resolves to boolean value that is true if given context existed and has been destroyed successfully.
| Parameter | Type | Description |
|---|---|---|
| exgl(optional) | number | ExpoWebGLRenderingContext | WebGL context to take a snapshot from. |
| options(optional) | SnapshotOptions | Default: {} |
Takes a snapshot of the framebuffer and saves it as a file to app's cache directory.
Promise<GLSnapshot>A promise that resolves to GLSnapshot object.
Component Methods
Promise<any>| Parameter | Type |
|---|---|
| options(optional) | SnapshotOptions |
Same as static takeSnapshotAsync(),
but uses WebGL context that is associated with the view on which the method is called.
Promise<GLSnapshot>Methods
Interfaces
Extends: WebGL2RenderingContext
| Property | Type | Description |
|---|---|---|
| contextId | number | - |
Types
Literal Type: union
Acceptable values are: null | number | Component<any, any> | ComponentClass<any>
| Property | Type | Description |
|---|---|---|
| height | number | Height of the snapshot. |
| localUri | string | Synonym for |
| uri | string | Blob | null | URI to the snapshot. |
| width | number | Width of the snapshot. |
| Property | Type | Description |
|---|---|---|
| compress(optional) | number | A value in range Default: 1.0 |
| flip(optional) | boolean | Whether to flip the snapshot vertically. Default: false |
| format(optional) | 'jpeg' | 'png' | 'webp' | Specifies what type of compression should be used and what is the result file extension. PNG compression is lossless but slower, JPEG is faster but the image has visible artifacts.
Default: 'jpeg' |
| framebuffer(optional) | WebGLFramebuffer | Specify the framebuffer that we will be reading from. Defaults to underlying framebuffer that is presented in the view or the current framebuffer if context is headless. |
| rect(optional) | {
height: number,
width: number,
x: number,
y: number
} | Rect to crop the snapshot. It's passed directly to |
Enums
GLLoggingOption.GET_ERRORS = 2Calls gl.getError() after each other method call and prints an error if any is returned.
This option has a significant impact on the performance as this method is blocking.
GLLoggingOption.RESOLVE_CONSTANTS = 4Resolves parameters of type number to their constant names.
GLLoggingOption.TRUNCATE_STRINGS = 8When this option is enabled, long strings will be truncated. It's useful if your shaders are really big and logging them significantly reduces performance.
WebGL API
一旦组件挂载并且 OpenGL ES 上下文已创建,通过 onContextCreate 属性接收到的 gl 对象就变成了 OpenGL ES 上下文的接口,提供了 WebGL API。它类似于 WebGL 2 规范中的 WebGL2RenderingContext。
🌐 Once the component is mounted and the OpenGL ES context has been created, the gl object received through the onContextCreate prop becomes the interface to the OpenGL ES context, providing a WebGL API. It resembles a WebGL2RenderingContext in the WebGL 2 spec.
一些较旧的安卓设备可能不支持 WebGL2 功能。要检查设备是否支持 WebGL2,建议使用 gl instanceof WebGL2RenderingContext。
🌐 Some older Android devices may not support WebGL2 features. To check whether the device supports WebGL2 it's recommended to use gl instanceof WebGL2RenderingContext.
存在一个额外的方法 gl.endFrameEXP(),用于通知上下文当前帧已准备好呈现。这类似于其他 OpenGL 平台中的“交换缓冲区”API 调用。
🌐 An additional method gl.endFrameEXP() is present, which notifies the context that the current frame is ready to present. This is similar to a 'swap buffers' API call in other OpenGL platforms.
以下 WebGL2RenderingContext 方法当前未实现:
🌐 The following WebGL2RenderingContext methods are currently unimplemented:
getFramebufferAttachmentParameter()getRenderbufferParameter()compressedTexImage2D()compressedTexSubImage2D()getTexParameter()getUniform()getVertexAttrib()getVertexAttribOffset()getBufferSubData()getInternalformatParameter()renderbufferStorageMultisample()compressedTexImage3D()compressedTexSubImage3D()fenceSync()isSync()deleteSync()clientWaitSync()waitSync()getSyncParameter()getActiveUniformBlockParameter()
[texImage2D()](https://web.nodejs.cn/en-US/docs/Web/API/WebGLRenderingContext/texImage2D) 的 pixels 参数必须是 null、带有像素数据的 ArrayBuffer,或者是形如 { localUri } 的对象,其中 localUri 是设备文件系统中图片的 file:// URI。因此,当对 Asset 对象调用 .downloadAsync() 并完成以获取资源后,会使用该对象。
🌐 The pixels argument of texImage2D() must be null, an ArrayBuffer with pixel data, or an object of the form { localUri } where localUri is the file:// URI of an image in the device's file system. Thus, an Asset object is used once .downloadAsync() has been called on it (and completed) to fetch the resource.
出于效率考虑,目前这些方法的实现不会对其参数进行类型或范围检查。因此,传入无效参数可能会导致本地崩溃。计划在即将发布的 SDK 版本中更新 API,以执行参数检查。
🌐 For efficiency reasons, the current implementations of the methods don't perform type or bounds checking on their arguments. So, passing invalid arguments may cause a native crash. There are plans to update the API to perform argument checking in upcoming SDK versions.
目前,错误检查的优先级较低,因为引擎通常不依赖 OpenGL API 来执行参数检查;否则,由底层 OpenGL ES 实现执行的检查通常已经足够。
🌐 Currently, the priority for error checking is low since engines generally don't rely on the OpenGL API to perform argument checking; otherwise, checks performed by the underlying OpenGL ES implementation are often sufficient.