Skip to content
Open
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 17 additions & 4 deletions packages/dev/core/src/Materials/effect.ts
Original file line number Diff line number Diff line change
Expand Up @@ -391,9 +391,18 @@ export class Effect implements IDisposable {

this.uniqueId = Effect._UniqueIdSeed++;
if (!cachedPipeline) {
// Floating promise - should be checked here.
// eslint-disable-next-line @typescript-eslint/no-floating-promises
this._processShaderCodeAsync(null, false, null, extraInitializationsAsync);
void (async () => {
try {
await this._processShaderCodeAsync(null, false, null, extraInitializationsAsync);
} catch (error) {
const message = error?.message ?? String(error);
const asyncError = new Error(`Effect async shader preparation failed for "${String(this.name)}": ${message}`);
if (error && typeof error.stack === "string") {
asyncError.stack = `${asyncError.message}\nCaused by: ${error.stack}`;
}
this._processCompilationErrors(asyncError);
}
})();
} else {
this._pipelineContext = cachedPipeline;
this._pipelineContext.setEngine(this._engine);
Expand Down Expand Up @@ -707,6 +716,10 @@ export class Effect implements IDisposable {
return this._rawFragmentSourceCode;
}

/**
* Gets the pipeline generation options for this effect.
* @returns the pipeline generation options for this effect
*/
public getPipelineGenerationOptions(): IPipelineGenerationOptions {
return {
platformName: this._engine.shaderPlatformName,
Expand Down Expand Up @@ -878,7 +891,7 @@ export class Effect implements IDisposable {
}

private _processCompilationErrors(e: any, previousPipelineContext: Nullable<IPipelineContext> = null) {
this._compilationError = e.message;
this._compilationError = typeof e?.stack === "string" ? e.stack : (e?.message ?? String(e));
const attributesNames = this._attributesNames;
const fallbacks = this._fallbacks;

Expand Down
28 changes: 15 additions & 13 deletions packages/dev/core/src/Materials/meshDebugPluginMaterial.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ flat varying float dbg_vPass;
const vertexDefinitionsWebGPU = `#if defined(DBG_ENABLED)
attribute dbg_initialPass: f32;
varying dbg_vBarycentric: vec3f;
varying dbg_vVertexWorldPos: vec3f;
varying dbg_vPass: f32;
flat varying dbg_vVertexWorldPos: vec3f;
flat varying dbg_vPass: f32;
#endif`;

const vertexMainEnd = `#if defined(DBG_ENABLED)
Expand Down Expand Up @@ -150,8 +150,8 @@ flat varying float dbg_vPass;

const fragmentDefinitionsWebGPU = `#if defined(DBG_ENABLED)
varying dbg_vBarycentric: vec3f;
varying dbg_vVertexWorldPos: vec3f;
varying dbg_vPass: f32;
flat varying dbg_vVertexWorldPos: vec3f;
flat varying dbg_vPass: f32;

#if !defined(DBG_MULTIPLY)
fn dbg_applyShading(color: vec3f) -> vec3f {
Expand All @@ -177,10 +177,10 @@ varying dbg_vPass: f32;

#if DBG_MODE == 2 || DBG_MODE == 3
fn dbg_cornerFactor() -> f32 {
var worldPos = fragmentInputs.vPositionW;
float dist = length(worldPos - fragmentInputs.dbg_vVertexWorldPos);
float camDist = length(worldPos - scene.vEyePosition.xyz);
float d = sqrt(camDist) * .001;
let worldPos = fragmentInputs.vPositionW;
let dist = length(worldPos - fragmentInputs.dbg_vVertexWorldPos);
let camDist = length(worldPos - scene.vEyePosition.xyz);
let d = sqrt(camDist) * .001;
return smoothstep((uniforms.dbg_thicknessRadiusScale.y * d), ((uniforms.dbg_thicknessRadiusScale.y * 1.01) * d), dist);
}
#endif
Expand All @@ -189,7 +189,7 @@ varying dbg_vPass: f32;
fn dbg_checkerboardFactor(uv: vec2f) -> f32 {
var f = fract(uv * uniforms.dbg_thicknessRadiusScale.z);
f -= .5;
return (f.x * f.y) > 0. ? 1. : 0.;
return select(0.0, 1.0, (f.x * f.y) > 0.0);
}
#endif
#endif`;
Expand Down Expand Up @@ -231,9 +231,11 @@ var dbg_color = vec3f(1.);
#if DBG_MODE == 1
dbg_color = mix(uniforms.dbg_wireframeTrianglesColor, vec3f(1.), dbg_edgeFactor());
#elif DBG_MODE == 2 || DBG_MODE == 3
var dbg_cornerFactor = dbg_cornerFactor();
if (fragmentInputs.dbg_vPass == 0. && dbg_cornerFactor == 1.) discard;
dbg_color = mix(uniforms.dbg_vertexColor, vec3(1.), dbg_cornerFactor);
let dbg_cornerFactor = dbg_cornerFactor();
if (fragmentInputs.dbg_vPass == 0.0 && dbg_cornerFactor == 1.0) {
discard;
}
dbg_color = mix(uniforms.dbg_vertexColor, vec3f(1.), dbg_cornerFactor);
#if DBG_MODE == 3
dbg_color *= mix(uniforms.dbg_wireframeVerticesColor, vec3f(1.), dbg_edgeFactor());
#endif
Expand All @@ -251,7 +253,7 @@ var dbg_color = vec3f(1.);
fragmentOutputs.color *= vec4f(dbg_color, 1.);
#else
#if DBG_MODE != 6
fragmentOutputs.color = vec4f(dbg_applyShading(dbg_shadedDiffuseColor) * dbg_color, 1.);
fragmentOutputs.color = vec4f(dbg_applyShading(uniforms.dbg_shadedDiffuseColor) * dbg_color, 1.);
#else
fragmentOutputs.color = vec4f(dbg_color, 1.);
#endif
Expand Down
29 changes: 27 additions & 2 deletions packages/dev/core/src/Particles/gpuParticleSystem.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import { Attractor } from "./attractor";
import { Logger } from "../Misc/logger";
import { BoxParticleEmitter } from "../Particles/EmitterTypes/boxParticleEmitter";
import { type IDisposable, Scene } from "../scene";
import { type Effect } from "../Materials/effect";
import { type Effect, type IEffectCreationOptions } from "../Materials/effect";
import { ImageProcessingConfiguration } from "../Materials/imageProcessingConfiguration";
import { RawTexture } from "../Materials/Textures/rawTexture";
import { Constants } from "../Engines/constants";
Expand All @@ -24,6 +24,7 @@ import { CustomParticleEmitter } from "./EmitterTypes/customParticleEmitter";
import { AbstractEngine } from "../Engines/abstractEngine";
import { type DataBuffer } from "../Buffers/dataBuffer";
import { DrawWrapper } from "../Materials/drawWrapper";
import { ShaderLanguage } from "../Materials/shaderLanguage";
import { type UniformBufferEffectCommonAccessor } from "../Materials/uniformBufferEffectCommonAccessor";
import { type IGPUParticleSystemPlatform } from "./IGPUParticleSystemPlatform";
import { GetClass } from "../Misc/typeStore";
Expand Down Expand Up @@ -96,6 +97,7 @@ export class GPUParticleSystem extends BaseParticleSystem implements IDisposable
private _actualFrame = 0;
private _drawWrappers: { [blendMode: number]: DrawWrapper };
private _customWrappers: { [blendMode: number]: Nullable<DrawWrapper> };
private _renderShadersLoaded = false;

private readonly _rawTextureWidth = 256;

Expand Down Expand Up @@ -1497,7 +1499,30 @@ export class GPUParticleSystem extends BaseParticleSystem implements IDisposable

this.fillUniformsAttributesAndSamplerNames(uniforms, attributes, samplers);

drawWrapper.setEffect(this._engine.createEffect("gpuRenderParticles", attributes, uniforms, samplers, join), join);
const shaderLanguage = this._engine.isWebGPU ? ShaderLanguage.WGSL : ShaderLanguage.GLSL;
drawWrapper.setEffect(
this._engine.createEffect(
"gpuRenderParticles",
<IEffectCreationOptions>{
attributes,
uniformsNames: uniforms,
samplers,
defines: join,
shaderLanguage,
extraInitializationsAsync: this._renderShadersLoaded
? undefined
: async () => {
if (shaderLanguage === ShaderLanguage.WGSL) {
await Promise.all([import("../ShadersWGSL/gpuRenderParticles.vertex"), import("../ShadersWGSL/gpuRenderParticles.fragment")]);
}

this._renderShadersLoaded = true;
},
},
this._engine
),
join
);
}

return drawWrapper;
Expand Down
39 changes: 39 additions & 0 deletions packages/dev/core/src/ShadersWGSL/gpuRenderParticles.fragment.fx
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
var diffuseSamplerSampler: sampler;
var diffuseSampler: texture_2d<f32>;

varying vUV: vec2f;
varying vColor: vec4f;

#include<clipPlaneFragmentDeclaration>
#include<imageProcessingDeclaration>
#include<logDepthDeclaration>
#include<helperFunctions>
#include<imageProcessingFunctions>
#include<fogFragmentDeclaration>

@fragment
fn main(input: FragmentInputs) -> FragmentOutputs {
#include<clipPlaneFragment>

let textureColor: vec4f = textureSample(diffuseSampler, diffuseSamplerSampler, input.vUV);
var baseColor: vec4f = textureColor * input.vColor;

#ifdef BLENDMULTIPLYMODE
let alpha: f32 = input.vColor.a * textureColor.a;
baseColor = vec4f(baseColor.rgb * alpha + vec3f(1.0) * (1.0 - alpha), baseColor.a);
#endif

#include<logDepthFragment>
#include<fogFragment>(color,baseColor)

#ifdef IMAGEPROCESSINGPOSTPROCESS
baseColor = vec4f(toLinearSpaceVec3(baseColor.rgb), baseColor.a);
#else
#ifdef IMAGEPROCESSING
baseColor = vec4f(toLinearSpaceVec3(baseColor.rgb), baseColor.a);
baseColor = applyImageProcessing(baseColor);
#endif
#endif

fragmentOutputs.color = baseColor;
}
189 changes: 189 additions & 0 deletions packages/dev/core/src/ShadersWGSL/gpuRenderParticles.vertex.fx
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
uniform view: mat4x4f;
uniform projection: mat4x4f;
uniform translationPivot: vec2f;
uniform worldOffset: vec3f;
#ifdef LOCAL
uniform emitterWM: mat4x4f;
#endif

// Particles state
attribute position: vec3f;
attribute age: f32;
attribute life: f32;
attribute size: vec3f;
#if !defined(BILLBOARD) || defined(BILLBOARDSTRETCHED_LOCAL)
attribute initialDirection: vec3f;
#endif
#ifdef BILLBOARDSTRETCHED
attribute direction: vec3f;
#endif
attribute angle: f32;
#ifdef ANIMATESHEET
attribute cellIndex: f32;
#endif
attribute offset: vec2f;
attribute uv: vec2f;

varying vUV: vec2f;
varying vColor: vec4f;
varying vPositionW: vec3f;

#if defined(BILLBOARD) && !defined(BILLBOARDY) && !defined(BILLBOARDSTRETCHED)
uniform invView: mat4x4f;
#endif

#include<clipPlaneVertexDeclaration>
#include<fogVertexDeclaration>
#include<logDepthDeclaration>

#ifdef COLORGRADIENTS
var colorGradientSamplerSampler: sampler;
var colorGradientSampler: texture_2d<f32>;
#ifdef COLORGRADIENTS_COLOR2
attribute seed: vec4f;
#endif
#else
uniform colorDead: vec4f;
attribute color: vec4f;
#endif

#ifdef ANIMATESHEET
uniform sheetInfos: vec3f;
#endif

#ifdef BILLBOARD
uniform eyePosition: vec3f;
#endif

fn particleBasePosition() -> vec3f {
#ifdef LOCAL
return (uniforms.emitterWM * vec4f(vertexInputs.position, 1.0)).xyz + uniforms.worldOffset;
#else
return vertexInputs.position + uniforms.worldOffset;
#endif
}

fn rotate(yaxis: vec3f, rotatedCorner: vec3f) -> vec3f {
let xaxis: vec3f = normalize(cross(vec3f(0.0, 1.0, 0.0), yaxis));
let zaxis: vec3f = normalize(cross(yaxis, xaxis));
let rotMatrix: mat3x3f = mat3x3f(xaxis, yaxis, zaxis);
return particleBasePosition() + rotMatrix * rotatedCorner;
}

#ifdef BILLBOARDSTRETCHED
fn rotateAlign(toCamera: vec3f, rotatedCorner: vec3f) -> vec3f {
let normalizedToCamera: vec3f = normalize(toCamera);
#ifdef BILLBOARDSTRETCHED_LOCAL
let normalizedCrossDirToCamera: vec3f = normalize(cross(normalize(vertexInputs.initialDirection), normalizedToCamera));
let row1: vec3f = normalize(vertexInputs.initialDirection);
#else
let normalizedCrossDirToCamera: vec3f = normalize(cross(normalize(vertexInputs.direction), normalizedToCamera));
let row1: vec3f = normalize(cross(normalizedToCamera, normalizedCrossDirToCamera));
#endif

let rotMatrix: mat3x3f = mat3x3f(normalizedCrossDirToCamera, row1, normalizedToCamera);
return particleBasePosition() + rotMatrix * rotatedCorner;
}
#endif

@vertex
fn main(input: VertexInputs) -> FragmentInputs {
#ifdef EMITRATECTRL
let shouldCullParticle: bool = vertexInputs.life > 0.0 && vertexInputs.age >= vertexInputs.life;
#endif

#ifdef ANIMATESHEET
let rowOffset: f32 = floor(vertexInputs.cellIndex / uniforms.sheetInfos.z);
let columnOffset: f32 = vertexInputs.cellIndex - rowOffset * uniforms.sheetInfos.z;
let uvScale: vec2f = uniforms.sheetInfos.xy;
let uvOffset: vec2f = vec2f(vertexInputs.uv.x, 1.0 - vertexInputs.uv.y);
vertexOutputs.vUV = (uvOffset + vec2f(columnOffset, rowOffset)) * uvScale;
#else
vertexOutputs.vUV = vertexInputs.uv;
#endif

let ratio: f32 = min(1.0, vertexInputs.age / vertexInputs.life);
#ifdef COLORGRADIENTS
#ifdef COLORGRADIENTS_COLOR2
let vColor1: vec4f = textureSampleLevel(colorGradientSampler, colorGradientSamplerSampler, vec2f(ratio, 0.25), 0.0);
let vColor2: vec4f = textureSampleLevel(colorGradientSampler, colorGradientSamplerSampler, vec2f(ratio, 0.75), 0.0);
vertexOutputs.vColor = mix(vColor1, vColor2, vertexInputs.seed.x);
#else
vertexOutputs.vColor = textureSampleLevel(colorGradientSampler, colorGradientSamplerSampler, vec2f(ratio, 0.0), 0.0);
#endif
#else
vertexOutputs.vColor = vertexInputs.color * vec4f(1.0 - ratio) + uniforms.colorDead * vec4f(ratio);
#endif

let cornerPos: vec2f = (vertexInputs.offset - uniforms.translationPivot) * vertexInputs.size.yz * vertexInputs.size.x;

#ifdef BILLBOARD
var rotatedCorner: vec4f;
rotatedCorner.w = 0.0;

#ifdef BILLBOARDY
rotatedCorner.x = cornerPos.x * cos(vertexInputs.angle) - cornerPos.y * sin(vertexInputs.angle);
rotatedCorner.z = cornerPos.x * sin(vertexInputs.angle) + cornerPos.y * cos(vertexInputs.angle);
rotatedCorner.y = 0.0;
rotatedCorner.x += uniforms.translationPivot.x;
rotatedCorner.z += uniforms.translationPivot.y;

var yaxis: vec3f = vertexInputs.position + uniforms.worldOffset - uniforms.eyePosition;
yaxis.y = 0.0;
vertexOutputs.vPositionW = rotate(normalize(yaxis), rotatedCorner.xyz);

let viewPosition: vec4f = uniforms.view * vec4f(vertexOutputs.vPositionW, 1.0);
#elif defined(BILLBOARDSTRETCHED)
rotatedCorner.x = cornerPos.x * cos(vertexInputs.angle) - cornerPos.y * sin(vertexInputs.angle);
rotatedCorner.y = cornerPos.x * sin(vertexInputs.angle) + cornerPos.y * cos(vertexInputs.angle);
rotatedCorner.z = 0.0;
rotatedCorner.x += uniforms.translationPivot.x;
rotatedCorner.y += uniforms.translationPivot.y;

let toCamera: vec3f = vertexInputs.position + uniforms.worldOffset - uniforms.eyePosition;
vertexOutputs.vPositionW = rotateAlign(toCamera, rotatedCorner.xyz);

let viewPosition: vec4f = uniforms.view * vec4f(vertexOutputs.vPositionW, 1.0);
#else
rotatedCorner.x = cornerPos.x * cos(vertexInputs.angle) - cornerPos.y * sin(vertexInputs.angle);
rotatedCorner.y = cornerPos.x * sin(vertexInputs.angle) + cornerPos.y * cos(vertexInputs.angle);
rotatedCorner.z = 0.0;
rotatedCorner.x += uniforms.translationPivot.x;
rotatedCorner.y += uniforms.translationPivot.y;

let viewPosition: vec4f = uniforms.view * vec4f(particleBasePosition(), 1.0) + rotatedCorner;
vertexOutputs.vPositionW = (uniforms.invView * viewPosition).xyz;
#endif

#else
var rotatedCorner: vec3f;
rotatedCorner.x = cornerPos.x * cos(vertexInputs.angle) - cornerPos.y * sin(vertexInputs.angle);
rotatedCorner.y = 0.0;
rotatedCorner.z = cornerPos.x * sin(vertexInputs.angle) + cornerPos.y * cos(vertexInputs.angle);
rotatedCorner.x += uniforms.translationPivot.x;
rotatedCorner.z += uniforms.translationPivot.y;

let yaxis: vec3f = normalize(vertexInputs.initialDirection);
vertexOutputs.vPositionW = rotate(yaxis, rotatedCorner);

let viewPosition: vec4f = uniforms.view * vec4f(vertexOutputs.vPositionW, 1.0);
#endif

vertexOutputs.position = uniforms.projection * viewPosition;

#if defined(CLIPPLANE) || defined(CLIPPLANE2) || defined(CLIPPLANE3) || defined(CLIPPLANE4) || defined(CLIPPLANE5) || defined(CLIPPLANE6) || defined(FOG)
let worldPos: vec4f = vec4f(vertexOutputs.vPositionW, 1.0);
#endif
#include<clipPlaneVertex>
#include<fogVertex>
#include<logDepthVertex>

#ifdef EMITRATECTRL
if (shouldCullParticle) {
vertexOutputs.position = vec4f(0.0, 0.0, 2.0, 1.0);
vertexOutputs.vColor = vec4f(0.0);
vertexOutputs.vUV = vec2f(0.0);
vertexOutputs.vPositionW = vec3f(0.0);
}
#endif
}
Loading
Loading