Skip to content

Implement glTF COLOR_0 vertex color support in StandardMaterial and UnlitMaterial - #618

Open
ind4x wants to merge 4 commits into
google:mainfrom
ind4x:main
Open

Implement glTF COLOR_0 vertex color support in StandardMaterial and UnlitMaterial#618
ind4x wants to merge 4 commits into
google:mainfrom
ind4x:main

Conversation

@ind4x

@ind4x ind4x commented May 1, 2026

Copy link
Copy Markdown

Description

Implement glTF COLOR_0 vertex color support in the scene renderer.

According to the glTF 2.0 specification (Section 3.9.2):

In addition to the material properties, if a primitive specifies a vertex color using the attribute
semantic property COLOR_0, then this value acts as an additional linear multiplier to base color.

Previously, while GltfLoader would load the COLOR_0 accessor, the data was not being utilized by the
StandardMaterial or UnlitMaterial shaders. This PR enables the vertex color attribute across the scene
renderer's shader pipeline and applies it as a linear multiplier to the base color.

Problem

Vertex colors provided in glTF assets were being ignored during shading, resulting in incorrect
rendering for models that rely on COLOR_0 for tinting, baked effects, or specific material tests.

Solution

  1. Shader Pipeline Updates:
    • Modified MaterialVertex.hlsl to define ENABLE_VTX_ATTR_COLOR and pass the color attribute from
      vertex input to the pixel shader.
    • Updated StandardMaterial.hlsl and UnlitMaterial.hlsl to multiply the calculated baseColor by
      input.Color.
  2. GltfLoader Enhancements:
    • Updated GltfLoader::LoadMeshData in scene_gltf_loader.cpp to correctly handle both VEC3 and VEC4
      glTF vertex color formats.
    • Ensured the default vertex color is set to white (1, 1, 1) when the attribute is missing,
      preventing unintended darkening.
  3. Debug Support:
    • Added DBG_VTX_ATTR_INDEX_COLOR to MaterialInterface.hlsli.
    • Updated DebugMaterial.hlsl to allow visualizing vertex colors for debugging purposes.

Verification Results

These changes enable correct rendering for several glTF-Sample-Assets, including:

  • BoxVertexColor
  • CompareBaseColor
  • IridescentDishWithOlives
  • PrimitiveModeNormalsTest
  • RecursiveSkeleton
  • SheenWoodLeatherSofa
  • VertexColorTest

@google-cla

google-cla Bot commented May 1, 2026

Copy link
Copy Markdown

Thanks for your pull request! It looks like this may be your first contribution to a Google open source project. Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA).

View this failed invocation of the CLA check for more information.

For the most up to date status, view the checks section at the bottom of the pull request.

@ind4x ind4x closed this May 1, 2026
@ind4x ind4x reopened this May 1, 2026
@ind4x ind4x closed this May 1, 2026
@ind4x ind4x reopened this May 1, 2026
@footballhead
footballhead self-requested a review May 20, 2026 14:33

@footballhead footballhead left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the contribution!

However, I'm encountering a validation layer error on Linux. When I run vk_gltf_basic_materials with the validation layers enabled, I ecounter the following assertion:

*** VULKAN VALIDATION ERROR MESSAGE ***
Severity : ERROR
Type     : [VALIDATION]
Objects  : [0]: <UNNAMED OBJECT>
Message  : vkCreateGraphicsPipelines(): pCreateInfos[0].pVertexInputState->pVertexAttributeDescriptions does not have a Location 4 but vertex shader has an input variable at that Location. (This can be valid if either the vertexAttributeRobustness or maintenance9 feature is enabled).
The Vulkan spec states: If the vertexAttributeRobustness feature is not enabled, and the maintenance9 feature is not enabled, and the pipeline is being created with vertex input state and pVertexInputState is not dynamic, then all variables with the Input storage class decorated with Location in the Vertex Execution Model OpEntryPoint must contain a location in VkVertexInputAttributeDescription::location (https://vulkan.lunarg.com/doc/view/1.4.335.0/linux/antora/spec/latest/chapters/pipelines.html#VUID-VkGraphicsPipelineCreateInfo-Input-07904)

The primary consumer of the scene loader is the gltf_basic_materials sample; I've added instructions for running it in #621. What results are you seeing?

Comment thread src/ppx/scene/scene_gltf_loader.cpp Outdated
// Process vertex data
for (cgltf_size i = 0; i < gltflAccessors.pPositions->count; ++i) {
TriMeshVertexData vertexData = {};
vertexData.color = glm::float3(1, 1, 1);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This file needs to be formatted with clang-format-16. Alternatively, the clang-format GitHub action includes a diff in its log that can be applied in order to format.

Address the Vulkan validation error reported when running
vk_gltf_basic_materials: the scene-renderer pipeline is built from the
shared MaterialVertex vertex shader which (with vertex color support)
declares an input at Location 4, but the loader was still forcing
`bits.colors = false`, so the pipeline never received a matching vertex
attribute description.

- Stop disabling vertex colors in GltfLoader::LoadMeshInternal so the
  material-requested COLOR_0 attribute (Location 4) is actually loaded.
- Drop the overly strict color-format assert in LoadMeshData. COLOR_0
  is valid as VEC3/VEC4 with float or normalized component types; the
  unpack helpers already convert components to floats via
  cgltf_accessor_unpack_floats.
- Request vertex colors from UnlitMaterial since its shader now
  multiplies the base color by input.Color.
- clang-format-16 pass over the changed sources.
@ind4x

ind4x commented Aug 31, 2026

Copy link
Copy Markdown
Author

Thanks for the review! I tracked down the validation error and fixed it, and ran clang-format-16 on the changed files.

The Location 4 validation error

The root cause is that the scene renderer builds every pipeline from the single shared MaterialVertex vertex shader, which (now that vertex colors are enabled) declares an input at Location 4. However, the pipeline's vertex attributes come from the loaded mesh data, and GltfLoader::LoadMeshInternal still had a leftover localLoadParams.requiredVertexAttributes.bits.colors = false; that disabled vertex colors on load. So the pipeline never received a Location 4 attribute, and Vulkan validation correctly complained.

This PR now:

  • Removes the bits.colors = false override in LoadMeshInternal so the material-requested COLOR_0 attribute (Location 4) is actually loaded and appears in the pipeline's VkVertexInputAttributeDescription.
  • Relaxes the color format assert in LoadMeshData. COLOR_0 is valid as VEC3/VEC4 with float or normalized component types, and the unpack helpers already convert components to floats via cgltf_accessor_unpack_floats — the old check rejected VEC4 (R32G32B32A32) and normalized (UNORM8/16) colors that the code already handles correctly.
  • Adds vertex colors to UnlitMaterial::GetRequiredVertexAttributes(), matching its shader, which now multiplies the base color by input.Color.
  • Ran clang-format-16 over the changed sources.

Please let me know if you still see the validation error or would prefer a different structure.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants