Skip to content

Commit

Permalink
shadertools: PBR module using UBO (#2173)
Browse files Browse the repository at this point in the history
  • Loading branch information
felixpalmer authored Aug 20, 2024
1 parent ffcaf82 commit 0cfb490
Show file tree
Hide file tree
Showing 10 changed files with 215 additions and 259 deletions.
23 changes: 12 additions & 11 deletions examples/tutorials/hello-gltf/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,17 +56,19 @@ export default class AppAnimationLoopTemplate extends AnimationLoopTemplate {
const eye = worldMatrix.transformAsPoint(vantage);
const center = worldMatrix.transformAsPoint(this.center);
const viewMatrix = new Matrix4().lookAt({eye, center});
const u_MVPMatrix = new Matrix4(projectionMatrix)
const modelViewProjectionMatrix = new Matrix4(projectionMatrix)
.multiplyRight(viewMatrix)
.multiplyRight(worldMatrix);
model.setUniforms({
u_Camera: eye,
u_MVPMatrix,
u_ModelMatrix: worldMatrix,
u_NormalMatrix: new Matrix4(worldMatrix).invert().transpose()
});

model.updateModuleSettings({lightSources});
model.shaderInputs.setProps({
lighting: lightSources,
pbrProjection: {
camera: eye,
modelViewProjectionMatrix,
modelMatrix: worldMatrix,
normalMatrix: new Matrix4(worldMatrix).invert().transpose()
}
});
model.draw(renderPass);
});
renderPass.end();
Expand All @@ -77,7 +79,7 @@ export default class AppAnimationLoopTemplate extends AnimationLoopTemplate {
canvas.style.opacity = '0.1';

const gltf = await load(
`https://github.khronos.org/glTF-Sample-Viewer-Release/assets/models/Models/${modelName}/glTF/${modelName}.gltf`,
`https://raw.githubusercontent.com/KhronosGroup/glTF-Sample-Assets/main/Models/${modelName}/glTF/${modelName}.gltf`,
GLTFLoader
);
const processedGLTF = postProcessGLTF(gltf);
Expand Down Expand Up @@ -109,17 +111,16 @@ const lightSources: LightingProps = {
type: 'ambient'
},
directionalLights: [
// @ts-expect-error Remove once npm package updated with new types
{
color: [222, 244, 255],
direction: [1, -0.5, 0.5],
intensity: 10,
position: [0, 0, 0],
type: 'directional'
}
],
pointLights: [
{
attenuation: 0,
color: [255, 222, 222],
position: [3, 10, 0],
intensity: 5,
Expand Down
21 changes: 15 additions & 6 deletions modules/gltf/src/gltf/create-gltf-model.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import {Device, RenderPipelineParameters, log} from '@luma.gl/core';
import {pbr} from '@luma.gl/shadertools';
import {pbrMaterial} from '@luma.gl/shadertools';
import {Geometry, Model, ModelNode, ModelProps} from '@luma.gl/engine';
import {ParsePBRMaterialOptions, parsePBRMaterial} from '../pbr/parse-pbr-material';
import {ShaderModule} from '@luma.gl/shadertools';

// TODO rename attributes to POSITION/NORMAL etc
// See gpu-geometry.ts: getAttributeBuffersFromGeometry()
Expand Down Expand Up @@ -48,7 +49,7 @@ const vs = `
#endif
pbr_setPositionNormalTangentUV(positions, _NORMAL, _TANGENT, _TEXCOORD_0);
gl_Position = u_MVPMatrix * positions;
gl_Position = pbrProjection.modelViewProjectionMatrix * positions;
}
`;

Expand Down Expand Up @@ -100,18 +101,26 @@ export function createGLTFModel(device: Device, options: CreateGLTFModelOptions)
geometry,
topology: geometry.topology,
vertexCount,
modules: [pbr],
modules: [pbrMaterial as unknown as ShaderModule],
vs: addVersionToShader(device, vs),
fs: addVersionToShader(device, fs),
// TODO can this be removed? Does deck need it?
...modelOptions,

bindings: {...parsedMaterial.bindings, ...modelOptions.bindings},
defines: {...parsedMaterial.defines, ...modelOptions.defines},
parameters: {...parameters, ...parsedMaterial.parameters, ...modelOptions.parameters},
uniforms: {...parsedMaterial.uniforms, ...modelOptions.uniforms}
parameters: {...parameters, ...parsedMaterial.parameters, ...modelOptions.parameters}
};

const model = new Model(device, modelProps);

const {camera, ...pbrMaterialProps} = {
...parsedMaterial.uniforms,
...modelOptions.uniforms,
...parsedMaterial.bindings,
...modelOptions.bindings
};

model.shaderInputs.setProps({pbrMaterial: pbrMaterialProps, pbrProjection: {camera}});
return new ModelNode({managedResources, model});
}

Expand Down
94 changes: 51 additions & 43 deletions modules/gltf/src/pbr/parse-pbr-material.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type {Device, Texture, Binding, Parameters} from '@luma.gl/core';
import type {Device, Texture, Parameters} from '@luma.gl/core';
import {log} from '@luma.gl/core';
import {PBREnvironment} from './pbr-environment';
import {PBRMaterialBindings, PBRMaterialUniforms, PBRProjectionProps} from '@luma.gl/shadertools';

/* eslint-disable camelcase */

Expand All @@ -17,8 +18,8 @@ export type ParsePBRMaterialOptions = {

export type ParsedPBRMaterial = {
readonly defines: Record<string, number | boolean>;
readonly bindings: Record<string, Binding>;
readonly uniforms: Record<string, any>;
readonly bindings: Partial<PBRMaterialBindings>;
readonly uniforms: Partial<PBRProjectionProps & PBRMaterialUniforms>;
readonly parameters: Parameters;
readonly glParameters: Record<string, any>;
/** List of all generated textures, makes it easy to destroy them later */
Expand Down Expand Up @@ -57,9 +58,9 @@ export function parsePBRMaterial(
bindings: {},
uniforms: {
// TODO: find better values?
u_Camera: [0, 0, 0], // Model should override
camera: [0, 0, 0], // Model should override

u_MetallicRoughnessValues: [1, 1] // Default is 1 and 1
metallicRoughnessValues: [1, 1] // Default is 1 and 1
},
parameters: {},
glParameters: {},
Expand All @@ -71,17 +72,18 @@ export function parsePBRMaterial(

const {imageBasedLightingEnvironment} = options;
if (imageBasedLightingEnvironment) {
parsedMaterial.bindings.u_DiffuseEnvSampler = imageBasedLightingEnvironment.diffuseEnvSampler;
parsedMaterial.bindings.u_SpecularEnvSampler = imageBasedLightingEnvironment.specularEnvSampler;
parsedMaterial.bindings.u_brdfLUT = imageBasedLightingEnvironment.brdfLutTexture;
parsedMaterial.uniforms.u_ScaleIBLAmbient = [1, 1];
parsedMaterial.bindings.pbr_diffuseEnvSampler = imageBasedLightingEnvironment.diffuseEnvSampler;
parsedMaterial.bindings.pbr_specularEnvSampler =
imageBasedLightingEnvironment.specularEnvSampler;
parsedMaterial.bindings.pbr_BrdfLUT = imageBasedLightingEnvironment.brdfLutTexture;
parsedMaterial.uniforms.scaleIBLAmbient = [1, 1];
}

if (options?.pbrDebug) {
parsedMaterial.defines.PBR_DEBUG = 1;
// Override final color for reference app visualization of various parameters in the lighting equation.
parsedMaterial.uniforms.u_ScaleDiffBaseMR = [0, 0, 0, 0];
parsedMaterial.uniforms.u_ScaleFGDSpec = [0, 0, 0, 0];
parsedMaterial.uniforms.scaleDiffBaseMR = [0, 0, 0, 0];
parsedMaterial.uniforms.scaleFGDSpec = [0, 0, 0, 0];
}

if (attributes.NORMAL) parsedMaterial.defines.HAS_NORMALS = 1;
Expand All @@ -100,45 +102,51 @@ export function parsePBRMaterial(

/** Parse GLTF material record */
function parseMaterial(device: Device, material, parsedMaterial: ParsedPBRMaterial): void {
parsedMaterial.uniforms.pbr_uUnlit = Boolean(material.unlit);
parsedMaterial.uniforms.unlit = Boolean(material.unlit);

if (material.pbrMetallicRoughness) {
parsePbrMetallicRoughness(device, material.pbrMetallicRoughness, parsedMaterial);
}
if (material.normalTexture) {
addTexture(device, material.normalTexture, 'u_NormalSampler', 'HAS_NORMALMAP', parsedMaterial);
addTexture(
device,
material.normalTexture,
'pbr_normalSampler',
'HAS_NORMALMAP',
parsedMaterial
);

const {scale = 1} = material.normalTexture;
parsedMaterial.uniforms.u_NormalScale = scale;
parsedMaterial.uniforms.normalScale = scale;
}
if (material.occlusionTexture) {
addTexture(
device,
material.occlusionTexture,
'u_OcclusionSampler',
'pbr_occlusionSampler',
'HAS_OCCLUSIONMAP',
parsedMaterial
);

const {strength = 1} = material.occlusionTexture;
parsedMaterial.uniforms.u_OcclusionStrength = strength;
parsedMaterial.uniforms.occlusionStrength = strength;
}
if (material.emissiveTexture) {
addTexture(
device,
material.emissiveTexture,
'u_EmissiveSampler',
'pbr_emissiveSampler',
'HAS_EMISSIVEMAP',
parsedMaterial
);
parsedMaterial.uniforms.u_EmissiveFactor = material.emissiveFactor || [0, 0, 0];
parsedMaterial.uniforms.emissiveFactor = material.emissiveFactor || [0, 0, 0];
}

switch (material.alphaMode) {
case 'MASK':
const {alphaCutoff = 0.5} = material;
parsedMaterial.defines.ALPHA_CUTOFF = 1;
parsedMaterial.uniforms.u_AlphaCutoff = alphaCutoff;
parsedMaterial.uniforms.alphaCutoff = alphaCutoff;
break;
case 'BLEND':
log.warn('glTF BLEND alphaMode might not work well because it requires mesh sorting')();
Expand Down Expand Up @@ -176,24 +184,24 @@ function parsePbrMetallicRoughness(
addTexture(
device,
pbrMetallicRoughness.baseColorTexture,
'u_BaseColorSampler',
'pbr_baseColorSampler',
'HAS_BASECOLORMAP',
parsedMaterial
);
}
parsedMaterial.uniforms.u_BaseColorFactor = pbrMetallicRoughness.baseColorFactor || [1, 1, 1, 1];
parsedMaterial.uniforms.baseColorFactor = pbrMetallicRoughness.baseColorFactor || [1, 1, 1, 1];

if (pbrMetallicRoughness.metallicRoughnessTexture) {
addTexture(
device,
pbrMetallicRoughness.metallicRoughnessTexture,
'u_MetallicRoughnessSampler',
'pbr_metallicRoughnessSampler',
'HAS_METALROUGHNESSMAP',
parsedMaterial
);
}
const {metallicFactor = 1, roughnessFactor = 1} = pbrMetallicRoughness;
parsedMaterial.uniforms.u_MetallicRoughnessValues = [metallicFactor, roughnessFactor];
parsedMaterial.uniforms.metallicRoughnessValues = [metallicFactor, roughnessFactor];
}

/** Create a texture from a glTF texture/sampler/image combo and add it to bindings */
Expand Down Expand Up @@ -268,9 +276,9 @@ export class PBRMaterialParser {
this.uniforms = {
// TODO: find better values?
u_Camera: [0, 0, 0], // Model should override
camera: [0, 0, 0], // Model should override
u_MetallicRoughnessValues: [1, 1] // Default is 1 and 1
metallicRoughnessValues: [1, 1] // Default is 1 and 1
};
this.bindings = {};
Expand All @@ -279,17 +287,17 @@ export class PBRMaterialParser {
this.generatedTextures = [];
if (imageBasedLightingEnvironment) {
this.bindings.u_DiffuseEnvSampler = imageBasedLightingEnvironment.getDiffuseEnvSampler();
this.bindings.u_SpecularEnvSampler = imageBasedLightingEnvironment.getSpecularEnvSampler();
this.bindings.u_brdfLUT = imageBasedLightingEnvironment.getBrdfTexture();
this.uniforms.u_ScaleIBLAmbient = [1, 1];
this.bindings.pbr_diffuseEnvSampler = imageBasedLightingEnvironment.getDiffuseEnvSampler();
this.bindings.pbr_specularEnvSampler = imageBasedLightingEnvironment.getSpecularEnvSampler();
this.bindings.pbr_BrdfLUT = imageBasedLightingEnvironment.getBrdfTexture();
this.uniforms.scaleIBLAmbient = [1, 1];
}
if (pbrDebug) {
// Override final color for reference app visualization
// of various parameters in the lighting equation.
this.uniforms.u_ScaleDiffBaseMR = [0, 0, 0, 0];
this.uniforms.u_ScaleFGDSpec = [0, 0, 0, 0];
this.uniforms.scaleDiffBaseMR = [0, 0, 0, 0];
this.uniforms.scaleFGDSpec = [0, 0, 0, 0];
}
this.defineIfPresent(attributes.NORMAL, 'HAS_NORMALS');
Expand Down Expand Up @@ -321,31 +329,31 @@ export class PBRMaterialParser {
/** Parse GLTF material record *
parseMaterial(material) {
this.uniforms.pbr_uUnlit = Boolean(material.unlit);
this.uniforms.unlit = Boolean(material.unlit);
if (material.pbrMetallicRoughness) {
this.parsePbrMetallicRoughness(material.pbrMetallicRoughness);
}
if (material.normalTexture) {
this.addTexture(material.normalTexture, 'u_NormalSampler', 'HAS_NORMALMAP');
this.addTexture(material.normalTexture, 'pbr_normalSampler', 'HAS_NORMALMAP');
const {scale = 1} = material.normalTexture;
this.uniforms.u_NormalScale = scale;
this.uniforms.normalScale = scale;
}
if (material.occlusionTexture) {
this.addTexture(material.occlusionTexture, 'u_OcclusionSampler', 'HAS_OCCLUSIONMAP');
this.addTexture(material.occlusionTexture, 'pbr_occlusionSampler', 'HAS_OCCLUSIONMAP');
const {strength = 1} = material.occlusionTexture;
this.uniforms.u_OcclusionStrength = strength;
this.uniforms.occlusionStrength = strength;
}
if (material.emissiveTexture) {
this.addTexture(material.emissiveTexture, 'u_EmissiveSampler', 'HAS_EMISSIVEMAP');
this.uniforms.u_EmissiveFactor = material.emissiveFactor || [0, 0, 0];
this.addTexture(material.emissiveTexture, 'pbr_emissiveSampler', 'HAS_EMISSIVEMAP');
this.uniforms.emissiveFactor = material.emissiveFactor || [0, 0, 0];
}
if (material.alphaMode === 'MASK') {
const {alphaCutoff = 0.5} = material;
this.defines.ALPHA_CUTOFF = 1;
this.uniforms.u_AlphaCutoff = alphaCutoff;
this.uniforms.alphaCutoff = alphaCutoff;
} else if (material.alphaMode === 'BLEND') {
log.warn('BLEND alphaMode might not work well because it requires mesh sorting')();
Object.assign(this.parameters, {
Expand All @@ -361,21 +369,21 @@ export class PBRMaterialParser {
if (pbrMetallicRoughness.baseColorTexture) {
this.addTexture(
pbrMetallicRoughness.baseColorTexture,
'u_BaseColorSampler',
'pbr_baseColorSampler',
'HAS_BASECOLORMAP'
);
}
this.uniforms.u_BaseColorFactor = pbrMetallicRoughness.baseColorFactor || [1, 1, 1, 1];
this.uniforms.baseColorFactor = pbrMetallicRoughness.baseColorFactor || [1, 1, 1, 1];
if (pbrMetallicRoughness.metallicRoughnessTexture) {
this.addTexture(
pbrMetallicRoughness.metallicRoughnessTexture,
'u_MetallicRoughnessSampler',
'pbr_metallicRoughnessSampler',
'HAS_METALROUGHNESSMAP'
);
}
const {metallicFactor = 1, roughnessFactor = 1} = pbrMetallicRoughness;
this.uniforms.u_MetallicRoughnessValues = [metallicFactor, roughnessFactor];
this.uniforms.metallicRoughnessValues = [metallicFactor, roughnessFactor];
}
/** Create a texture from a glTF texture/sampler/image combo and add it to bindings *
Expand Down
8 changes: 7 additions & 1 deletion modules/shadertools/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,13 @@ export type {GouraudMaterialProps} from './modules/lighting/gouraud-material/gou
export {gouraudMaterial} from './modules/lighting/gouraud-material/gouraud-material';
export type {PhongMaterialProps} from './modules/lighting/phong-material/phong-material';
export {phongMaterial} from './modules/lighting/phong-material/phong-material';
// export type {PBRMaterialSettings, PBRMaterialUniforms} from './modules/lighting/pbr-material/pbr';
export type {
PBRMaterialBindings,
PBRMaterialProps,
PBRMaterialUniforms
} from './modules/lighting/pbr-material/pbr-material';
export type {PBRProjectionProps} from './modules/lighting/pbr-material/pbr-projection';

export {pbrMaterial} from './modules/lighting/pbr-material/pbr-material';

// POST PROCESSING / SHADER PASS MODULES
Expand Down
Loading

0 comments on commit 0cfb490

Please sign in to comment.