mirror of
https://github.com/storytold/spark.git
synced 2026-10-17 00:12:59 +00:00
Splat Texture (#79)
* Add experimental Splat Texture rendering and examples/splat-texture. Added star.png and heart.png to assets.json and to assets repo. Update examples/depth-of-field to compute from metric aperture size. Added SparkRenderer.renderScale debug parameter. Updated examples/editor: set default highDPI to true, added logFocalDistance, apertureAngle, refactored, grouped into debugFolder, default settings open, preset buttons for nonAA and AA settings. * Set default highDpi in examples/editor to not(isMobile).
This commit is contained in:
committed by
GitHub
parent
c0db3576bd
commit
179885595c
@@ -148,6 +148,17 @@ export class SparkRenderer extends THREE.Mesh {
|
||||
apertureAngle: number;
|
||||
falloff: number;
|
||||
clipXY: number;
|
||||
renderScale = 1.0;
|
||||
|
||||
splatTexture: null | {
|
||||
enable?: boolean;
|
||||
texture?: THREE.Data3DTexture;
|
||||
multiply?: THREE.Matrix2;
|
||||
add?: THREE.Vector2;
|
||||
near?: number;
|
||||
far?: number;
|
||||
mid?: number;
|
||||
} = null;
|
||||
|
||||
time?: number;
|
||||
deltaTime?: number;
|
||||
@@ -198,6 +209,8 @@ export class SparkRenderer extends THREE.Mesh {
|
||||
} | null = null;
|
||||
private static pmrem: THREE.PMREMGenerator | null = null;
|
||||
|
||||
static EMPTY_SPLAT_TEXTURE = new THREE.Data3DTexture();
|
||||
|
||||
constructor(options: SparkRendererOptions) {
|
||||
const uniforms = SparkRenderer.makeUniforms();
|
||||
const shaders = getShaders();
|
||||
@@ -281,6 +294,9 @@ export class SparkRenderer extends THREE.Mesh {
|
||||
const uniforms = {
|
||||
// Size of render viewport in pixels
|
||||
renderSize: { value: new THREE.Vector2() },
|
||||
// Near and far plane distances
|
||||
near: { value: 0.1 },
|
||||
far: { value: 1000.0 },
|
||||
// Total number of Gsplats in packedSplats to render
|
||||
numSplats: { value: 0 },
|
||||
// SplatAccumulator to view transformation quaternion
|
||||
@@ -304,6 +320,22 @@ export class SparkRenderer extends THREE.Mesh {
|
||||
falloff: { value: 1.0 },
|
||||
// Clip Gsplats that are clipXY times beyond the +-1 frustum bounds
|
||||
clipXY: { value: 1.4 },
|
||||
// Debug renderSize scale factor
|
||||
renderScale: { value: 1.0 },
|
||||
// Enable splat texture rendering
|
||||
splatTexEnable: { value: false },
|
||||
// Splat texture to render
|
||||
splatTexture: { type: "t", value: SparkRenderer.EMPTY_SPLAT_TEXTURE },
|
||||
// Splat texture UV transform (multiply)
|
||||
splatTexMul: { value: new THREE.Matrix2() },
|
||||
// Splat texture UV transform (add)
|
||||
splatTexAdd: { value: new THREE.Vector2() },
|
||||
// Splat texture near plane distance
|
||||
splatTexNear: { value: 0.1 },
|
||||
// Splat texture far plane distance
|
||||
splatTexFar: { value: 1000.0 },
|
||||
// Splat texture mid plane distance, or 0.0 to disable
|
||||
splatTexMid: { value: 0.0 },
|
||||
// Gsplat collection to render
|
||||
packedSplats: { type: "t", value: PackedSplats.getEmpty() },
|
||||
// Time in seconds for time-based effects
|
||||
@@ -433,6 +465,11 @@ export class SparkRenderer extends THREE.Mesh {
|
||||
}
|
||||
|
||||
// Update uniforms from instance properties
|
||||
const typedCamera = camera as
|
||||
| THREE.PerspectiveCamera
|
||||
| THREE.OrthographicCamera;
|
||||
this.uniforms.near.value = typedCamera.near;
|
||||
this.uniforms.far.value = typedCamera.far;
|
||||
this.uniforms.encodeLinear.value = viewpoint.encodeLinear;
|
||||
this.uniforms.maxStdDev.value = this.maxStdDev;
|
||||
this.uniforms.enable2DGS.value = this.enable2DGS;
|
||||
@@ -442,6 +479,36 @@ export class SparkRenderer extends THREE.Mesh {
|
||||
this.uniforms.apertureAngle.value = this.apertureAngle;
|
||||
this.uniforms.falloff.value = this.falloff;
|
||||
this.uniforms.clipXY.value = this.clipXY;
|
||||
this.uniforms.renderScale.value = this.renderScale;
|
||||
|
||||
if (this.splatTexture) {
|
||||
const { enable, texture, multiply, add, near, far, mid } =
|
||||
this.splatTexture;
|
||||
if (enable && texture) {
|
||||
this.uniforms.splatTexEnable.value = true;
|
||||
this.uniforms.splatTexture.value = texture;
|
||||
if (multiply) {
|
||||
this.uniforms.splatTexMul.value.fromArray(multiply.elements);
|
||||
} else {
|
||||
this.uniforms.splatTexMul.value.set(
|
||||
0.5 / this.maxStdDev,
|
||||
0,
|
||||
0,
|
||||
0.5 / this.maxStdDev,
|
||||
);
|
||||
}
|
||||
this.uniforms.splatTexAdd.value.set(add?.x ?? 0.5, add?.y ?? 0.5);
|
||||
this.uniforms.splatTexNear.value = near ?? this.uniforms.near.value;
|
||||
this.uniforms.splatTexFar.value = far ?? this.uniforms.far.value;
|
||||
this.uniforms.splatTexMid.value = mid ?? 0.0;
|
||||
} else {
|
||||
this.uniforms.splatTexEnable.value = false;
|
||||
this.uniforms.splatTexture.value = SparkRenderer.EMPTY_SPLAT_TEXTURE;
|
||||
}
|
||||
} else {
|
||||
this.uniforms.splatTexEnable.value = false;
|
||||
this.uniforms.splatTexture.value = SparkRenderer.EMPTY_SPLAT_TEXTURE;
|
||||
}
|
||||
|
||||
// Calculate the transform from the accumulator to the current camera
|
||||
const accumToWorld =
|
||||
|
||||
@@ -4,11 +4,21 @@ precision highp int;
|
||||
|
||||
#include <splatDefines>
|
||||
|
||||
uniform float near;
|
||||
uniform float far;
|
||||
uniform bool encodeLinear;
|
||||
uniform float maxStdDev;
|
||||
uniform bool disableFalloff;
|
||||
uniform float falloff;
|
||||
|
||||
uniform bool splatTexEnable;
|
||||
uniform sampler3D splatTexture;
|
||||
uniform mat2 splatTexMul;
|
||||
uniform vec2 splatTexAdd;
|
||||
uniform float splatTexNear;
|
||||
uniform float splatTexFar;
|
||||
uniform float splatTexMid;
|
||||
|
||||
out vec4 fragColor;
|
||||
|
||||
in vec4 vRgba;
|
||||
@@ -16,20 +26,45 @@ in vec2 vSplatUv;
|
||||
in vec3 vNdc;
|
||||
|
||||
void main() {
|
||||
vec4 rgba = vRgba;
|
||||
|
||||
float z = dot(vSplatUv, vSplatUv);
|
||||
if (z > (maxStdDev * maxStdDev)) {
|
||||
discard;
|
||||
if (!splatTexEnable) {
|
||||
if (z > (maxStdDev * maxStdDev)) {
|
||||
discard;
|
||||
}
|
||||
} else {
|
||||
vec2 uv = splatTexMul * vSplatUv + splatTexAdd;
|
||||
float ndcZ = vNdc.z;
|
||||
float depth = (2.0 * near * far) / (far + near - ndcZ * (far - near));
|
||||
float clampedFar = max(splatTexFar, splatTexNear);
|
||||
float clampedDepth = clamp(depth, splatTexNear, clampedFar);
|
||||
float logDepth = log2(clampedDepth + 1.0);
|
||||
float logNear = log2(splatTexNear + 1.0);
|
||||
float logFar = log2(clampedFar + 1.0);
|
||||
|
||||
float texZ;
|
||||
if (splatTexMid > 0.0) {
|
||||
float clampedMid = clamp(splatTexMid, splatTexNear, clampedFar);
|
||||
float logMid = log2(clampedMid + 1.0);
|
||||
texZ = (clampedDepth <= clampedMid) ?
|
||||
(0.5 * ((logDepth - logNear) / (logMid - logNear))) :
|
||||
(0.5 * ((logDepth - logMid) / (logFar - logMid)) + 0.5);
|
||||
} else {
|
||||
texZ = (logDepth - logNear) / (logFar - logNear);
|
||||
}
|
||||
|
||||
vec4 modulate = texture(splatTexture, vec3(uv, 1.0 - texZ));
|
||||
rgba *= modulate;
|
||||
}
|
||||
|
||||
float alpha = vRgba.a;
|
||||
alpha *= mix(1.0, exp(-0.5 * z), falloff);
|
||||
if (alpha < MIN_ALPHA) {
|
||||
rgba.a *= mix(1.0, exp(-0.5 * z), falloff);
|
||||
|
||||
if (rgba.a < MIN_ALPHA) {
|
||||
discard;
|
||||
}
|
||||
|
||||
vec3 rgb = vRgba.rgb;
|
||||
if (encodeLinear) {
|
||||
rgb = srgbToLinear(rgb);
|
||||
rgba.rgb = srgbToLinear(rgba.rgb);
|
||||
}
|
||||
fragColor = vec4(rgb, alpha);
|
||||
fragColor = rgba;
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@ uniform float preBlurAmount;
|
||||
uniform float focalDistance;
|
||||
uniform float apertureAngle;
|
||||
uniform float clipXY;
|
||||
uniform float renderScale;
|
||||
|
||||
uniform usampler2DArray packedSplats;
|
||||
|
||||
@@ -111,7 +112,8 @@ void main() {
|
||||
mat3 cov3D = RS * transpose(RS);
|
||||
|
||||
// Compute the Jacobian of the splat's projection at its center
|
||||
vec2 focal = 0.5 * renderSize * vec2(projectionMatrix[0][0], projectionMatrix[1][1]);
|
||||
vec2 scaledRenderSize = renderSize * renderScale;
|
||||
vec2 focal = 0.5 * scaledRenderSize * vec2(projectionMatrix[0][0], projectionMatrix[1][1]);
|
||||
float invZ = 1.0 / viewCenter.z;
|
||||
vec2 J1 = focal * invZ;
|
||||
vec2 J2 = -(J1 * viewCenter.xy) * invZ;
|
||||
@@ -175,7 +177,7 @@ void main() {
|
||||
|
||||
// Compute the NDC coordinates for the ellipsoid's diagonal axes.
|
||||
vec2 pixelOffset = eigenVec1 * scale1 + eigenVec2 * scale2;
|
||||
vec2 ndcOffset = (2.0 / renderSize) * pixelOffset;
|
||||
vec2 ndcOffset = (2.0 / scaledRenderSize) * pixelOffset;
|
||||
vec3 ndc = vec3(ndcCenter.xy + ndcOffset, ndcCenter.z);
|
||||
|
||||
vRgba = rgba;
|
||||
|
||||
Reference in New Issue
Block a user