Skip to content

Commit

Permalink
Merge pull request #79 from martinlaxenaire/develop
Browse files Browse the repository at this point in the history
Minor bugs fixes and improvements - v0.7.5
  • Loading branch information
martinlaxenaire authored Jun 21, 2024
2 parents d140a48 + e7c2ae0 commit 880c7e1
Show file tree
Hide file tree
Showing 459 changed files with 6,736 additions and 4,548 deletions.
2 changes: 1 addition & 1 deletion dist/esm/core/bindings/utils.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ const getTextureBindingWGSLVarType = (binding) => {
if (binding.bindingType === "externalTexture") {
return `var ${binding.name}: texture_external;`;
}
return binding.bindingType === "storage" ? `var ${binding.name}: texture_storage_${binding.options.viewDimension}<${binding.options.format}, ${binding.options.access}>;` : binding.bindingType === "depth" ? `var ${binding.name}: texture_depth${binding.options.multisampled ? "_multisampled" : ""}_${binding.options.viewDimension};` : `var ${binding.name}: texture${binding.options.multisampled ? "_multisampled" : ""}_${binding.options.viewDimension}<f32>;`;
return binding.bindingType === "storage" ? `var ${binding.name}: texture_storage_${binding.options.viewDimension.replace("-", "_")}<${binding.options.format}, ${binding.options.access}>;` : binding.bindingType === "depth" ? `var ${binding.name}: texture_depth${binding.options.multisampled ? "_multisampled" : ""}_${binding.options.viewDimension.replace("-", "_")};` : `var ${binding.name}: texture${binding.options.multisampled ? "_multisampled" : ""}_${binding.options.viewDimension.replace("-", "_")}<f32>;`;
};
const getBindGroupLayoutBindingType = (binding) => {
if (binding.bindingType === "storage" && binding.options.access === "read_write") {
Expand Down
2 changes: 2 additions & 0 deletions dist/esm/core/materials/Material.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -373,6 +373,8 @@ class Material {
destroyTexture(texture) {
if (texture.options.cache)
return;
if (!texture.options.autoDestroy)
return;
const objectsUsingTexture = this.renderer.getObjectsByTexture(texture);
const shouldDestroy = !objectsUsingTexture || !objectsUsingTexture.some((object) => object.material.uuid !== this.uuid);
if (shouldDestroy) {
Expand Down
29 changes: 28 additions & 1 deletion dist/esm/core/textures/Texture.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@ const defaultTextureParams = {
// copy external texture options
generateMips: false,
flipY: false,
premultipliedAlpha: false
premultipliedAlpha: false,
autoDestroy: true
};
class Texture {
/**
Expand Down Expand Up @@ -151,6 +152,32 @@ class Texture {
generateMips(this.renderer.device, this.texture);
}
}
/**
* Use data as the {@link texture} source and upload it to the GPU.
* @param parameters - parameters used to upload the source.
* @param parameters.width - data source width.
* @param parameters.height - data source height.
* @param parameters.depth - data source depth.
* @param parameters.origin - {@link GPUOrigin3D | origin} of the data source copy.
* @param parameters.data - {@link Float32Array} data to use as source.
*/
uploadData({
width = this.size.width,
height = this.size.height,
depth = this.size.depth,
origin = [0, 0, 0],
data = new Float32Array(width * height * 4)
}) {
this.renderer.device.queue.writeTexture(
{ texture: this.texture, origin },
data,
{ bytesPerRow: width * data.BYTES_PER_ELEMENT * 4, rowsPerImage: height },
[width, height, depth]
);
if (this.texture.mipLevelCount > 1) {
generateMips(this.renderer.device, this.texture);
}
}
/**
* Set our {@link Texture#bindings | bindings}
*/
Expand Down
6 changes: 6 additions & 0 deletions dist/esm/extras/gltf/GLTFScenesManager.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -664,6 +664,12 @@ const _GLTFScenesManager = class _GLTFScenesManager {
mesh.storages.instances.modelMatrix.shouldUpdate = true;
mesh.storages.instances.normalMatrix.shouldUpdate = true;
};
this.renderer.onAfterRenderScene.add(
() => {
mesh.shouldUpdateModelMatrix();
},
{ once: true }
);
}
mesh.parent = meshDescriptor.parent;
this.scenesManager.meshes.push(mesh);
Expand Down
255 changes: 249 additions & 6 deletions dist/esm/extras/gltf/utils.mjs
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
import { Texture } from '../../core/textures/Texture.mjs';
import { Sampler } from '../../core/samplers/Sampler.mjs';
import { ComputePass } from '../../core/computePasses/ComputePass.mjs';
import { throwWarning } from '../../utils/utils.mjs';

const buildShaders = (meshDescriptor, shaderParameters = null) => {
const baseColorTexture = meshDescriptor.textures.find((t) => t.texture === "baseColorTexture");
const normalTexture = meshDescriptor.textures.find((t) => t.texture === "normalTexture");
Expand Down Expand Up @@ -239,7 +244,7 @@ const buildShaders = (meshDescriptor, shaderParameters = null) => {
/* wgsl */
`
lightContribution.ambient *= color.rgb * occlusion;
lightContribution.diffuse *= occlusion;
lightContribution.diffuse *= color.rgb * occlusion;
lightContribution.specular *= occlusion;
color = vec4(
Expand Down Expand Up @@ -440,7 +445,7 @@ const buildIBLShaders = (meshDescriptor, shaderParameters = null) => {
specular: vec3f,
};
fn getIBLContribution(NdotV: f32, roughness: f32, n: vec3f, reflection: vec3f, diffuseColor: vec3f, f0: vec3f) -> IBLContribution {
fn getIBLContribution(NdotV: f32, roughness: f32, normal: vec3f, reflection: vec3f, diffuseColor: vec3f, f0: vec3f) -> IBLContribution {
var iblContribution: IBLContribution;
let brdfSamplePoint: vec2f = clamp(vec2(NdotV, roughness), vec2(0.0), vec2(1.0));
Expand Down Expand Up @@ -471,13 +476,16 @@ const buildIBLShaders = (meshDescriptor, shaderParameters = null) => {
let diffuseLight: vec4f = textureSample(
${envDiffuseTexture.texture.options.name},
${envDiffuseTexture.samplerName},
${envDiffuseTexture.texture.options.viewDimension === "cube" ? "n" : "cartesianToPolar(n)"}
${envDiffuseTexture.texture.options.viewDimension === "cube" ? "normal" : "cartesianToPolar(normal)"}
);
// product of specularFactor and specularTexture.a
let specularWeight: f32 = 1.0;
FssEss = ibl.specularStrength * k_S * brdf.x + brdf.y;
FssEss = specularWeight * k_S * brdf.x + brdf.y;
let Ems: f32 = (1.0 - (brdf.x + brdf.y));
let F_avg: vec3f = ibl.specularStrength * (f0 + (1.0 - f0) / 21.0);
let F_avg: vec3f = specularWeight * (f0 + (1.0 - f0) / 21.0);
let FmsEms: vec3f = Ems * FssEss * F_avg / (1.0 - F_avg * Ems);
let k_D: vec3f = diffuseColor * (1.0 - FssEss + FmsEms);
Expand Down Expand Up @@ -522,5 +530,240 @@ const buildIBLShaders = (meshDescriptor, shaderParameters = null) => {
shaderParameters.chunks = chunks;
return buildPBRShaders(meshDescriptor, shaderParameters);
};
const computeDiffuseFromSpecular = async (renderer, diffuseTexture, specularTexture) => {
if (specularTexture.options.viewDimension !== "cube") {
throwWarning(
"Could not compute the diffuse texture because the specular texture is not a cube map:" + specularTexture.options.viewDimension
);
return;
}
const computeDiffuseShader = `
fn radicalInverse_VdC(inputBits: u32) -> f32 {
var bits: u32 = inputBits;
bits = (bits << 16u) | (bits >> 16u);
bits = ((bits & 0x55555555u) << 1u) | ((bits & 0xAAAAAAAAu) >> 1u);
bits = ((bits & 0x33333333u) << 2u) | ((bits & 0xCCCCCCCCu) >> 2u);
bits = ((bits & 0x0F0F0F0Fu) << 4u) | ((bits & 0xF0F0F0F0u) >> 4u);
bits = ((bits & 0x00FF00FFu) << 8u) | ((bits & 0xFF00FF00u) >> 8u);
return f32(bits) * 2.3283064365386963e-10; // / 0x100000000
}
// hammersley2d describes a sequence of points in the 2d unit square [0,1)^2
// that can be used for quasi Monte Carlo integration
fn hammersley2d(i: u32, N: u32) -> vec2f {
return vec2(f32(i) / f32(N), radicalInverse_VdC(i));
}
// TBN generates a tangent bitangent normal coordinate frame from the normal
// (the normal must be normalized)
fn generateTBN(normal: vec3f) -> mat3x3f {
var bitangent: vec3f = vec3(0.0, 1.0, 0.0);
let NdotUp: f32 = dot(normal, vec3(0.0, 1.0, 0.0));
let epsilon: f32 = 0.0000001;
if (1.0 - abs(NdotUp) <= epsilon) {
// Sampling +Y or -Y, so we need a more robust bitangent.
if (NdotUp > 0.0) {
bitangent = vec3(0.0, 0.0, 1.0);
}
else {
bitangent = vec3(0.0, 0.0, -1.0);
}
}
let tangent: vec3f = normalize(cross(bitangent, normal));
bitangent = cross(normal, tangent);
return mat3x3f(tangent, bitangent, normal);
}
// Mipmap Filtered Samples (GPU Gems 3, 20.4)
// https://developer.nvidia.com/gpugems/gpugems3/part-iii-rendering/chapter-20-gpu-based-importance-sampling
// https://cgg.mff.cuni.cz/~jaroslav/papers/2007-sketch-fis/Final_sap_0073.pdf
fn computeLod(pdf: f32) -> f32 {
// https://cgg.mff.cuni.cz/~jaroslav/papers/2007-sketch-fis/Final_sap_0073.pdf
return 0.5 * log2( 6.0 * f32(params.faceSize) * f32(params.faceSize) / (f32(params.sampleCount) * pdf));
}
fn transformDirection(face: u32, uv: vec2f) -> vec3f {
// Transform the direction based on the cubemap face
switch (face) {
case 0u {
// +X
return vec3f( 1.0, uv.y, -uv.x);
}
case 1u {
// -X
return vec3f(-1.0, uv.y, uv.x);
}
case 2u {
// +Y
return vec3f( uv.x, -1.0, uv.y);
}
case 3u {
// -Y
return vec3f( uv.x, 1.0, -uv.y);
}
case 4u {
// +Z
return vec3f( uv.x, uv.y, 1.0);
}
case 5u {
// -Z
return vec3f(-uv.x, uv.y, -1.0);
}
default {
return vec3f(0.0, 0.0, 0.0);
}
}
}
const PI = ${Math.PI};
@compute @workgroup_size(8, 8, 1) fn main(
@builtin(global_invocation_id) GlobalInvocationID: vec3u,
) {
let faceSize: u32 = params.faceSize;
let sampleCount: u32 = params.sampleCount;
let face: u32 = GlobalInvocationID.z;
let x: u32 = GlobalInvocationID.x;
let y: u32 = GlobalInvocationID.y;
if (x >= faceSize || y >= faceSize) {
return;
}
let texelSize: f32 = 1.0 / f32(faceSize);
let halfTexel: f32 = texelSize * 0.5;
var uv: vec2f = vec2(
(f32(x) + halfTexel) * texelSize,
(f32(y) + halfTexel) * texelSize
);
uv = uv * 2.0 - 1.0;
let normal: vec3<f32> = transformDirection(face, uv);
var irradiance: vec3f = vec3f(0.0, 0.0, 0.0);
for (var i: u32 = 0; i < sampleCount; i++) {
// generate a quasi monte carlo point in the unit square [0.1)^2
let xi: vec2f = hammersley2d(i, sampleCount);
let cosTheta: f32 = sqrt(1.0 - xi.y);
let sinTheta: f32 = sqrt(1.0 - cosTheta * cosTheta);
let phi: f32 = 2.0 * PI * xi.x;
let pdf: f32 = cosTheta / PI; // evaluation for solid angle, therefore drop the sinTheta
let sampleVec: vec3f = vec3f(
sinTheta * cos(phi),
sinTheta * sin(phi),
cosTheta
);
let TBN: mat3x3f = generateTBN(normalize(normal));
var direction: vec3f = TBN * sampleVec;
// invert along Y axis
direction.y *= -1.0;
let lod: f32 = computeLod(pdf);
// Convert sampleVec to texture coordinates of the specular env map
irradiance += textureSampleLevel(
envSpecularTexture,
specularSampler,
direction,
min(lod, f32(params.maxMipLevel))
).rgb;
}
irradiance /= f32(sampleCount);
textureStore(diffuseEnvMap, vec2(x, y), face, vec4f(irradiance, 1.0));
}
`;
let diffuseStorageTexture = new Texture(renderer, {
label: "Diffuse storage cubemap",
name: "diffuseEnvMap",
format: "rgba32float",
visibility: ["compute"],
usage: ["copySrc", "storageBinding"],
type: "storage",
fixedSize: {
width: specularTexture.size.width,
height: specularTexture.size.height,
depth: 6
},
viewDimension: "2d-array"
});
const sampler = new Sampler(renderer, {
label: "Compute diffuse sampler",
name: "specularSampler",
addressModeU: "clamp-to-edge",
addressModeV: "clamp-to-edge",
minFilter: "linear",
magFilter: "linear"
});
let computeDiffusePass = new ComputePass(renderer, {
autoRender: false,
// we're going to render only on demand
dispatchSize: [Math.ceil(specularTexture.size.width / 8), Math.ceil(specularTexture.size.height / 8), 6],
shaders: {
compute: {
code: computeDiffuseShader
}
},
uniforms: {
params: {
struct: {
faceSize: {
type: "u32",
value: specularTexture.size.width
},
maxMipLevel: {
type: "u32",
value: specularTexture.texture.mipLevelCount
},
sampleCount: {
type: "u32",
value: 2048
}
}
}
},
samplers: [sampler],
textures: [specularTexture, diffuseStorageTexture]
});
await computeDiffusePass.material.compileMaterial();
renderer.onBeforeRenderScene.add(
(commandEncoder) => {
renderer.renderSingleComputePass(commandEncoder, computeDiffusePass);
commandEncoder.copyTextureToTexture(
{
texture: diffuseStorageTexture.texture
},
{
texture: diffuseTexture.texture
},
[diffuseTexture.texture.width, diffuseTexture.texture.height, diffuseTexture.texture.depthOrArrayLayers]
);
},
{ once: true }
);
renderer.onAfterCommandEncoderSubmission.add(
() => {
computeDiffusePass.destroy();
diffuseStorageTexture.destroy();
diffuseStorageTexture = null;
computeDiffusePass = null;
},
{ once: true }
);
};

export { buildIBLShaders, buildPBRShaders, buildShaders };
export { buildIBLShaders, buildPBRShaders, buildShaders, computeDiffuseFromSpecular };
Loading

0 comments on commit 880c7e1

Please sign in to comment.