Expo GLView
一个提供 GLView 的库,GLView 可作为 OpenGL ES 渲染目标,并提供 GLContext。适用于渲染 2D 和 3D 图形。
For the complete documentation index, see llms.txt. Use this Use this file to discover all available pages.
expo-gl 提供了一个 View,它充当 OpenGL ES 渲染目标,适用于渲染 2D 和 3D 图形。在挂载时,会创建一个 OpenGL ES 上下文。其绘图缓冲区会在每一帧作为 View 的内容呈现出来。
安装
- npx expo install expo-glIf you are installing this in an existing React Native app, make sure to install expo in your project.
使用
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); // 创建顶点着色器(形状与位置) 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); // 创建片段着色器(颜色) 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); // 链接为一个程序 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
由于 WebGL API 相当底层,使用更高级的图形 API 并在底层通过 GLView 渲染会很有帮助。以下库集成了流行的图形 API:
任何支持 WebGL、并期望使用 WebGLRenderingContext 的库都可以使用。有时这类库会假设存在一个 Web 端 JavaScript 上下文(例如假设有 document)。通常这只是用于资源加载或事件处理,而主要的渲染逻辑仍然只使用纯 WebGL。因此,这些库通常仍可通过一些变通方法使用。上面的 Expo 专用集成已经为一些流行库包含了这些变通方法。
与 Reanimated worklet 集成
要在 Reanimated worklet 中使用此 API,你需要将 GL 上下文 ID 传递给 worklet,并像下面示例那样重新创建 GL 对象。
import { View } from 'react-native'; import { runOnUI } from 'react-native-reanimated'; import { GLView } from 'expo-gl'; function render(gl) { 'worklet'; // 在这里添加你的 WebGL 代码 } 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 一起使用的更深入示例,可以查看这个示例。
限制
Worklet 运行时对其中运行的代码施加了一些限制,因此如果你已有 WebGL 代码,通常需要做一些修改才能在 worklet 线程中运行。
- 像 Pixi.js 或 Three.js 这样的第三方库无法在 worklet 中工作,你只能使用开头加了
'worklet'的函数。 - 如果你需要加载一些要传给 WebGL 代码的资源,必须在主线程中完成,并通过某种引用传递给 worklet。如果你使用
expo-assets,则可以直接将Asset.fromModule或 hookuseAssets返回的资源对象传递给runOnUI函数。 - 要实现渲染循环,你需要使用
requestAnimationFrame,setTimeout之类的 API 不受支持。 - 它仅支持 Android 和 iOS,不支持 Web。
更多内容请查看 Reanimated 文档。
远程调试与 GLView
在启用远程调试时,此 API 无法按预期工作。React Native 调试器是在你的电脑上运行 JavaScript,而不是在移动设备上。GLView 需要同步的原生调用,而这些在 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。
某些较旧的 Android 设备可能不支持 WebGL2 功能。要检查设备是否支持 WebGL2,建议使用 gl instanceof WebGL2RenderingContext。
另外还提供了 gl.endFrameEXP() 方法,它会通知上下文当前帧已准备好呈现。这类似于其他 OpenGL 平台中的 “swap buffers” API 调用。
以下 WebGL2RenderingContext 方法目前尚未实现:
getFramebufferAttachmentParameter()getRenderbufferParameter()compressedTexImage2D()compressedTexSubImage2D()getTexParameter()getUniform()getVertexAttrib()getVertexAttribOffset()getBufferSubData()getInternalformatParameter()renderbufferStorageMultisample()compressedTexImage3D()compressedTexSubImage3D()fenceSync()isSync()deleteSync()clientWaitSync()waitSync()getSyncParameter()getActiveUniformBlockParameter()
texImage2D() 的 pixels 参数必须是 null、包含像素数据的 ArrayBuffer,或者是形如 { localUri } 的对象,其中 localUri 是设备文件系统中某个图片的 file:// URI。因此,在对 Asset 对象调用 .downloadAsync()(并完成)以获取资源后,会使用该对象。
出于效率考虑,当前这些方法的实现不会对其参数进行类型或边界检查。因此,传入无效参数可能会导致原生崩溃。我们计划在未来的 SDK 版本中更新该 API 以执行参数检查。
目前,错误检查的优先级较低,因为引擎通常并不依赖 OpenGL API 来进行参数检查;否则,底层 OpenGL ES 实现所执行的检查通常已经足够。