-
Notifications
You must be signed in to change notification settings - Fork 11
Description
This proposal is about adding built-in GLTF support in the browser.
At the W3C workshop on web games, Yasushi Ando conducted a breakout session discussing native support for GLTF in the browser. His position statement can be read here https://www.w3.org/2018/12/games-workshop/papers/new-html-3D-element.txt, proposes a scene element that can render a gltf/glb model.
<scene controls vrenabled width="300">
<source src="http://example.com/Monster_small.glb" type="model/gltf-binary" media="(min-width: 320px)">
<source src="http://example.com/Monster.gltf" type="model/gltf+json" media="(min-width: 640px)">
Message for unsupported browsers
</scene>
The GLTF would be exposed to JavaScript allowing manipulation of the scene graph, transform or triggering animations. I took a follow up from the workshop to post in this community group to discuss value proposition and shape of the API.
Adding native support for GLTF is appealing and can add value in the following ways
Performance
A single GLTF model file can describe an entire scene apart from standalone 3d objects. Traversing, rendering such a scene graph in native code allows fulling utilizing the hardware capabilities (threads, graphics API) to have performant rendering. Performance improvements allows for higher fps or richer scenes.
Model Protection
Protecting 3d assets was a developer concern during the workshop, native support for GLTF can allow for solutions like EME to protect assets.
Independent Rendering
Describing the 3d scene to the browser instead of the current immediate mode WebGL style API, allows the browser to render the scene independent of the JavaScript frame rate. In a XR use case, a scene can be re-rendered to the new headset orientation independent of script performance.
Foreign Object
Similar in concept to foreign objects in SVG, in the future we can allow embedding HTML content within 3D scenes as a texture source for 3d objects in the scene.
Hit Testing
The browser can offer hit testing of the 3D scene in a performant manner. Combined with Foreign Object because the browser understands the scene, it can offer hit testing down to the HTML elements. Caveat - supporting hit test into HTML content will require further spec’ing input to prevent feature misuse for click-baiting.
Dev Tools Integration
Like HTML, browser dev tools (F12) integration can provide value to developers. Ability to debug a scene, see properties on nodes, view performance metrics are examples of data surface through dev tools.
3D IFrames
Far out in the future, when 3d content is ubiquitous - we could want a section of 3d space in AR/VR to be owned by a domain1.com and another section to be domain2.com. This is a similar model to Iframes in 2d, having the browser understand the scene emitted by each domain is a steppingstone to building such a metaverse.
Prior Art
Apple supports viewing usdz models through anchor tags
https://webkit.org/blog/8421/viewing-augmented-reality-assets-in-safari-for-ios/
<a rel="ar" href="model.usdz">
<img src="model-preview.jpg">
</a>
Announced during Google IO 2019, Google’s approach is through model viewer js library which adds a custom element the <model-viewer>
.
https://developers.google.com/web/updates/2019/02/model-viewer#basic_3d_models
Later the src attribute can be set to a gltf file and the javascript renders the model for you.
<model-viewer src="assets/Astronaut.gltf" alt="A 3D model of an astronaut">
API Shape
This proposal adds WebGL interop to enable a broader use case where WebGL can add post render effects, particles or text sourced from canvas elements. There are at least 3 concepts here when it comes to native 3d model support.
GltfModel
This is the component / class responsible for loading a GLTF file and loading up resources to be consumed in a format agnostic way.
Setting GltfModel.Src = http://modelfile.gltf or glb would download , parse turn buffer views into a texture that WebGL understands and then will construct the Scene/SceneNodes. We would have a loaded event at the end of the whole process.
GltfModel.Scene then points to the loaded GLTF scene.
Scene/SceneNodes
This is the way we represent the 3d content from the GLTF file in a retained fashion – it is the object model for GLTF/3d. JavaScript can act on these objects and change transform and properties on them.
Splitting the loader from the Scene will insulate us from any model format changes in the future.
Viewers
Now that we have a representation of the scene as Scene/SceneNodes there can be viewers for this scene. Three of these are possible
-
WebGL
Scene.draw(WebGLRenderingContext context). This would take the vertex buffer and transforms represented in the scene graph and draw that with the WebGL context. Almost the same steps that a WebGL renderer would go through but in native code behind a single JavaScript call. -
WebGPU
Like WebGL in the future we should add some way to render a Scene with SceneNodes to webgpu workflow. -
SceneElement
<scene></scene>
This is the final one that is an actual html element. This is the home for adding capabilities beyond what WebGL can offer. Here is where independent rendering will show up, here is where with a single attribute, the developer can ask the browser to render the same scene to an XR device.
Example use cases
Focusing on the WebGL view, here is how a developer would use it to render a spinning model. Modifying the spinning cube sample from https://www.tutorialspoint.com/webgl/webgl_cube_rotation.htm
<!doctype html>
<html>
<body>
<canvas width = "570" height = "570" id = "my_Canvas"></canvas>
<script>
var canvas = document.getElementById('my_Canvas');
gl = canvas.getContext('webgl');
function get_projection(angle, a, zMin, zMax) {
var ang = Math.tan((angle*.5)*Math.PI/180);//angle*.5
return [
0.5/ang, 0 , 0, 0,
0, 0.5*a/ang, 0, 0,
0, 0, -(zMax+zMin)/(zMax-zMin), -1,
0, 0, (-2*zMax*zMin)/(zMax-zMin), 0
];
}
var proj_matrix = new DOMMatrix(get_projection(40,
canvas.width/canvas.height, 1, 100));
/*===============Instantiate GLTF Model =================*/
const model = new GltfModel('https://github.com/KhronosGroup/
glTF-Sample-Models/blob/master/2.0/BarramundiFish/
glTF-Binary/BarramundiFish.glb');
var time_old = 0;
var animate = function(time) {
var dt = time-time_old;
/*===========Update model transform each frame ==========*/
model.scene.children[0].localTransform.rotateSelf(
dt*0.003, dt*0.002, dt*0.005);
time_old = time;
gl.enable(gl.DEPTH_TEST);
gl.depthFunc(gl.LEQUAL);
gl.clearColor(0.5, 0.5, 0.5, 0.9);
gl.clearDepth(1.0);
gl.viewport(0.0, 0.0, canvas.width, canvas.height);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
/*================= Draw GLTF model ================*/
model.scene.draw(gl);
window.requestAnimationFrame(animate);
}
/*======= Wait for model download / parse to complete =========*/
model.load().then(() => {
model.scene.activeCameraNode.projectionMatrix = proj_matrix;
animate(0);
}).catch((UnknownError) => {
alert("Failed to load Model !");
})
</script>
</body>
</html>
Next Steps
WebGL is a stateful API and there are details to work through in this proposal. The purpose of this post is to gather some consensus on Why and collect feedback on How we surface GLTF support in the browser. Hope to have a conversation around this.