Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
14 changes: 13 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,21 @@ pubspec_overrides.yaml
/pubspec.lock
*.iml
**/*/*.filamat
!examples/assets/force_core.filamat
!examples/assets/hologram_projector.filamat
!examples/assets/fire_ground.filamat
!examples/assets/wetness.filamat
!examples/assets/crystal_ice.filamat
!examples/assets/snow_accumulation.filamat
!examples/assets/damage_decals.filamat
!examples/assets/portal_rift.filamat
!examples/assets/electricity.filamat
!examples/assets/invisibility_cloak.filamat
!examples/assets/energy_weapon.filamat
**/*/*.g.dart
**/*.BAK
.wrangler
node_modules
.claude
.tickets
.tickets
examples/dart/headless_runner/output/
705 changes: 705 additions & 0 deletions docs/game-effects.md

Large diffs are not rendered by default.

Binary file added examples/assets/crystal_ice.filamat
Binary file not shown.
56 changes: 56 additions & 0 deletions examples/assets/crystal_ice.mat
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
material {
name : CrystalIce,
shadingModel : unlit,
requires : [ position, tangents ],
variables : [ objectPos, surfaceNormal, worldPos ],
parameters : [
{ type : float, name : time },
{ type : float4, name : tint }
],
blending : transparent,
depthWrite : true,
culling : none,
variantFilter : [ skinning, shadowReceiver, vsm ]
}
vertex {
void materialVertex(inout MaterialVertexInputs material) {
material.objectPos.xyz = getPosition().xyz;
material.surfaceNormal.xyz = material.worldNormal;
material.worldPos.xyz = material.worldPosition.xyz;
}
}
fragment {
float hash(vec3 p) { p = fract(p * .1031); p += dot(p, p.yzx + 33.33); return fract((p.x+p.y)*p.z); }
float voronoi(vec3 x) {
vec3 n = floor(x), f = fract(x); float md = 8.0;
for (int k=-1;k<=1;k++) for (int j=-1;j<=1;j++) for (int i=-1;i<=1;i++) {
vec3 g=vec3(float(i),float(j),float(k));
vec3 o=vec3(hash(n+g),hash(n+g+17.0),hash(n+g+41.0));
md=min(md,length(g+o-f));
}
return md;
}
void material(inout MaterialInputs material) {
prepareMaterial(material);
vec3 p = variable_objectPos.xyz;
vec3 n = normalize(variable_surfaceNormal.xyz);
vec3 v = normalize(getWorldCameraPosition() - variable_worldPos.xyz);
float fres = pow(1.0 - abs(dot(n, v)), 2.6);
float cells = voronoi(p * 4.2);
float facets = smoothstep(.12, .72, cells);
float fractureA = 1.0 - smoothstep(.012, .038, abs(p.x*.72+p.y*.31-p.z*.28-.18));
float fractureB = 1.0 - smoothstep(.01, .032, abs(p.x*.22-p.y*.37+p.z*.81+.26));
float branchGate = smoothstep(.35,.8,sin(p.y*17.0+p.z*8.0)*.5+.5);
float crack = clamp((fractureA*branchGate + fractureB*(1.0-branchGate)) * smoothstep(.28, .72, length(p)), 0.0, 1.0);
float inner = .5 + .5 * sin(dot(p, vec3(8.0, 13.0, 17.0)) - materialParams.time * .9);
vec3 deep = materialParams.tint.rgb * (.05 + .09 * facets);
vec3 spectral = mix(vec3(.03,.35,.75), vec3(.48,.08,.85), .5 + .5 * dot(n, vec3(.57,.0,.82)));
vec3 color = deep + spectral * fres * .42;
color += vec3(.48,.85,1.0) * crack * (.38 + .12 * sin(materialParams.time * 4.0 + p.y * 20.0));
color += materialParams.tint.rgb * inner * .035;
float inclusion=step(.992,hash(floor(p*37.0)))*(.3+.7*fres);
color+=vec3(.55,.8,1.0)*inclusion*.38;
float alpha = clamp(.22 + fres * .5 + crack * .18, 0.0, .82) * materialParams.tint.a;
material.baseColor = vec4(color * alpha, alpha);
}
}
Binary file added examples/assets/damage_decals.filamat
Binary file not shown.
49 changes: 49 additions & 0 deletions examples/assets/damage_decals.mat
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
material {
name : DamageDecals,
shadingModel : unlit,
requires : [ position ],
variables : [ objectPos ],
parameters : [ { type : float, name : time } ],
culling : none,
variantFilter : [ skinning, shadowReceiver, vsm ]
}
vertex { void materialVertex(inout MaterialVertexInputs material) { material.objectPos.xyz=getPosition().xyz; } }
fragment {
float hash(vec2 p){return fract(sin(dot(p,vec2(127.1,311.7)))*43758.5453);}
float impact(vec2 p, vec2 c, float born, inout vec3 color) {
vec2 q=p-c; float r=length(q), a=atan(q.y,q.x);
float age=clamp((materialParams.time-born)*2.4,0.0,1.0);
float shape=r*(1.0+.045*sin(a*7.0+hash(c)*6.0)+.025*sin(a*13.0));
float hole=1.0-smoothstep(.035,.105,shape);
float bevel=smoothstep(.055,.09,shape)*(1.0-smoothstep(.105,.15,shape));
float scorch=(1.0-smoothstep(.10,.34,shape))*smoothstep(.065,.13,shape);
float cracks=0.0;
for(int i=0;i<9;i++){
float fi=float(i); float ang=fi*.698+hash(c+fi)*.42;
float line=abs(sin(a-ang));
float broken=step(.42,hash(vec2(floor(r*28.0),fi)+c*9.0));
cracks=max(cracks,(1.0-smoothstep(.0,.011,line))*smoothstep(.09,.14,r)*(1.0-smoothstep(.16,.44,r+hash(c+fi)*.14))*broken);
}
float fresh=exp(-max(materialParams.time-born,0.0)*2.3);
float sideLight=.25+.75*max(dot(normalize(q+vec2(.0001)),normalize(vec2(-.55,.83))),0.0);
color=mix(color,vec3(.001,.0005,.0003),max(hole,scorch*.62)*age);
color+=vec3(.36,.075,.006)*bevel*fresh*age*sideLight;
color=mix(color,vec3(.003,.0015,.001),cracks*age);
color+=vec3(.42,.07,.004)*bevel*fresh*age*.42;
return max(max(hole,scorch),cracks)*age;
}
void material(inout MaterialInputs material){
prepareMaterial(material);
vec2 p=variable_objectPos.xz;
float grain=hash(floor(p*75.0));
vec3 color=mix(vec3(.012,.014,.017),vec3(.026,.021,.016),grain*.3);
float d=0.0;
d=max(d,impact(p,vec2(-.78,.38),.1,color));
d=max(d,impact(p,vec2(.22,.52),.72,color));
d=max(d,impact(p,vec2(.68,-.16),1.34,color));
d=max(d,impact(p,vec2(-.28,-.48),1.96,color));
float vignette=smoothstep(2.1,.4,length(p));
color*=.62+.38*vignette;
material.baseColor=vec4(color,1.0);
}
}
Binary file added examples/assets/dissolve_burn.filamat
Binary file not shown.
119 changes: 119 additions & 0 deletions examples/assets/dissolve_burn.mat
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
material {
name : DissolveBurn,
requires : [ position, tangents ],
variables : [
objectPos,
surfaceNormal
],
parameters : [
{ type : float4, name : baseColor },
{ type : float4, name : edgeColor },
{ type : float, name : threshold },
{ type : float, name : edgeWidth },
{ type : float, name : edgeIntensity },
{ type : float, name : noiseScale },
{ type : float, name : time }
],
depthWrite : true,
depthCulling : true,
shadingModel : unlit,
blending : opaque,
culling : none,
variantFilter : [ shadowReceiver, vsm ]
}

vertex {
void materialVertex(inout MaterialVertexInputs material) {
// Object-space position for the noise field: pinning the noise to
// the mesh means a moving model slides through the burn instead of
// the burn sliding across the model.
material.objectPos.xyz = getPosition().xyz;
material.surfaceNormal.xyz = material.worldNormal;
}
}

fragment {
float hash(vec3 p) {
p = fract(p * vec3(443.897, 441.423, 437.195));
p += dot(p, p.yzx + 19.19);
return fract((p.x + p.y) * p.z);
}

float noise3(vec3 p) {
vec3 i = floor(p);
vec3 f = fract(p);
f = f * f * (3.0 - 2.0 * f);
return mix(
mix(mix(hash(i), hash(i + vec3(1, 0, 0)), f.x),
mix(hash(i + vec3(0, 1, 0)), hash(i + vec3(1, 1, 0)), f.x), f.y),
mix(mix(hash(i + vec3(0, 0, 1)), hash(i + vec3(1, 0, 1)), f.x),
mix(hash(i + vec3(0, 1, 1)), hash(i + vec3(1, 1, 1)), f.x), f.y),
f.z);
}

float fbm(vec3 p) {
float v = 0.0;
v += 0.5 * noise3(p); p *= 2.01;
v += 0.25 * noise3(p); p *= 2.02;
v += 0.125 * noise3(p); p *= 2.00;
v += 0.0625 * noise3(p);
return v / 0.9375;
}

void material(inout MaterialInputs material) {
prepareMaterial(material);

vec3 pos = variable_objectPos.xyz;
float n = fbm(pos * materialParams.noiseScale +
vec3(0.0, 0.0, materialParams.time * 0.15));

float t = clamp(materialParams.threshold, 0.0, 1.0);
if (n < t) {
discard;
}

// Distance from the burn front, normalized over the edge band:
// 0 right at the front, 1 once past it.
float e = clamp((n - t) / materialParams.edgeWidth, 0.0, 1.0);
float front = 1.0 - e;

// --- burning edge: white-hot core -> orange -> deep red ----------
vec3 hot = vec3(1.0, 0.96, 0.82);
vec3 mid = materialParams.edgeColor.rgb;
vec3 cool = vec3(0.55, 0.05, 0.0);
vec3 edgeCol = mix(hot, mid, smoothstep(0.06, 0.4, e));
edgeCol = mix(edgeCol, cool, smoothstep(0.4, 1.0, e));

// The front flickers like real combustion.
float flicker = 0.78 + 0.3 *
noise3(pos * 7.0 + vec3(0.0, 0.0, materialParams.time * 5.0)) +
0.16 * noise3(pos * 15.0 +
vec3(0.0, 0.0, materialParams.time * 9.0));
float glow = pow(front, 2.2) * materialParams.edgeIntensity * flicker;

// --- ember sparks right at the front ------------------------------
float spark = step(0.988, hash(floor(pos * 47.0) +
floor(materialParams.time * 9.0))) *
pow(front, 4.0);

// --- charred surface ----------------------------------------------
// Near the front the material is fully charred and faintly glowing;
// further ahead it is merely scorched, then untouched.
float charr = smoothstep(t + materialParams.edgeWidth * 3.0,
t + materialParams.edgeWidth * 0.6, n);
float detail = fbm(pos * materialParams.noiseScale * 2.7 + 31.0);
vec3 normal = normalize(variable_surfaceNormal.xyz);
vec3 key = normalize(vec3(-0.35, 0.75, 0.55));
float diffuse = 0.28 + 0.72 * max(dot(normal, key), 0.0);
float grazing = pow(1.0 - abs(normal.z), 3.0);
vec3 color = materialParams.baseColor.rgb *
(0.54 + 0.50 * detail) * diffuse;
color += materialParams.baseColor.rgb * grazing * 0.18;
color *= 1.0 - 0.72 * charr;
color += cool * pow(charr, 3.0) * 0.45;

color += edgeCol * glow + vec3(1.0, 0.65, 0.22) * spark * 1.8;

material.baseColor = vec4(color, 1.0);
}
}
Binary file added examples/assets/electricity.filamat
Binary file not shown.
52 changes: 52 additions & 0 deletions examples/assets/electricity.mat
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
material {
name : Electricity,
shadingModel : unlit,
requires : [ position ],
variables : [ quadUv, boltData ],
parameters : [ { type : float, name : time }, { type : float, name : segmentCount } ],
blending : add,
depthWrite : false,
culling : none,
variantFilter : [ skinning, shadowReceiver, vsm ]
}
vertex {
float hash(float n){return fract(sin(n)*43758.5453);}
vec2 path(float u,float seed,float t){
float x=sin(u*11.0+seed)*.18+sin(u*27.0-seed*2.0)*.075;
x+=(hash(floor(u*28.0)+seed+floor(t*18.0))-.5)*.22;
return vec2(x,(u-.5)*2.55);
}
void materialVertex(inout MaterialVertexInputs material){
int vi=getVertexIndex(); int corner=vi%6; int qi=vi/6;
vec2 uv=vec2(corner==1||corner==2||corner==4?1.0:0.0,corner==2||corner==3||corner==4?1.0:0.0);
float count=materialParams.segmentCount; float mainCount=floor(count*.62);
float id=float(qi); float branch=step(mainCount-.5,id); float sid=mix(id,id-mainCount,branch);
float u0=(sid+floor(branch*mod(sid,3.0)))/mainCount;
float u1=u0+1.0/mainCount;
vec2 a=path(u0,3.7,materialParams.time), b=path(u1,3.7,materialParams.time);
if(branch>.5){
float group=floor(sid/3.0); float root=fract((group*7.0+5.0)/mainCount)*.76+.12;
float seg=mod(sid,3.0); float side=mix(-1.0,1.0,step(.5,hash(group+9.0)));
a=path(root+seg/mainCount,3.7,materialParams.time)+vec2(side*seg*.055,0.0);
b=path(root+(seg+1.0)/mainCount,3.7,materialParams.time)+vec2(side*(seg+1.0)*.09,.02);
}
vec2 d=b-a; float len=length(d); vec2 tangent=d/max(len,.001), side=vec2(-tangent.y,tangent.x);
float width=mix(.072,.042,branch);
vec2 pt=mix(a,b,uv.y)+side*(uv.x-.5)*width;
material.worldPosition.xyz+=vec3(pt.x,pt.y,0.0)-getPosition().xyz;
material.quadUv.xy=uv; material.boltData.xy=vec2(branch,id);
}
}
fragment {
float hash(float n){return fract(sin(n)*43758.5453);}
void material(inout MaterialInputs material){
prepareMaterial(material);
vec2 uv=variable_quadUv.xy; float d=abs(uv.x-.5)*2.0;
float core=pow(max(1.0-d,0.0),11.0), glow=pow(max(1.0-d,0.0),2.0);
float pulse=.72+.28*sin(materialParams.time*32.0+variable_boltData.y*1.7);
float branch=variable_boltData.x;
vec3 color=mix(vec3(.08,.18,1.0),vec3(.42,.05,1.0),branch);
color=color*glow*.65+vec3(.65,.88,1.0)*core*4.4;
material.baseColor=vec4(color*pulse*(1.0-branch*.22),1.0);
}
}
Binary file added examples/assets/energy_weapon.filamat
Binary file not shown.
67 changes: 67 additions & 0 deletions examples/assets/energy_weapon.mat
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
material {
name : EnergyWeapon,
shadingModel : unlit,
requires : [ position ],
variables : [ objectPos ],
parameters : [
{ type : float, name : time },
{ type : float, name : mode },
{ type : float, name : phase }
],
blending : add,
depthWrite : false,
culling : none,
variantFilter : [ skinning, shadowReceiver, vsm ]
}
vertex { void materialVertex(inout MaterialVertexInputs material){material.objectPos.xyz=getPosition().xyz;} }
fragment {
float hash(vec2 p){return fract(sin(dot(p,vec2(127.1,311.7)))*43758.5453);}
float noise2(vec2 p){vec2 i=floor(p),f=fract(p);f=f*f*(3.0-2.0*f);return mix(mix(hash(i),hash(i+vec2(1,0)),f.x),mix(hash(i+vec2(0,1)),hash(i+vec2(1,1)),f.x),f.y);}
void material(inout MaterialInputs material){
prepareMaterial(material);
vec2 p=variable_objectPos.xz; float mode=materialParams.mode,t=materialParams.time;
vec3 color=vec3(0.0); float amount=0.0;
if(mode<.5){
float x=p.x/4.7+.5, y=abs(p.y)/.22;
float envelope=smoothstep(0.0,.08,x)*(1.0-smoothstep(.84,1.0,x));
float core=exp(-y*y*19.0), halo=exp(-y*y*2.2);
float plasma=.6+.4*noise2(vec2(x*35.0-t*9.0,p.y*22.0));
float pulse=pow(.5+.5*sin(x*70.0-t*18.0),8.0);
float beamAmount=envelope*(core*1.15+halo*.44*plasma+pulse*core*.65)*materialParams.phase;
vec3 beamColor=mix(vec3(.015,.1,.82),vec3(.38,.82,1.0),core);

float cycle=mod(t,2.6);
float charge=clamp(cycle/.65,0.0,1.0);
float hit=clamp((cycle-.92)/.95,0.0,1.0);
float hitEnvelope=sin(hit*3.14159265);
vec2 muzzleP=p-vec2(-1.55,0.0);
float muzzleR=length(muzzleP);
float muzzle=exp(-muzzleR*muzzleR*24.0)*(0.12+charge*.95)*(1.0-materialParams.phase*.55);
muzzle+=exp(-abs(muzzleR-(.08+charge*.2))*40.0)*charge*.65;

vec2 impactP=p-vec2(1.55,0.0);
float impactR=length(impactP), impactA=atan(impactP.y,impactP.x);
float impact=exp(-impactR*impactR*19.0)*hitEnvelope*1.8;
impact+=exp(-abs(impactR-hit*.48)*32.0)*hitEnvelope*1.5;
impact+=pow(max(sin(impactA*10.0+t*7.0),0.0),18.0)*
smoothstep(.56,.05,impactR)*hitEnvelope*.9;

color=beamColor*beamAmount+
vec3(.08,.48,1.0)*muzzle+
vec3(.42,.08,1.0)*impact;
color+=vec3(.62,.9,1.0)*(core*beamAmount*.45+
exp(-muzzleR*muzzleR*85.0)*charge*.8+
exp(-impactR*impactR*70.0)*hitEnvelope);
amount=1.0;
} else {
vec2 q=p*2.0; float r=length(q),a=atan(q.y,q.x);
if(r>.94) discard;
float ring=exp(-abs(r-materialParams.phase*.72)*22.0);
float rays=pow(max(0.0,sin(a*11.0+t*8.0)),16.0)*smoothstep(.85,.04,r);
float orb=pow(max(1.0-r,0.0),2.2);
amount=(orb*4.5+ring*4.0+rays*2.4)*smoothstep(0.0,.04,materialParams.phase)*(1.0-smoothstep(.88,1.0,materialParams.phase));
color=mix(vec3(.055,.008,.62),vec3(.28,.72,1.0),clamp(orb+ring,0.0,1.0));
}
material.baseColor=vec4(color*amount*.055,1.0);
}
}
Binary file added examples/assets/fire.filamat
Binary file not shown.
Loading