Sparrow WebGL Devlog 13: Materials and Object Inspector
A simple material system and real-time object transforms
07.11.2023 - 12:13Materials are an essential concept in computer graphics. They are used to describe how the surface of an object looks and interacts with light. From 3D modeling programs like Blender to game engines like Unreal, they all use materials.
Last week, I finally started to work on a new 3D portfolio website, which is one of the projects I have been developing a custom WebGL engine for. Because it involves a larger scene with multiple objects with different materials, I had to add a material system to my WebGL engine.
Continuing the theme of last week’s blog post, I also added two more features to the engine interface: A material editor and an object inspector to see and edit the transforms of an object.
Materials
There are many different ways you can conceptualize and implement materials. Materials are tightly integrated with the renderer or shader. For example, the same Blender material will look drastically different depending on whether you use Cycles or Eevee. A ray-tracing renderer like Cycles will produce the best-looking results and allow for the most flexibility in the material system. However, while modern GPUs come with ray-tracing cores, they are only used for certain parts of the image and cannot render the whole frame fast enough. Sadly, WebGL cannot use the ray-tracing features anyway (we would need WebGPU for that, and it’s not widely supported yet).
When using rasterized rendering like WebGL, there are still many different ways you can handle materials and shading. The best-looking option is arguably physically-based rendering (PBR). However, this is quite difficult and time-consuming to implement (I assume, I haven’t tried it yet, but I would like to at some point).
For now, I’m using very traditional Phong shading (Blinn-Phong to be exact, because it’s better for directional lights). Because I generally create low-poly and vertex-colored scenes with a more stylized look, a simpler shading model is perfectly fine in most situations.
Since I’m focusing on the default engine shader at the moment, I added only one material so far (although I’ll probably add more and different types in the future). The material has the same parameters as the global engine options: Ambient, diffuse, and specular intensities and shininess. By default, everything uses the global values unless a mesh has a material specified in which case the material options are used instead.
Two cubes with different materials and the material editor:
The most important aspect of materials is the correct assignment when importing complex models from Blender. A single object in Blender can have multiple materials that can be assigned per vertex. When such a model is exported in the glTF file format, this assignment is kept via multiple primitives per mesh (“primitives” is a bit misleading here because for me primitives are simple shapes like triangles, cubes, or spheres, but in glTF a primitive is pretty much any mesh). I already knew this when I worked on my glTF importer, so I also split these models into multiple draw calls so that material assignment would be possible later on. This means that materials are assigned per mesh in my engine and not per model (a model can have multiple meshes).
Because Blender and glTF files have different material systems, the only thing that matters is the mesh assignment and the material name. When a glTF file is imported, a new material with the correct name is automatically created and assigned to the meshes that use it. Then the material can be tweaked in the engine interface and saved/exported via a JavaScript source code line.
var material = new Sparrow.Material( engine , "red" , {"ambientIntensity":0.3,"diffuseIntensity":1,"specularIntensity":2,"shininess":1000} );
Material Options:
Object Inspector
As mentioned in the intro, I started to work on a new project that involves a more complicated scene than anything I have been testing with so far. When I tried to import it for the first time, it didn’t work correctly and some models were not at the right location. I quickly narrowed it down to models that were children of empty objects in Blender and while I thought about empties when I worked on the glTF importer, I had forgotten to apply their transforms.
While trying to find the problem, I decided to add a new feature to the engine options: A list of objects and an object inspector. The list of objects is a placeholder at the moment because it’s just a container with a bunch of buttons in it. It cannot display any parent-child relationships between the objects. A better solution would be a folder tree (I’m not sure what the correct name for a UI element like this is, basically something like the object manager in Blender or the small Windows folder view, which is why I have been calling it folder tree). Anyway, selecting an object opens up the object inspector, which shows the transforms of the object. The position and scale can be changed here, but the rotation isn’t working at the moment.
The object inspector:
Besides the empty problem, some models were also rotated incorrectly. Working with rotations is always a pain: Not only does Blender use different axes compared to WebGL, but the multiplication order of the rotation matrices also matters. You can avoid most of these problems with quaternions, but humans don’t have an inherent understanding of how quaternions work, while Euler angles (rotations around the X, Y, and Z axes) are much more straightforward.
glTF uses quaternions (and it should), but I had originally tried to convert them to Euler angles and construct the rotation matrix from there because my engine was using Euler angles. However, while this is theoretically possible, I couldn’t get it right so that the models were rotated in the same way that they were in Blender. Rather than wasting more time and trying to get it right, I gave up and used quaternion rotations instead when a model is imported from a glTF file. However, I still have to get the Euler angles to quaternion conversion right at some point to fill the angle values in the object inspector correctly and make it so that a model can be rotated by changing these values.
Unlike programming where the basic threshold is binary (does it work or not?), creating art has no such threshold. You can keep tweaking little details forever and never be happy with the result. Therefore my progress has been a lot slower while trying to create the assets and environment for the scene of the new project. I would prefer to add more features to the engine or develop a game, but I need to work on the new portfolio website and finish it sooner rather than later even though it’s quite tedious.
by Christian - 07.11.2023 - 12:13
Comments
Comments are disabled