feat: add pbr support

This commit is contained in:
2026-10-13 00:57:35 +02:00
parent ce55f75ac3
commit 21621001c2
34 changed files with 968 additions and 135 deletions
Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 MiB

@@ -0,0 +1,712 @@
{
"asset": {
"version": "2.0",
"generator": "babylon.js glTF exporter for Maya 2018 v20200228.3 (with minor hand modifications)"
},
"extensionsUsed": [
"KHR_materials_transmission"
],
"scene": 0,
"scenes": [
{
"nodes": [
0,
1,
2,
3,
4,
5
]
}
],
"nodes": [
{
"mesh": 0,
"name": "Hose_low"
},
{
"mesh": 1,
"name": "RubberWood_low"
},
{
"mesh": 2,
"name": "GlassPlastic_low"
},
{
"mesh": 3,
"name": "MetalParts_low"
},
{
"mesh": 4,
"name": "LeatherParts_low"
},
{
"mesh": 5,
"name": "Lenses_low"
}
],
"meshes": [
{
"primitives": [
{
"attributes": {
"POSITION": 1,
"TANGENT": 2,
"NORMAL": 3,
"TEXCOORD_0": 4
},
"indices": 0,
"material": 0
}
],
"name": "Hose_low"
},
{
"primitives": [
{
"attributes": {
"POSITION": 6,
"TANGENT": 7,
"NORMAL": 8,
"TEXCOORD_0": 9
},
"indices": 5,
"material": 1
}
],
"name": "RubberWood_low"
},
{
"primitives": [
{
"attributes": {
"POSITION": 11,
"TANGENT": 12,
"NORMAL": 13,
"TEXCOORD_0": 14
},
"indices": 10,
"material": 2
}
],
"name": "GlassPlastic_low"
},
{
"primitives": [
{
"attributes": {
"POSITION": 16,
"TANGENT": 17,
"NORMAL": 18,
"TEXCOORD_0": 19
},
"indices": 15,
"material": 3
}
],
"name": "MetalParts_low"
},
{
"primitives": [
{
"attributes": {
"POSITION": 21,
"TANGENT": 22,
"NORMAL": 23,
"TEXCOORD_0": 24
},
"indices": 20,
"material": 4
}
],
"name": "LeatherParts_low"
},
{
"primitives": [
{
"attributes": {
"POSITION": 26,
"TANGENT": 27,
"NORMAL": 28,
"TEXCOORD_0": 29
},
"indices": 25,
"material": 5
}
],
"name": "Lenses_low"
}
],
"accessors": [
{
"bufferView": 0,
"componentType": 5123,
"count": 59040,
"type": "SCALAR",
"name": "accessorIndices"
},
{
"bufferView": 1,
"componentType": 5126,
"count": 10472,
"max": [
0.10810829,
0.356580257,
0.190409869
],
"min": [
-0.07221258,
0.104120225,
-0.088394776
],
"type": "VEC3",
"name": "accessorPositions"
},
{
"bufferView": 2,
"componentType": 5126,
"count": 10472,
"type": "VEC4",
"name": "accessorTangents"
},
{
"bufferView": 1,
"byteOffset": 125664,
"componentType": 5126,
"count": 10472,
"type": "VEC3",
"name": "accessorNormals"
},
{
"bufferView": 3,
"componentType": 5126,
"count": 10472,
"type": "VEC2",
"name": "accessorUVs"
},
{
"bufferView": 0,
"byteOffset": 118080,
"componentType": 5123,
"count": 72534,
"type": "SCALAR",
"name": "accessorIndices"
},
{
"bufferView": 1,
"byteOffset": 251328,
"componentType": 5126,
"count": 13638,
"max": [
0.162940636,
0.7025226,
0.200029165
],
"min": [
-0.158857465,
-2.14242937E-05,
-0.171545789
],
"type": "VEC3",
"name": "accessorPositions"
},
{
"bufferView": 2,
"byteOffset": 167552,
"componentType": 5126,
"count": 13638,
"type": "VEC4",
"name": "accessorTangents"
},
{
"bufferView": 1,
"byteOffset": 414984,
"componentType": 5126,
"count": 13638,
"type": "VEC3",
"name": "accessorNormals"
},
{
"bufferView": 3,
"byteOffset": 83776,
"componentType": 5126,
"count": 13638,
"type": "VEC2",
"name": "accessorUVs"
},
{
"bufferView": 0,
"byteOffset": 263148,
"componentType": 5123,
"count": 24408,
"type": "SCALAR",
"name": "accessorIndices"
},
{
"bufferView": 1,
"byteOffset": 578640,
"componentType": 5126,
"count": 4676,
"max": [
0.140494063,
0.61828655,
0.147373646
],
"min": [
-0.140846014,
0.440957,
-0.107818365
],
"type": "VEC3",
"name": "accessorPositions"
},
{
"bufferView": 2,
"byteOffset": 385760,
"componentType": 5126,
"count": 4676,
"type": "VEC4",
"name": "accessorTangents"
},
{
"bufferView": 1,
"byteOffset": 634752,
"componentType": 5126,
"count": 4676,
"type": "VEC3",
"name": "accessorNormals"
},
{
"bufferView": 3,
"byteOffset": 192880,
"componentType": 5126,
"count": 4676,
"type": "VEC2",
"name": "accessorUVs"
},
{
"bufferView": 0,
"byteOffset": 311964,
"componentType": 5123,
"count": 60288,
"type": "SCALAR",
"name": "accessorIndices"
},
{
"bufferView": 1,
"byteOffset": 690864,
"componentType": 5126,
"count": 13636,
"max": [
0.132708371,
0.6024364,
0.199477077
],
"min": [
-0.203642711,
0.02116075,
-0.147512689
],
"type": "VEC3",
"name": "accessorPositions"
},
{
"bufferView": 2,
"byteOffset": 460576,
"componentType": 5126,
"count": 13636,
"type": "VEC4",
"name": "accessorTangents"
},
{
"bufferView": 1,
"byteOffset": 854496,
"componentType": 5126,
"count": 13636,
"type": "VEC3",
"name": "accessorNormals"
},
{
"bufferView": 3,
"byteOffset": 230288,
"componentType": 5126,
"count": 13636,
"type": "VEC2",
"name": "accessorUVs"
},
{
"bufferView": 0,
"byteOffset": 432540,
"componentType": 5123,
"count": 65688,
"type": "SCALAR",
"name": "accessorIndices"
},
{
"bufferView": 1,
"byteOffset": 1018128,
"componentType": 5126,
"count": 12534,
"max": [
0.124933377,
0.716000438,
0.129168555
],
"min": [
-0.125863016,
0.2958266,
-0.1541516
],
"type": "VEC3",
"name": "accessorPositions"
},
{
"bufferView": 2,
"byteOffset": 678752,
"componentType": 5126,
"count": 12534,
"type": "VEC4",
"name": "accessorTangents"
},
{
"bufferView": 1,
"byteOffset": 1168536,
"componentType": 5126,
"count": 12534,
"type": "VEC3",
"name": "accessorNormals"
},
{
"bufferView": 3,
"byteOffset": 339376,
"componentType": 5126,
"count": 12534,
"type": "VEC2",
"name": "accessorUVs"
},
{
"bufferView": 0,
"byteOffset": 563916,
"componentType": 5123,
"count": 2208,
"type": "SCALAR",
"name": "accessorIndices"
},
{
"bufferView": 1,
"byteOffset": 1318944,
"componentType": 5126,
"count": 436,
"max": [
0.101920746,
0.5936986,
0.152926728
],
"min": [
-0.101920947,
0.5300429,
0.090174824
],
"type": "VEC3",
"name": "accessorPositions"
},
{
"bufferView": 2,
"byteOffset": 879296,
"componentType": 5126,
"count": 436,
"type": "VEC4",
"name": "accessorTangents"
},
{
"bufferView": 1,
"byteOffset": 1324176,
"componentType": 5126,
"count": 436,
"type": "VEC3",
"name": "accessorNormals"
},
{
"bufferView": 3,
"byteOffset": 439648,
"componentType": 5126,
"count": 436,
"type": "VEC2",
"name": "accessorUVs"
}
],
"bufferViews": [
{
"buffer": 0,
"byteLength": 568332,
"name": "bufferViewScalar"
},
{
"buffer": 0,
"byteOffset": 568332,
"byteLength": 1329408,
"byteStride": 12,
"name": "bufferViewFloatVec3"
},
{
"buffer": 0,
"byteOffset": 1897740,
"byteLength": 886272,
"byteStride": 16,
"name": "bufferViewFloatVec4"
},
{
"buffer": 0,
"byteOffset": 2784012,
"byteLength": 443136,
"byteStride": 8,
"name": "bufferViewFloatVec2"
}
],
"buffers": [
{
"uri": "FlightHelmet.bin",
"byteLength": 3227148
}
],
"materials": [
{
"pbrMetallicRoughness": {
"baseColorTexture": {
"index": 2
},
"metallicRoughnessTexture": {
"index": 1
}
},
"normalTexture": {
"index": 0
},
"occlusionTexture": {
"index": 1
},
"doubleSided": true,
"name": "HoseMat"
},
{
"pbrMetallicRoughness": {
"baseColorTexture": {
"index": 2
},
"metallicRoughnessTexture": {
"index": 1
}
},
"normalTexture": {
"index": 0
},
"occlusionTexture": {
"index": 1
},
"name": "RubberWoodMat"
},
{
"pbrMetallicRoughness": {
"baseColorTexture": {
"index": 5
},
"metallicRoughnessTexture": {
"index": 4
}
},
"normalTexture": {
"index": 3
},
"occlusionTexture": {
"index": 4
},
"name": "GlassPlasticMat"
},
{
"pbrMetallicRoughness": {
"baseColorTexture": {
"index": 8
},
"metallicRoughnessTexture": {
"index": 7
}
},
"normalTexture": {
"index": 6
},
"occlusionTexture": {
"index": 7
},
"name": "MetalPartsMat"
},
{
"pbrMetallicRoughness": {
"baseColorTexture": {
"index": 11
},
"metallicRoughnessTexture": {
"index": 10
}
},
"normalTexture": {
"index": 9
},
"occlusionTexture": {
"index": 10
},
"name": "LeatherPartsMat"
},
{
"extensions": {
"KHR_materials_transmission": {
"transmissionFactor": 1
}
},
"pbrMetallicRoughness": {
"baseColorTexture": {
"index": 14
},
"metallicRoughnessTexture": {
"index": 13
}
},
"normalTexture": {
"index": 12
},
"occlusionTexture": {
"index": 13
},
"name": "LensesMat"
}
],
"textures": [
{
"sampler": 0,
"source": 0,
"name": "FlightHelmet_Materials_RubberWoodMat_Normal.png"
},
{
"sampler": 0,
"source": 1,
"name": "FlightHelmet_Materials_RubberWoodMat_OcclusionRoughMetal.png"
},
{
"sampler": 0,
"source": 2,
"name": "FlightHelmet_Materials_RubberWoodMat_BaseColor.png"
},
{
"sampler": 0,
"source": 3,
"name": "FlightHelmet_Materials_GlassPlasticMat_Normal.png"
},
{
"sampler": 0,
"source": 4,
"name": "FlightHelmet_Materials_GlassPlasticMat_OcclusionRoughMetal.png"
},
{
"sampler": 0,
"source": 5,
"name": "FlightHelmet_Materials_GlassPlasticMat_BaseColor.png"
},
{
"sampler": 0,
"source": 6,
"name": "FlightHelmet_Materials_MetalPartsMat_Normal.png"
},
{
"sampler": 0,
"source": 7,
"name": "FlightHelmet_Materials_MetalPartsMat_OcclusionRoughMetal.png"
},
{
"sampler": 0,
"source": 8,
"name": "FlightHelmet_Materials_MetalPartsMat_BaseColor.png"
},
{
"sampler": 0,
"source": 9,
"name": "FlightHelmet_Materials_LeatherPartsMat_Normal.png"
},
{
"sampler": 0,
"source": 10,
"name": "FlightHelmet_Materials_LeatherPartsMat_OcclusionRoughMetal.png"
},
{
"sampler": 0,
"source": 11,
"name": "FlightHelmet_Materials_LeatherPartsMat_BaseColor.png"
},
{
"sampler": 0,
"source": 12,
"name": "FlightHelmet_Materials_LensesMat_Normal.png"
},
{
"sampler": 0,
"source": 13,
"name": "FlightHelmet_Materials_LensesMat_OcclusionRoughMetal.png"
},
{
"sampler": 0,
"source": 14,
"name": "FlightHelmet_Materials_LensesMat_BaseColor.png"
}
],
"images": [
{
"uri": "FlightHelmet_Materials_RubberWoodMat_Normal.png"
},
{
"uri": "FlightHelmet_Materials_RubberWoodMat_OcclusionRoughMetal.png"
},
{
"uri": "FlightHelmet_Materials_RubberWoodMat_BaseColor.png"
},
{
"uri": "FlightHelmet_Materials_GlassPlasticMat_Normal.png"
},
{
"uri": "FlightHelmet_Materials_GlassPlasticMat_OcclusionRoughMetal.png"
},
{
"uri": "FlightHelmet_Materials_GlassPlasticMat_BaseColor.png"
},
{
"uri": "FlightHelmet_Materials_MetalPartsMat_Normal.png"
},
{
"uri": "FlightHelmet_Materials_MetalPartsMat_OcclusionRoughMetal.png"
},
{
"uri": "FlightHelmet_Materials_MetalPartsMat_BaseColor.png"
},
{
"uri": "FlightHelmet_Materials_LeatherPartsMat_Normal.png"
},
{
"uri": "FlightHelmet_Materials_LeatherPartsMat_OcclusionRoughMetal.png"
},
{
"uri": "FlightHelmet_Materials_LeatherPartsMat_BaseColor.png"
},
{
"uri": "FlightHelmet_Materials_LensesMat_Normal.png"
},
{
"uri": "FlightHelmet_Materials_LensesMat_OcclusionRoughMetal.png"
},
{
"uri": "FlightHelmet_Materials_LensesMat_BaseColor.png"
}
],
"samplers": [
{
"magFilter": 9729,
"minFilter": 9987
}
]
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 680 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 588 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 MiB

+12
View File
@@ -83,6 +83,18 @@ vec3 calculateLight(Light light, vec3 fragPos, vec3 n, vec3 v,
return (diffuse + specular) * light.color * light.intensity * attenuation * NoL * occlusion; return (diffuse + specular) * light.color * light.intensity * attenuation * NoL * occlusion;
} }
// Analytic approximation of the split sum environment BRDF (Karis, "Physically Based Shading on Mobile").
// Used to light the flat ambient term with the same specular response as the lights.
vec3 envBRDFApprox(vec3 f0, float roughness, float NoV)
{
const vec4 c0 = vec4(-1.0, -0.0275, -0.572, 0.022);
const vec4 c1 = vec4(1.0, 0.0425, 1.04, -0.04);
vec4 r = roughness * c0 + c1;
float a004 = min(r.x * r.x, exp2(-9.28 * NoV)) * r.x + r.y;
vec2 ab = vec2(-1.04, 1.04) * a004 + r.zw;
return f0 * ab.x + ab.y;
}
// blue -> green -> red, used to visualise how many lights a tile has // blue -> green -> red, used to visualise how many lights a tile has
vec3 lightHeatmapColor(uint lightCount) vec3 lightHeatmapColor(uint lightCount)
{ {
+7 -2
View File
@@ -3,14 +3,19 @@
#extension GL_EXT_buffer_reference : require #extension GL_EXT_buffer_reference : require
// Must match MaterialData in Material.h
struct MaterialData { struct MaterialData {
vec4 baseColor; vec4 baseColor;
vec4 metallicRoughnessEmissive; vec4 emissiveColor;
vec4 metallicRoughnessNormalOcclusion;
uint textureFilteringMode; uint textureFilteringMode;
uint diffuseTex; uint diffuseTex;
uint normalTex; uint normalTex;
uint metallicRoughnessTex; uint metallicTex;
uint roughnessTex;
uint occlusionTex;
uint emissiveTex; uint emissiveTex;
uint _pad0;
}; };
layout (buffer_reference, std430) readonly buffer MaterialsBuffer { layout (buffer_reference, std430) readonly buffer MaterialsBuffer {
+39 -29
View File
@@ -12,14 +12,32 @@ layout (location = 0) in vec3 inPos;
layout (location = 1) in vec2 inUV; layout (location = 1) in vec2 inUV;
layout (location = 2) in vec3 inNormal; layout (location = 2) in vec3 inNormal;
layout (location = 3) in vec4 inTangent; layout (location = 3) in vec4 inTangent;
layout (location = 4) in mat3 inTBN;
layout (location = 0) out vec4 outFragColor; layout (location = 0) out vec4 outFragColor;
vec3 calculateNormal(MaterialData material, uint samplerID)
{
vec3 n = normalize(inNormal);
// Meshes without tangents can't use a normal map
if (dot(inTangent.xyz, inTangent.xyz) < 1e-8) {
return n;
}
// Re-orthogonalize, the interpolated tangent isn't perpendicular to the normal anymore
vec3 t = normalize(inTangent.xyz - n * dot(n, inTangent.xyz));
vec3 b = cross(n, t) * inTangent.w;
vec3 tangentNormal = sampleTexture2D(material.normalTex, samplerID, inUV).rgb * 2.0 - 1.0;
tangentNormal.xy *= material.metallicRoughnessNormalOcclusion.z;
return normalize(mat3(t, b, n) * tangentNormal);
}
void main() void main()
{ {
MaterialData material = pcs.sceneData.materials.data[pcs.materialID]; MaterialData material = pcs.sceneData.materials.data[pcs.materialID];
uint samplerID = pcs.sceneData.materials.data[pcs.materialID].textureFilteringMode; uint samplerID = material.textureFilteringMode;
vec4 diffuse = sampleTexture2D(material.diffuseTex, samplerID, inUV) * material.baseColor; vec4 diffuse = sampleTexture2D(material.diffuseTex, samplerID, inUV) * material.baseColor;
vec3 baseColor = diffuse.rgb; vec3 baseColor = diffuse.rgb;
@@ -28,27 +46,18 @@ void main()
// discard; // discard;
// } // }
vec3 normal = normalize(inNormal); vec3 n = calculateNormal(material, samplerID);
// if (inTangent != vec4(0.0)) {
// // FIXME: sometimes Blender doesn't export tangents for some objects
// // for some reason. When we will start computing tangents manually,
// // this check can be removed
// normal = sampleTexture2DLinear(material.normalTex, inUV).rgb;
// // normal.y = 1 - normal.y; // flip to make OpenGL normal maps work
// normal = inTBN * normalize(normal * 2.0 - 1.0);
// normal = normalize(normal);
// }
float metallicF = material.metallicRoughnessEmissive.r; float metallicF = material.metallicRoughnessNormalOcclusion.x;
float roughnessF = material.metallicRoughnessEmissive.g; float roughnessF = material.metallicRoughnessNormalOcclusion.y;
float occlusionStrength = material.metallicRoughnessNormalOcclusion.w;
vec4 metallicRoughness = sampleTexture2DLinear(material.metallicRoughnessTex, inUV); float metallic = metallicF * sampleTexture2D(material.metallicTex, samplerID, inUV).b;
float roughness = roughnessF * sampleTexture2D(material.roughnessTex, samplerID, inUV).g;
float roughness = roughnessF * metallicRoughness.g;
// roughness *= roughness; // from perceptual to linear
roughness = max(roughness, 1e-2); // 0.0 roughness leads to NaNs roughness = max(roughness, 1e-2); // 0.0 roughness leads to NaNs
float metallic = metallicF * metallicRoughness.b; float occlusion = sampleTexture2D(material.occlusionTex, samplerID, inUV).r;
float ambientOcclusion = mix(1.0, occlusion, occlusionStrength);
vec3 dielectricSpecular = vec3(0.04); vec3 dielectricSpecular = vec3(0.04);
vec3 black = vec3(0.0); vec3 black = vec3(0.0);
@@ -57,7 +66,6 @@ void main()
vec3 cameraPos = pcs.sceneData.cameraPos.xyz; vec3 cameraPos = pcs.sceneData.cameraPos.xyz;
vec3 fragPos = inPos.xyz; vec3 fragPos = inPos.xyz;
vec3 n = normal;
vec3 v = normalize(cameraPos - fragPos); vec3 v = normalize(cameraPos - fragPos);
// forward+: only loop over the lights the culling pass put in this pixel's tile // forward+: only loop over the lights the culling pass put in this pixel's tile
@@ -70,26 +78,28 @@ void main()
uint lightIndex = pcs.sceneData.tileLightIndices.data[tileOffset + 1 + i]; uint lightIndex = pcs.sceneData.tileLightIndices.data[tileOffset + 1 + i];
Light light = pcs.sceneData.lights.data[lightIndex]; Light light = pcs.sceneData.lights.data[lightIndex];
float occlusion = 1.0; float shadow = 1.0;
if (light.castShadows != 0) { if (light.castShadows != 0) {
occlusion = calculateDirectionalShadow( shadow = calculateDirectionalShadow(
fragPos, n, fragPos, normalize(inNormal),
pcs.sceneData.lightViewProj, pcs.sceneData.lightViewProj,
pcs.sceneData.shadowMapId, pcs.sceneData.shadowMapId,
pcs.sceneData.shadowNormalOffset); pcs.sceneData.shadowNormalOffset);
} }
fragColor += calculateLight(light, fragPos, n, v, fragColor += calculateLight(light, fragPos, n, v,
diffuseColor, roughness, metallic, f0, occlusion); diffuseColor, roughness, metallic, f0, shadow);
} }
// emissive // emissive
float emissiveF = material.metallicRoughnessEmissive.b; vec3 emissiveColor = material.emissiveColor.rgb * sampleTexture2D(material.emissiveTex, samplerID, inUV).rgb;
vec3 emissiveColor = emissiveF * sampleTexture2DLinear(material.emissiveTex, inUV).rgb;
fragColor += emissiveColor; fragColor += emissiveColor;
// ambient // ambient, the specular part keeps metals from turning black without lights
fragColor += baseColor * pcs.sceneData.ambientColor * pcs.sceneData.ambientIntensity; float NoV = clamp(dot(n, v), 0.0001, 1.0);
vec3 ambientLight = pcs.sceneData.ambientColor * pcs.sceneData.ambientIntensity;
vec3 ambientSpecular = envBRDFApprox(f0, roughness, NoV);
fragColor += (diffuseColor + ambientSpecular) * ambientLight * ambientOcclusion;
if (pcs.sceneData.debugLightHeatmap != 0) { if (pcs.sceneData.debugLightHeatmap != 0) {
fragColor = mix(fragColor, lightHeatmapColor(tileLightCount), 0.5); fragColor = mix(fragColor, lightHeatmapColor(tileLightCount), 0.5);
@@ -104,7 +114,7 @@ void main()
#if 0 #if 0
// NORMAL DEBUG // NORMAL DEBUG
fragColor = normal; fragColor = n;
#endif #endif
outFragColor = vec4(fragColor, diffuse.a); outFragColor = vec4(fragColor, diffuse.a);
+2 -7
View File
@@ -8,7 +8,6 @@ layout (location = 0) out vec3 outPos;
layout (location = 1) out vec2 outUV; layout (location = 1) out vec2 outUV;
layout (location = 2) out vec3 outNormal; layout (location = 2) out vec3 outNormal;
layout (location = 3) out vec4 outTangent; layout (location = 3) out vec4 outTangent;
layout (location = 4) out mat3 outTBN;
// The depth pre-pass reuses this shader, positions have to match bit for bit // The depth pre-pass reuses this shader, positions have to match bit for bit
// so the main pass can depth test against it with LESS_OR_EQUAL // so the main pass can depth test against it with LESS_OR_EQUAL
@@ -29,10 +28,6 @@ void main()
// outNormal = (pcs.transform * vec4(v.normal, 0.0)).xyz; // outNormal = (pcs.transform * vec4(v.normal, 0.0)).xyz;
outNormal = mat3(transpose(inverse(pcs.transform))) * v.normal; outNormal = mat3(transpose(inverse(pcs.transform))) * v.normal;
outTangent = v.tangent; // w is the bitangent sign, the fragment shader builds the TBN from the interpolated vectors
outTangent = vec4(mat3(pcs.transform) * v.tangent.xyz, v.tangent.w);
vec3 T = normalize(vec3(pcs.transform * v.tangent));
vec3 N = normalize(outNormal);
vec3 B = cross(N, T) * v.tangent.w;
outTBN = mat3(T, B, N);
} }
+1 -1
View File
@@ -11,7 +11,7 @@
#include <destrum/Util/ModelDoc.h> #include <destrum/Util/ModelDoc.h>
namespace ModelFile { namespace ModelFile {
inline constexpr std::uint32_t Version = 1; inline constexpr std::uint32_t Version = 2;
inline constexpr std::string_view Extension = ".dmodel"; inline constexpr std::string_view Extension = ".dmodel";
inline constexpr ModelDoc::LoadOptions CookOptions{}; inline constexpr ModelDoc::LoadOptions CookOptions{};
@@ -50,6 +50,8 @@ public:
void updateMaterialGPU(MaterialID id); void updateMaterialGPU(MaterialID id);
private: private:
[[nodiscard]] MaterialData toMaterialData(const Material& material) const;
std::vector<Material> materials; std::vector<Material> materials;
std::vector<std::string> materialKeys; std::vector<std::string> materialKeys;
+27 -8
View File
@@ -5,17 +5,23 @@
#include <utility> #include <utility>
#include <glm/glm.hpp> #include <glm/glm.hpp>
#include <destrum/Assets/AssetReference.h> #include <destrum/Assets/AssetReference.h>
#include <destrum/Graphics/ids.h>
// Must match MaterialData in materials.glsl
struct alignas(16) MaterialData { struct alignas(16) MaterialData {
alignas(16) glm::vec4 baseColor; alignas(16) glm::vec4 baseColor;
alignas(16) glm::vec4 metalRoughnessEmissive; alignas(16) glm::vec4 emissiveColor;
alignas(16) glm::vec4 metallicRoughnessNormalOcclusion;
uint32_t textureFilteringMode; uint32_t textureFilteringMode;
uint32_t diffuseTex; uint32_t diffuseTex;
uint32_t normalTex; uint32_t normalTex;
uint32_t metallicRoughnessTex; uint32_t metallicTex;
uint32_t roughnessTex;
uint32_t occlusionTex;
uint32_t emissiveTex; uint32_t emissiveTex;
uint32_t _pad0, _pad1, _pad2; // explicit to 64 bytes uint32_t _pad0;
}; };
static_assert(sizeof(MaterialData) == 80);
enum class TextureFilteringMode : std::uint32_t { enum class TextureFilteringMode : std::uint32_t {
Nearest, Nearest,
@@ -23,6 +29,9 @@ enum class TextureFilteringMode : std::uint32_t {
Anisotropic, Anisotropic,
}; };
// Metallic / roughness workflow, matching glTF:
// metallic is read from the blue channel, roughness from green and occlusion from red,
// so a packed (occlusion)metallicRoughness texture can be used for all three slots.
struct Material { struct Material {
[[nodiscard]] static Material SimpleColor( [[nodiscard]] static Material SimpleColor(
glm::vec3 color, glm::vec3 color,
@@ -32,22 +41,32 @@ struct Material {
material.baseColor = color; material.baseColor = color;
material.metallicFactor = 0.0f; material.metallicFactor = 0.0f;
material.roughnessFactor = 1.0f; material.roughnessFactor = 1.0f;
material.emissiveFactor = 0.0f;
material.name = std::move(materialName); material.name = std::move(materialName);
return material; return material;
} }
[[nodiscard]] bool UsesTexture(ImageID id) const
{
return id != NULL_IMAGE_ID &&
(diffuseTexture == id || normalTexture == id || metallicTexture == id ||
roughnessTexture == id || occlusionTexture == id || emissiveTexture == id);
}
glm::vec3 baseColor{1.f, 1.f, 1.f}; glm::vec3 baseColor{1.f, 1.f, 1.f};
float metallicFactor{0.f}; float metallicFactor{0.f};
float roughnessFactor{0.7f}; float roughnessFactor{0.7f};
float emissiveFactor{0.f}; glm::vec3 emissiveColor{0.f};
float normalScale{1.f};
float occlusionStrength{1.f};
TextureFilteringMode textureFilteringMode{TextureFilteringMode::Linear}; TextureFilteringMode textureFilteringMode{TextureFilteringMode::Linear};
ImageID diffuseTexture{NULL_IMAGE_ID}; ImageID diffuseTexture{NULL_IMAGE_ID};
// ImageId normalMapTexture{NULL_IMAGE_ID}; ImageID normalTexture{NULL_IMAGE_ID};
// ImageId metallicRoughnessTexture{NULL_IMAGE_ID}; ImageID metallicTexture{NULL_IMAGE_ID};
// ImageId emissiveTexture{NULL_IMAGE_ID}; ImageID roughnessTexture{NULL_IMAGE_ID};
ImageID occlusionTexture{NULL_IMAGE_ID};
ImageID emissiveTexture{NULL_IMAGE_ID};
std::string name; std::string name;
AssetReference assetReference; AssetReference assetReference;
@@ -11,6 +11,8 @@
#include <memory> #include <memory>
#include <string_view> #include <string_view>
namespace ModelDoc { struct MaterialInfo; }
class GfxDevice; class GfxDevice;
class RenderResources { class RenderResources {
@@ -40,7 +42,7 @@ public:
GfxDevice& gfxDevice, GfxDevice& gfxDevice,
const std::filesystem::path& path, const std::filesystem::path& path,
VkImageUsageFlags usage = VK_IMAGE_USAGE_SAMPLED_BIT, VkImageUsageFlags usage = VK_IMAGE_USAGE_SAMPLED_BIT,
bool mipMap = false, bool mipMap = true,
TextureIntent intent = TextureIntent::ColorSrgb); TextureIntent intent = TextureIntent::ColorSrgb);
ImageID addImageToCache(GPUImage image); ImageID addImageToCache(GPUImage image);
@@ -49,6 +51,9 @@ public:
// References are keyed as "source#subresource" in the resource caches. // References are keyed as "source#subresource" in the resource caches.
void loadModelAsset(GfxDevice& gfxDevice, std::string_view sourceReference); void loadModelAsset(GfxDevice& gfxDevice, std::string_view sourceReference);
// Creates a PBR material from an imported model material, loading its texture maps.
MaterialID addModelMaterial(GfxDevice& gfxDevice, const ModelDoc::MaterialInfo& sourceMaterial);
[[nodiscard]] const GPUImage& getImage(ImageID id) const; [[nodiscard]] const GPUImage& getImage(ImageID id) const;
[[nodiscard]] ImageID getWhiteTextureID() const { return whiteImageId; } [[nodiscard]] ImageID getWhiteTextureID() const { return whiteImageId; }
+3
View File
@@ -369,6 +369,9 @@ static MaterialInfo LoadMaterialInfo(const aiScene* scene,
TextureSemantic::Roughness, out.textures); TextureSemantic::Roughness, out.textures);
AddMaterialTextureSlots(scene, material, modelDir, aiTextureType_AMBIENT_OCCLUSION, AddMaterialTextureSlots(scene, material, modelDir, aiTextureType_AMBIENT_OCCLUSION,
TextureSemantic::AmbientOcclusion, out.textures); TextureSemantic::AmbientOcclusion, out.textures);
// glTF occlusion maps are imported as lightmaps
AddMaterialTextureSlots(scene, material, modelDir, aiTextureType_LIGHTMAP,
TextureSemantic::AmbientOcclusion, out.textures);
AddMaterialTextureSlots(scene, material, modelDir, aiTextureType_EMISSION_COLOR, AddMaterialTextureSlots(scene, material, modelDir, aiTextureType_EMISSION_COLOR,
TextureSemantic::Emissive, out.textures); TextureSemantic::Emissive, out.textures);
@@ -147,6 +147,8 @@ void BindlessSetManager::initDefaultSamplers(VkDevice device, float maxAnisotrop
.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO, .sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO,
.magFilter = VK_FILTER_NEAREST, .magFilter = VK_FILTER_NEAREST,
.minFilter = VK_FILTER_NEAREST, .minFilter = VK_FILTER_NEAREST,
.mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST,
.maxLod = VK_LOD_CLAMP_NONE,
}; };
VK_CHECK(vkCreateSampler(device, &samplerCreateInfo, nullptr, &nearestSampler)); VK_CHECK(vkCreateSampler(device, &samplerCreateInfo, nullptr, &nearestSampler));
vkutil::addDebugLabel(device, nearestSampler, "nearest"); vkutil::addDebugLabel(device, nearestSampler, "nearest");
@@ -159,6 +161,7 @@ void BindlessSetManager::initDefaultSamplers(VkDevice device, float maxAnisotrop
.magFilter = VK_FILTER_LINEAR, .magFilter = VK_FILTER_LINEAR,
.minFilter = VK_FILTER_LINEAR, .minFilter = VK_FILTER_LINEAR,
.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR, .mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR,
.maxLod = VK_LOD_CLAMP_NONE,
}; };
VK_CHECK(vkCreateSampler(device, &samplerCreateInfo, nullptr, &linearSampler)); VK_CHECK(vkCreateSampler(device, &samplerCreateInfo, nullptr, &linearSampler));
vkutil::addDebugLabel(device, linearSampler, "linear"); vkutil::addDebugLabel(device, linearSampler, "linear");
@@ -173,6 +176,7 @@ void BindlessSetManager::initDefaultSamplers(VkDevice device, float maxAnisotrop
.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR, .mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR,
.anisotropyEnable = VK_TRUE, .anisotropyEnable = VK_TRUE,
.maxAnisotropy = maxAnisotropy, .maxAnisotropy = maxAnisotropy,
.maxLod = VK_LOD_CLAMP_NONE,
}; };
VK_CHECK(vkCreateSampler(device, &samplerCreateInfo, nullptr, &anisotropicSampler)); VK_CHECK(vkCreateSampler(device, &samplerCreateInfo, nullptr, &anisotropicSampler));
vkutil::addDebugLabel(device, anisotropicSampler, "anisotropic"); vkutil::addDebugLabel(device, anisotropicSampler, "anisotropic");
+31 -50
View File
@@ -57,11 +57,6 @@ MaterialID MaterialCache::addMaterial(Material material)
device->waitIdle(); device->waitIdle();
} }
const auto getTextureOrElse = [](ImageID imageId, ImageID placeholder)
{
return imageId != NULL_IMAGE_ID ? imageId : placeholder;
};
MaterialData* data = static_cast<MaterialData*>(materialDataBuffer.info.pMappedData); MaterialData* data = static_cast<MaterialData*>(materialDataBuffer.info.pMappedData);
const auto id = getFreeMaterialId(); const auto id = getFreeMaterialId();
@@ -72,28 +67,7 @@ MaterialID MaterialCache::addMaterial(Material material)
throw std::runtime_error("MaterialCache material buffer is not mapped"); throw std::runtime_error("MaterialCache material buffer is not mapped");
} }
data[id] = MaterialData{ data[id] = toMaterialData(material);
.baseColor = glm::vec4(material.baseColor, 1.0f),
.metalRoughnessEmissive = glm::vec4{
material.metallicFactor,
material.roughnessFactor,
material.emissiveFactor,
0.f
},
.textureFilteringMode = static_cast<std::uint32_t>(material.textureFilteringMode),
.diffuseTex = getTextureOrElse(
material.diffuseTexture,
defaultTextures.white),
.normalTex = defaultTextures.normal,
.metallicRoughnessTex = defaultTextures.metallicRoughness,
.emissiveTex = defaultTextures.emissive,
};
device->getMemoryManager().flushAllocation( device->getMemoryManager().flushAllocation(
materialDataBuffer, materialDataBuffer,
static_cast<VkDeviceSize>(id) * sizeof(MaterialData), static_cast<VkDeviceSize>(id) * sizeof(MaterialData),
@@ -190,6 +164,35 @@ std::optional<MaterialID> MaterialCache::findMaterialByName(
return fallback; return fallback;
} }
MaterialData MaterialCache::toMaterialData(const Material& material) const
{
const auto getTextureOrElse = [](ImageID imageId, ImageID placeholder)
{
return imageId != NULL_IMAGE_ID ? imageId : placeholder;
};
return MaterialData{
.baseColor = glm::vec4(material.baseColor, 1.0f),
.emissiveColor = glm::vec4(material.emissiveColor, 0.0f),
.metallicRoughnessNormalOcclusion = glm::vec4{
material.metallicFactor,
material.roughnessFactor,
material.normalScale,
material.occlusionStrength
},
.textureFilteringMode = static_cast<std::uint32_t>(material.textureFilteringMode),
.diffuseTex = getTextureOrElse(material.diffuseTexture, defaultTextures.white),
.normalTex = getTextureOrElse(material.normalTexture, defaultTextures.normal),
.metallicTex = getTextureOrElse(material.metallicTexture, defaultTextures.metallicRoughness),
.roughnessTex = getTextureOrElse(material.roughnessTexture, defaultTextures.metallicRoughness),
.occlusionTex = getTextureOrElse(material.occlusionTexture, defaultTextures.white),
.emissiveTex = getTextureOrElse(material.emissiveTexture, defaultTextures.emissive),
._pad0 = 0,
};
}
MaterialID MaterialCache::getFreeMaterialId() const MaterialID MaterialCache::getFreeMaterialId() const
{ {
return static_cast<MaterialID>(materials.size()); return static_cast<MaterialID>(materials.size());
@@ -212,32 +215,10 @@ void MaterialCache::updateMaterialGPU(MaterialID id)
assert(id < materials.size()); assert(id < materials.size());
assert(materialDataBuffer.info.pMappedData && "materialDataBuffer must be mapped"); assert(materialDataBuffer.info.pMappedData && "materialDataBuffer must be mapped");
const auto getTextureOrElse = [](ImageID imageId, ImageID placeholder)
{
return imageId != NULL_IMAGE_ID ? imageId : placeholder;
};
Material& material = materials[id];
MaterialData* data = MaterialData* data =
reinterpret_cast<MaterialData*>(materialDataBuffer.info.pMappedData); reinterpret_cast<MaterialData*>(materialDataBuffer.info.pMappedData);
data[id] = MaterialData{ data[id] = toMaterialData(materials[id]);
.baseColor = glm::vec4(material.baseColor, 1.0f),
.metalRoughnessEmissive = glm::vec4{
material.metallicFactor,
material.roughnessFactor,
material.emissiveFactor,
0.f
},
.textureFilteringMode = static_cast<std::uint32_t>(material.textureFilteringMode),
.diffuseTex = getTextureOrElse(material.diffuseTexture, defaultTextures.white),
.normalTex = defaultTextures.normal,
.metallicRoughnessTex = defaultTextures.metallicRoughness,
.emissiveTex = defaultTextures.emissive,
};
device->getMemoryManager().flushAllocation( device->getMemoryManager().flushAllocation(
materialDataBuffer, materialDataBuffer,
static_cast<VkDeviceSize>(id) * sizeof(MaterialData), static_cast<VkDeviceSize>(id) * sizeof(MaterialData),
@@ -148,10 +148,11 @@ std::optional<GPUImage> ImageManager::loadImageFromFile(
VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT |
VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT; VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT;
if ((formatProperties.optimalTilingFeatures & requiredFeatures) != requiredFeatures) { if ((formatProperties.optimalTilingFeatures & requiredFeatures) != requiredFeatures) {
spdlog::error( spdlog::warn(
"Format {} does not support linear mip generation", "Format {} does not support linear mip generation, loading '{}' without mips",
static_cast<int>(data.vkFormat)); static_cast<int>(data.vkFormat),
return std::nullopt; path.string());
mipMap = false;
} }
} }
+54 -30
View File
@@ -9,18 +9,18 @@
#include <algorithm> #include <algorithm>
#include <array> #include <array>
#include <initializer_list>
#include <utility> #include <utility>
namespace { namespace {
const ModelDoc::TextureInfo* FindDiffuseTexture( // Returns the first loadable texture, trying the semantics in order
const ModelDoc::MaterialInfo& material) const ModelDoc::TextureInfo* FindTexture(
const ModelDoc::MaterialInfo& material,
std::initializer_list<ModelDoc::TextureSemantic> semantics)
{ {
for (const auto semantic : std::array{ for (const auto semantic : semantics) {
ModelDoc::TextureSemantic::BaseColor,
ModelDoc::TextureSemantic::Diffuse,
}) {
for (const auto& texture : material.textures) { for (const auto& texture : material.textures) {
if (texture.semantic == semantic) { if (texture.semantic == semantic && !texture.embedded && !texture.path.empty()) {
return &texture; return &texture;
} }
} }
@@ -230,32 +230,56 @@ void RenderResources::loadModelAsset(
continue; continue;
} }
Material material = Material::SimpleColor( addModelMaterial(gfxDevice, sourceMaterial);
glm::vec3{sourceMaterial.baseColor},
sourceMaterial.name);
material.assetReference = sourceMaterial.assetReference;
material.metallicFactor = sourceMaterial.metallicFactor;
material.roughnessFactor = sourceMaterial.roughnessFactor;
material.emissiveFactor = std::max({
sourceMaterial.emissiveColor.x,
sourceMaterial.emissiveColor.y,
sourceMaterial.emissiveColor.z,
});
if (const auto* texture = FindDiffuseTexture(sourceMaterial);
texture != nullptr && !texture->embedded && !texture->path.empty()) {
material.diffuseTexture = loadImageFromFile(
gfxDevice,
std::filesystem::path{texture->path},
VK_IMAGE_USAGE_SAMPLED_BIT,
false,
TextureIntent::ColorSrgb);
}
materials().addMaterial(std::move(material));
} }
} }
MaterialID RenderResources::addModelMaterial(
GfxDevice& gfxDevice,
const ModelDoc::MaterialInfo& sourceMaterial)
{
using Semantic = ModelDoc::TextureSemantic;
const auto loadTexture = [&](std::initializer_list<Semantic> semantics, TextureIntent intent)
{
const auto* texture = FindTexture(sourceMaterial, semantics);
if (texture == nullptr) {
return NULL_IMAGE_ID;
}
return loadImageFromFile(
gfxDevice,
std::filesystem::path{texture->path},
VK_IMAGE_USAGE_SAMPLED_BIT,
true,
intent);
};
Material material = Material::SimpleColor(
glm::vec3{sourceMaterial.baseColor},
sourceMaterial.name);
material.assetReference = sourceMaterial.assetReference;
material.metallicFactor = sourceMaterial.metallicFactor;
material.roughnessFactor = sourceMaterial.roughnessFactor;
material.emissiveColor = sourceMaterial.emissiveColor;
material.diffuseTexture = loadTexture({Semantic::BaseColor, Semantic::Diffuse}, TextureIntent::ColorSrgb);
material.normalTexture = loadTexture({Semantic::Normal}, TextureIntent::DataLinear);
material.metallicTexture = loadTexture({Semantic::MetallicRoughness, Semantic::Metallic}, TextureIntent::DataLinear);
material.roughnessTexture = loadTexture({Semantic::MetallicRoughness, Semantic::Roughness}, TextureIntent::DataLinear);
material.occlusionTexture = loadTexture({Semantic::AmbientOcclusion}, TextureIntent::DataLinear);
material.emissiveTexture = loadTexture({Semantic::Emissive}, TextureIntent::ColorSrgb);
// Formats without PBR factors (FBX, OBJ) leave them at 0, which would hide the map completely
if (material.metallicTexture != NULL_IMAGE_ID && material.metallicFactor == 0.0f) {
material.metallicFactor = 1.0f;
}
if (material.emissiveTexture != NULL_IMAGE_ID && material.emissiveColor == glm::vec3{0.0f}) {
material.emissiveColor = glm::vec3{1.0f};
}
return materials().addMaterial(std::move(material));
}
BindlessSetManager& RenderResources::getBindlessSetManager() { BindlessSetManager& RenderResources::getBindlessSetManager() {
return imageCache->bindlessSetManager; return imageCache->bindlessSetManager;
} }
+2 -2
View File
@@ -223,7 +223,7 @@ void ResourcesInspector::drawImageDetails(ImageID id, const RenderResources& res
std::vector<std::size_t> users; std::vector<std::size_t> users;
for (std::size_t materialId = 0; materialId < materials.materials.size(); ++materialId) for (std::size_t materialId = 0; materialId < materials.materials.size(); ++materialId)
{ {
if (materials.materials[materialId].diffuseTexture == id) if (materials.materials[materialId].UsesTexture(id))
{ {
users.push_back(materialId); users.push_back(materialId);
} }
@@ -232,7 +232,7 @@ void ResourcesInspector::drawImageDetails(ImageID id, const RenderResources& res
if (!users.empty()) if (!users.empty())
{ {
ImGui::Separator(); ImGui::Separator();
ImGui::Text("Diffuse texture of %zu material(s):", users.size()); ImGui::Text("Used by %zu material(s):", users.size());
constexpr std::size_t maxListed = 8; constexpr std::size_t maxListed = 8;
for (std::size_t i = 0; i < std::min(users.size(), maxListed); ++i) for (std::size_t i = 0; i < std::min(users.size(), maxListed); ++i)
+30
View File
@@ -0,0 +1,30 @@
[Window][Debug##Default]
Pos=60,60
Size=400,400
Collapsed=0
LastUsed=20261013
[Window][Test]
Pos=60,60
Size=94,54
Collapsed=0
LastUsed=20261013
[Window][Debug]
Pos=60,60
Size=107,48
Collapsed=0
LastUsed=20261013
[Window][Physics]
Pos=60,60
Size=215,201
Collapsed=0
LastUsed=20261013
[Window][Inspector]
Pos=669,99
Size=640,520
Collapsed=0
LastUsed=20261013
+30
View File
@@ -142,6 +142,33 @@ void LightKeeper::customInit()
floorRb->SetType(RigidbodyType::Static); floorRb->SetType(RigidbodyType::Static);
scene.GetPhysics().RegisterGameObject(*planeObj); scene.GetPhysics().RegisterGameObject(*planeObj);
const auto helmetObj = scene.CreateGameObject("FlightHelmet");
helmetObj->GetTransform().SetWorldPosition(glm::vec3(2.f, -1.f, 0.f));
helmetObj->GetTransform().SetWorldScale(glm::vec3(2.f));
const auto helmetModel = AssetManager::GetInstance().LoadModel(
"engine://flight-helmet/FlightHelmet.gltf",
staticModelOptions);
ModelDocUtils::LogModelDocSummary(helmetModel, "FlightHelmet.gltf");
std::vector<MaterialID> helmetMaterialIDs;
for (const auto& material : helmetModel.materials)
{
helmetMaterialIDs.push_back(resources.addModelMaterial(gfxDevice, material));
}
for (const auto& primitive : helmetModel.primitives)
{
const auto partObj = scene.CreateGameObject(primitive.nodeName);
partObj->GetTransform().SetParent(&helmetObj->GetTransform(), false);
const auto partMeshComp = partObj->AddComponent<MeshRendererComponent>();
partMeshComp->SetMeshID(resources.meshes().addMesh(gfxDevice, primitive.mesh));
partMeshComp->SetMaterialID(primitive.materialIndex < helmetMaterialIDs.size()
? helmetMaterialIDs[primitive.materialIndex]
: resources.materials().getPlaceholderMaterialId());
}
const auto CharObj = scene.CreateGameObject("Character"); const auto CharObj = scene.CreateGameObject("Character");
capybara = CharObj; capybara = CharObj;
@@ -504,6 +531,9 @@ void LightKeeper::customInit()
const auto texPath = AssetFS::GetInstance().GetFullPath("engine://textures/kobe.png"); const auto texPath = AssetFS::GetInstance().GetFullPath("engine://textures/kobe.png");
texID = resources.loadImageFromFile(gfxDevice, texPath); texID = resources.loadImageFromFile(gfxDevice, texPath);
} }
void LightKeeper::customUpdate(float dt) void LightKeeper::customUpdate(float dt)
+1 -1
View File
@@ -859,7 +859,7 @@ namespace {
"simple materials must preserve their color"); "simple materials must preserve their color");
check(material.metallicFactor == 0.0f && check(material.metallicFactor == 0.0f &&
material.roughnessFactor == 1.0f && material.roughnessFactor == 1.0f &&
material.emissiveFactor == 0.0f, material.emissiveColor == glm::vec3{0.0f},
"simple materials must use non-metallic default factors"); "simple materials must use non-metallic default factors");
check(material.diffuseTexture == NULL_IMAGE_ID, check(material.diffuseTexture == NULL_IMAGE_ID,
"simple materials must not require a diffuse image"); "simple materials must not require a diffuse image");