@absolutejs/rendering 0.5.0 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +17 -2
- package/dist/index.d.ts +3 -2
- package/dist/index.js +2 -1
- package/dist/leafLighting.d.ts +7 -0
- package/dist/leafLighting.js +64 -0
- package/dist/thinFoliage.d.ts +4 -4
- package/dist/thinFoliage.js +18 -25
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -137,9 +137,24 @@ scene-dependent; the qualification uses software rendering, not a hardware FPS c
|
|
|
137
137
|
`createThinFoliageMaterial({ leafTransmission: 0.35 })` creates a standard
|
|
138
138
|
material with approximate thin-leaf backlighting. Transmission is a bounded
|
|
139
139
|
fraction (0–1) of direct diffuse; zero retains standard lighting. The shader
|
|
140
|
-
uses Three's shadowed incident light, preserving PBR specular,
|
|
141
|
-
fog and tone mapping. Area lights retain standard lighting. This is a lightweight
|
|
140
|
+
uses Three's shadowed incident light, preserving PBR specular,
|
|
141
|
+
fog and tone mapping. Area lights retain standard lighting; hemisphere lights and PMREM diffuse illumination transmit from the reverse side. This is a lightweight
|
|
142
142
|
leaf approximation, not volumetric subsurface scattering or a complete grass
|
|
143
143
|
renderer. Distribution, blade shape, wind and terrain integration remain the
|
|
144
144
|
caller's responsibility. Compose custom shader hooks after the factory hook;
|
|
145
145
|
use `cloneRenderMaterial` to preserve them when cloning.
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
`withLeafLighting(fragmentShader, transmission)` and
|
|
149
|
+
`withLeafTransmissionField(fragmentShader, identifier)` apply the same canonical
|
|
150
|
+
thin-sheet model to an existing standard-material shader. The field form expects
|
|
151
|
+
a declared scalar clamped to [0,1]. These helpers preserve the existing Three
|
|
152
|
+
specular paths, including environment multiple-scattering compensation. Compose
|
|
153
|
+
vertex deformation independently and include transmission policy in cache keys.
|
|
154
|
+
An environment-lit leaf uses an additional reverse-normal irradiance lookup;
|
|
155
|
+
this is not a free shader operation. No extra passes or geometry are added.
|
|
156
|
+
|
|
157
|
+
Version 0.6 replaces the factory's view-dependent direct-scattering factor with
|
|
158
|
+
the same thin-sheet diffuse weighting used by the lower-level hooks. Existing
|
|
159
|
+
material colour/transmission settings remain valid, but direct backlighting can
|
|
160
|
+
change. This release also adds sky transmission missing from 0.5.
|
package/dist/index.d.ts
CHANGED
|
@@ -14,5 +14,6 @@ export declare function cloneRenderMaterial<T extends Material>(source: T): T;
|
|
|
14
14
|
export { renderTargetScope, readTargetPixelsScoped, readFloatTargetScoped, } from "./renderTargetScope.js";
|
|
15
15
|
export { createObjectTriplanarMaterial } from "./objectTriplanar.js";
|
|
16
16
|
export type { ObjectTriplanarProjection, ObjectTriplanarMaterialParameters, } from "./objectTriplanar.js";
|
|
17
|
-
export { createThinFoliageMaterial } from
|
|
18
|
-
export type { ThinFoliageMaterialParameters } from
|
|
17
|
+
export { createThinFoliageMaterial } from "./thinFoliage.js";
|
|
18
|
+
export type { ThinFoliageMaterialParameters } from "./thinFoliage.js";
|
|
19
|
+
export { withLeafLighting, withLeafTransmissionField, leafLightingGLSL, } from "./leafLighting.js";
|
package/dist/index.js
CHANGED
|
@@ -77,4 +77,5 @@ export function cloneRenderMaterial(source) {
|
|
|
77
77
|
}
|
|
78
78
|
export { renderTargetScope, readTargetPixelsScoped, readFloatTargetScoped, } from "./renderTargetScope.js";
|
|
79
79
|
export { createObjectTriplanarMaterial } from "./objectTriplanar.js";
|
|
80
|
-
export { createThinFoliageMaterial } from
|
|
80
|
+
export { createThinFoliageMaterial } from "./thinFoliage.js";
|
|
81
|
+
export { withLeafLighting, withLeafTransmissionField, leafLightingGLSL, } from "./leafLighting.js";
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/** Bounded thin-sheet diffuse approximation. Thickness values are art calibration,
|
|
2
|
+
* not measured transmittance; direct-light color already includes Three's shadows. */
|
|
3
|
+
export declare const leafLightingGLSL = "\nvec2 leafDiffuseWeights(float cosine, float transmission) {\n float t=clamp(transmission,0.,1.);\n return vec2(max(0.,cosine)*(1.-t),max(0.,-cosine)*t);\n}\nvoid RE_Direct_Leaf(const in IncidentLight light, const in vec3 position,\n const in vec3 normal, const in vec3 viewDirection, const in vec3 clearcoatNormal,\n const in PhysicalMaterial material, inout ReflectedLight reflectedLight) {\n vec3 previous=reflectedLight.directDiffuse;\n RE_Direct_Physical(light,position,normal,viewDirection,clearcoatNormal,material,reflectedLight);\n vec2 weights=leafDiffuseWeights(dot(normal,light.direction),LEAF_TRANSMISSION);\n reflectedLight.directDiffuse=previous+(reflectedLight.directDiffuse-previous)*(1.-LEAF_TRANSMISSION)\n +light.color*material.diffuseColor*(RECIPROCAL_PI*weights.y);\n}\n#if NUM_HEMI_LIGHTS > 0\nvec3 getLeafHemisphereLightIrradiance(const in HemisphereLight light, const in vec3 normal) {\n return mix(getHemisphereLightIrradiance(light,normal),getHemisphereLightIrradiance(light,-normal),LEAF_TRANSMISSION);\n}\n#define getHemisphereLightIrradiance getLeafHemisphereLightIrradiance\n#endif\n#undef RE_Direct\n#define RE_Direct RE_Direct_Leaf\n";
|
|
4
|
+
export declare function withLeafLighting(shader: string, transmission: number): string;
|
|
5
|
+
/** Per-fragment transmission must already be clamped to [0,1] by its material.
|
|
6
|
+
* Accept a single declared identifier, never arbitrary injected shader text. */
|
|
7
|
+
export declare function withLeafTransmissionField(shader: string, identifier: string): string;
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { ShaderChunk } from "three";
|
|
2
|
+
/** Bounded thin-sheet diffuse approximation. Thickness values are art calibration,
|
|
3
|
+
* not measured transmittance; direct-light color already includes Three's shadows. */
|
|
4
|
+
export const leafLightingGLSL = /* glsl */ `
|
|
5
|
+
vec2 leafDiffuseWeights(float cosine, float transmission) {
|
|
6
|
+
float t=clamp(transmission,0.,1.);
|
|
7
|
+
return vec2(max(0.,cosine)*(1.-t),max(0.,-cosine)*t);
|
|
8
|
+
}
|
|
9
|
+
void RE_Direct_Leaf(const in IncidentLight light, const in vec3 position,
|
|
10
|
+
const in vec3 normal, const in vec3 viewDirection, const in vec3 clearcoatNormal,
|
|
11
|
+
const in PhysicalMaterial material, inout ReflectedLight reflectedLight) {
|
|
12
|
+
vec3 previous=reflectedLight.directDiffuse;
|
|
13
|
+
RE_Direct_Physical(light,position,normal,viewDirection,clearcoatNormal,material,reflectedLight);
|
|
14
|
+
vec2 weights=leafDiffuseWeights(dot(normal,light.direction),LEAF_TRANSMISSION);
|
|
15
|
+
reflectedLight.directDiffuse=previous+(reflectedLight.directDiffuse-previous)*(1.-LEAF_TRANSMISSION)
|
|
16
|
+
+light.color*material.diffuseColor*(RECIPROCAL_PI*weights.y);
|
|
17
|
+
}
|
|
18
|
+
#if NUM_HEMI_LIGHTS > 0
|
|
19
|
+
vec3 getLeafHemisphereLightIrradiance(const in HemisphereLight light, const in vec3 normal) {
|
|
20
|
+
return mix(getHemisphereLightIrradiance(light,normal),getHemisphereLightIrradiance(light,-normal),LEAF_TRANSMISSION);
|
|
21
|
+
}
|
|
22
|
+
#define getHemisphereLightIrradiance getLeafHemisphereLightIrradiance
|
|
23
|
+
#endif
|
|
24
|
+
#undef RE_Direct
|
|
25
|
+
#define RE_Direct RE_Direct_Leaf
|
|
26
|
+
`;
|
|
27
|
+
export function withLeafLighting(shader, transmission) {
|
|
28
|
+
if (!Number.isFinite(transmission) || transmission < 0 || transmission > 1)
|
|
29
|
+
throw Error("Leaf transmission must be in [0,1]");
|
|
30
|
+
return withLeafExpression(shader, transmission.toFixed(4));
|
|
31
|
+
}
|
|
32
|
+
/** Per-fragment transmission must already be clamped to [0,1] by its material.
|
|
33
|
+
* Accept a single declared identifier, never arbitrary injected shader text. */
|
|
34
|
+
export function withLeafTransmissionField(shader, identifier) {
|
|
35
|
+
if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(identifier) ||
|
|
36
|
+
identifier.startsWith("gl_"))
|
|
37
|
+
throw Error("Invalid leaf transmission field identifier");
|
|
38
|
+
return withLeafExpression(shader, identifier);
|
|
39
|
+
}
|
|
40
|
+
function withLeafExpression(shader, expression) {
|
|
41
|
+
const marker = "#include <lights_physical_pars_fragment>";
|
|
42
|
+
if (shader.split(marker).length !== 2)
|
|
43
|
+
throw Error("Three physical lighting hook missing");
|
|
44
|
+
// Three r186 shares irradiance with multiple-scattering specular compensation.
|
|
45
|
+
// Patch only its diffuse accumulation; changing getIBLIrradiance globally
|
|
46
|
+
// would also transmit a portion of the specular response through the leaf.
|
|
47
|
+
const diffuse = "vec3 indirectDiffuse = diffuse * cosineWeightedIrradiance;";
|
|
48
|
+
const physical = ShaderChunk.lights_physical_pars_fragment;
|
|
49
|
+
if (physical.split(diffuse).length !== 2)
|
|
50
|
+
throw Error("Three indirect diffuse hook missing");
|
|
51
|
+
return shader.replace(marker, "\n#define LEAF_TRANSMISSION " +
|
|
52
|
+
expression +
|
|
53
|
+
"\n" +
|
|
54
|
+
physical.replace(diffuse, `
|
|
55
|
+
#ifdef USE_ENVMAP
|
|
56
|
+
vec3 leafIrradiance = mix(irradiance, getIBLIrradiance(-geometryNormal), LEAF_TRANSMISSION);
|
|
57
|
+
vec3 indirectDiffuse = diffuse * leafIrradiance * RECIPROCAL_PI;
|
|
58
|
+
#else
|
|
59
|
+
${diffuse}
|
|
60
|
+
#endif
|
|
61
|
+
`) +
|
|
62
|
+
"\n" +
|
|
63
|
+
leafLightingGLSL);
|
|
64
|
+
}
|
package/dist/thinFoliage.d.ts
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import { MeshStandardMaterial, type MeshStandardMaterialParameters } from
|
|
1
|
+
import { MeshStandardMaterial, type MeshStandardMaterialParameters } from "three";
|
|
2
2
|
export type ThinFoliageMaterialParameters = MeshStandardMaterialParameters & {
|
|
3
|
-
/** Fraction of
|
|
3
|
+
/** Fraction of diffuse illumination allocated to approximate leaf transmission, 0..1. */
|
|
4
4
|
leafTransmission?: number;
|
|
5
5
|
};
|
|
6
|
-
/** Approximate thin-leaf backlighting using Three's
|
|
6
|
+
/** Approximate thin-leaf backlighting using Three's shadowed direct and directional sky light.
|
|
7
7
|
* Not volumetric transmission. No extra render pass, texture, or animation loop.
|
|
8
|
-
*
|
|
8
|
+
* Sky diffuse is transmitted; specular and area lights retain standard shading. Use
|
|
9
9
|
* cloneRenderMaterial to preserve shader hooks when cloning.
|
|
10
10
|
*/
|
|
11
11
|
export declare function createThinFoliageMaterial(options?: ThinFoliageMaterialParameters): MeshStandardMaterial;
|
package/dist/thinFoliage.js
CHANGED
|
@@ -1,34 +1,27 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
1
|
+
import { withLeafTransmissionField } from "./leafLighting.js";
|
|
2
|
+
import { DoubleSide, MeshStandardMaterial, } from "three";
|
|
3
|
+
/** Approximate thin-leaf backlighting using Three's shadowed direct and directional sky light.
|
|
3
4
|
* Not volumetric transmission. No extra render pass, texture, or animation loop.
|
|
4
|
-
*
|
|
5
|
+
* Sky diffuse is transmitted; specular and area lights retain standard shading. Use
|
|
5
6
|
* cloneRenderMaterial to preserve shader hooks when cloning.
|
|
6
7
|
*/
|
|
7
8
|
export function createThinFoliageMaterial(options = {}) {
|
|
8
9
|
const { leafTransmission = 0.35, ...parameters } = options;
|
|
9
|
-
if (!Number.isFinite(leafTransmission) ||
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
10
|
+
if (!Number.isFinite(leafTransmission) ||
|
|
11
|
+
leafTransmission < 0 ||
|
|
12
|
+
leafTransmission > 1)
|
|
13
|
+
throw Error("Invalid leaf transmission");
|
|
14
|
+
const material = new MeshStandardMaterial({
|
|
15
|
+
side: DoubleSide,
|
|
16
|
+
roughness: 0.85,
|
|
17
|
+
...parameters,
|
|
18
|
+
});
|
|
19
|
+
material.onBeforeCompile = (shader) => {
|
|
16
20
|
shader.uniforms.absLeafTransmission = { value: leafTransmission };
|
|
17
|
-
shader.fragmentShader =
|
|
18
|
-
uniform float absLeafTransmission
|
|
19
|
-
|
|
20
|
-
const in vec3 v, const in vec3 coatN, const in PhysicalMaterial m, inout ReflectedLight result) {
|
|
21
|
-
vec3 previousDiffuse=result.directDiffuse;
|
|
22
|
-
RE_Direct_Physical(light,p,n,v,coatN,m,result);
|
|
23
|
-
float through=max(0.,dot(-n,light.direction));
|
|
24
|
-
float forward=max(0.,dot(v,-light.direction));
|
|
25
|
-
vec3 transmitted=light.color*BRDF_Lambert(m.diffuseColor)*through*(.5+.5*forward*forward);
|
|
26
|
-
result.directDiffuse=previousDiffuse+mix(result.directDiffuse-previousDiffuse,transmitted,absLeafTransmission);
|
|
27
|
-
}
|
|
28
|
-
#undef RE_Direct
|
|
29
|
-
#define RE_Direct absLeafDirect
|
|
30
|
-
`);
|
|
21
|
+
shader.fragmentShader =
|
|
22
|
+
"uniform float absLeafTransmission;\n" +
|
|
23
|
+
withLeafTransmissionField(shader.fragmentShader, "absLeafTransmission");
|
|
31
24
|
};
|
|
32
|
-
material.customProgramCacheKey = () => `absolute-thin-foliage-
|
|
25
|
+
material.customProgramCacheKey = () => `absolute-thin-foliage-2:${leafTransmission}`;
|
|
33
26
|
return material;
|
|
34
27
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@absolutejs/rendering",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"description": "Three.js material ownership, destination coverage, rigid-object triplanar mapping and isolated React Spring hosts.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -39,7 +39,8 @@
|
|
|
39
39
|
"test:browser": "bun scripts/verify-browser.cjs",
|
|
40
40
|
"test:spring:browser": "bun scripts/verify-spring.cjs",
|
|
41
41
|
"test:triplanar:browser": "bun scripts/verify-triplanar.cjs",
|
|
42
|
-
"test:foliage:browser": "bun scripts/verify-foliage.cjs"
|
|
42
|
+
"test:foliage:browser": "bun scripts/verify-foliage.cjs",
|
|
43
|
+
"test:foliage:environment": "bun scripts/verify-foliage-environment.cjs"
|
|
43
44
|
},
|
|
44
45
|
"peerDependencies": {
|
|
45
46
|
"three": ">=0.186.0 <0.187.0",
|