Created
May 14, 2025 07:18
-
-
Save Ctrlmonster/cc4a47c7483db0e3baf790ea7d250536 to your computer and use it in GitHub Desktop.
Lod dither fade for mesh and batched mesh
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// for regular meshes | |
import {ShaderMaterial, Vector2, Color, Texture} from "three"; | |
export class LodFadeMaterial extends ShaderMaterial { | |
constructor(map: Texture, ditherOffset: number, ditherThreshold: number, invertMask: boolean, resolution: Vector2, color: Color) { | |
super({ | |
uniforms: { | |
map: {value: map}, | |
resolution: {value: resolution}, | |
ditherOffset: {value: ditherOffset}, | |
ditherThreshold: {value: ditherThreshold}, | |
invertMask: {value: invertMask}, | |
color: {value: color}, | |
}, | |
vertexShader: ` | |
varying vec2 vUv; | |
void main() { | |
vUv = uv; | |
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0); | |
} | |
`, | |
fragmentShader: ` | |
varying vec2 vUv; | |
uniform vec2 resolution; | |
uniform float ditherOffset; | |
uniform float ditherThreshold; | |
uniform vec3 color; // Debug color | |
uniform bool invertMask; | |
uniform sampler2D map; | |
const mat4 bayerMatrix = mat4( | |
0.0, 0.5, 0.125, 0.625, | |
0.75, 0.25, 0.875, 0.375, | |
0.1875, 0.6875, 0.0625, 0.5625, | |
0.9375, 0.4375, 0.8125, 0.3125 | |
); | |
float dither(vec2 uv, float offset) { | |
vec2 gridPos = mod(floor(uv * 4.0), 4.0); | |
float value = bayerMatrix[int(gridPos.y)][int(gridPos.x)]; | |
return mod(value + offset, 1.0); | |
} | |
void main() { | |
vec2 uv = gl_FragCoord.xy / resolution; | |
float threshold = dither(uv, ditherOffset); | |
bool shouldDiscard = invertMask ? (threshold >= ditherThreshold) : (threshold < ditherThreshold); | |
if (shouldDiscard) discard; | |
// Use the uniform color for debugging | |
vec3 color0 = texture2D(map, vUv).rgb; | |
gl_FragColor = vec4(color0 * color, 1.0); | |
} | |
`, | |
}); | |
} | |
/** | |
* Updates the resolution uniform. | |
* @param resolution New resolution value. | |
*/ | |
setResolution(resolution: number) { | |
this.uniforms.resolution.value.set(resolution, resolution); | |
} | |
setDitherThreshold(threshold: number) { | |
this.uniforms.ditherThreshold.value = threshold; | |
} | |
setInvertMask(invertMask: boolean) { | |
this.uniforms.invertMask.value = invertMask; | |
} | |
} | |
// ============================================================================================================= | |
// for batched mesh using UBOs | |
import {Color, ShaderMaterial, StaticDrawUsage, Texture, Uniform, UniformsGroup, Vector2, Vector4} from "three"; | |
const INIT_DITHER_OFFSET = 0.0; | |
const INIT_DITHER_THRESHOLD = 0.0;//0.5; | |
const INIT_INVERT_MASK_ = 0; // <- false | |
const INIT_DITHER_RESOLUTION = new Vector2(4, 4); | |
//const INIT_COLOR = new Color("white"); | |
const INIT_DITHER_SETTINGS = { | |
ditherOffset: INIT_DITHER_OFFSET, | |
ditherThreshold: INIT_DITHER_THRESHOLD, | |
invertMask: INIT_INVERT_MASK_, | |
resolution: INIT_DITHER_RESOLUTION, | |
}; | |
export class LodFadeBatchMaterial extends ShaderMaterial { | |
ditherUniformsGroup: UniformsGroup; | |
readonly isLodMaterial = true; | |
constructor(maxBatchInstances: number, map: Texture, color: Color, initDitherSettings = INIT_DITHER_SETTINGS) { | |
/*const color = new Color(Math.random(), Math.random(), Math.random()); | |
console.log("Inside constructor of lod fade batch material:"); | |
console.log("args: ", maxBatchInstances, map);*/ | |
if (!maxBatchInstances || !map || !color) { | |
throw new Error(`args not there`); | |
} | |
super({ | |
//wireframe: true, | |
uniforms: { | |
map: {value: map}, | |
debugColor: {value: color} | |
}, | |
defines: { | |
MAX_BATCH_INSTANCE_COUNT: maxBatchInstances | |
}, | |
vertexShader: ` | |
varying vec2 vUv; | |
flat out int vBatchId; | |
#if ! defined( GL_ANGLE_multi_draw ) | |
#define gl_DrawID _gl_DrawID | |
uniform int _gl_DrawID; | |
#endif | |
uniform highp sampler2D batchingTexture; | |
uniform highp usampler2D batchingIdTexture; | |
mat4 getBatchingMatrix( const in float i ) { | |
int size = textureSize( batchingTexture, 0 ).x; | |
int j = int( i ) * 4; | |
int x = j % size; | |
int y = j / size; | |
vec4 v1 = texelFetch( batchingTexture, ivec2( x, y ), 0 ); | |
vec4 v2 = texelFetch( batchingTexture, ivec2( x + 1, y ), 0 ); | |
vec4 v3 = texelFetch( batchingTexture, ivec2( x + 2, y ), 0 ); | |
vec4 v4 = texelFetch( batchingTexture, ivec2( x + 3, y ), 0 ); | |
return mat4( v1, v2, v3, v4 ); | |
} | |
float getIndirectIndex( const in int i ) { | |
int size = textureSize( batchingIdTexture, 0 ).x; | |
int x = i % size; | |
int y = i / size; | |
return float( texelFetch( batchingIdTexture, ivec2( x, y ), 0 ).r ); | |
} | |
void main() { | |
vUv = uv; | |
float indirectIdx = getIndirectIndex( gl_DrawID ); | |
mat4 batchingMatrix = getBatchingMatrix( indirectIdx ); | |
// pass the batch id to the fragment shader | |
vBatchId = int(indirectIdx); | |
// multiply batch position with per-instance matrix | |
vec4 instancePosition = (batchingMatrix * vec4( position, 1.0 )).xyzw; | |
// Compute view-space position. | |
vec4 mvPosition = modelViewMatrix * instancePosition; | |
// Transform vertex to clip space. | |
gl_Position = projectionMatrix * mvPosition; | |
} | |
`, | |
fragmentShader: ` | |
#ifndef MAX_BATCH_INSTANCE_COUNT | |
#define MAX_BATCH_INSTANCE_COUNT 64 | |
#endif | |
varying vec2 vUv; | |
flat in int vBatchId; | |
uniform sampler2D map; | |
uniform vec3 debugColor; | |
/*layout (std140) uniform DitherSettings { | |
vec4 packedSettings0[MAX_BATCH_INSTANCE_COUNT]; | |
vec4 packedSettings1[MAX_BATCH_INSTANCE_COUNT]; | |
};*/ | |
/* | |
const mat4 bayerMatrix = mat4( | |
0.0, 0.5, 0.125, 0.625, | |
0.75, 0.25, 0.875, 0.375, | |
0.1875, 0.6875, 0.0625, 0.5625, | |
0.9375, 0.4375, 0.8125, 0.3125 | |
); | |
float dither(vec2 uv, float offset) { | |
vec2 gridPos = mod(floor(uv * 4.0), 4.0); | |
float value = bayerMatrix[int(gridPos.y)][int(gridPos.x)]; | |
return mod(value + offset, 1.0); | |
}*/ | |
void main() { | |
/* | |
vec4 packed0 = packedSettings0[vBatchId].rgba; | |
float ditherOffset = packed0.x; | |
float ditherThreshold = packed0.y; | |
vec2 ditherResolution = vec2(4.0, 4.0); | |
//vec2 ditherResolution = vec2(packedSettings0[vBatchId].z, packedSettings0[vBatchId].w); | |
vec2 ditherUv = gl_FragCoord.xy / ditherResolution; | |
bool invertMask = packed0.z > 0.5; | |
float ditherValue = dither(ditherUv, ditherOffset); | |
bool shouldDiscard = invertMask ? (ditherValue >= ditherThreshold) : (ditherValue < ditherThreshold); | |
if (shouldDiscard) discard;*/ | |
//vec3 color = packedSettings1[vBatchId].xyz; | |
// Use the uniform color for debugging | |
vec3 albedo = texture2D(map, vUv).rgb; | |
gl_FragColor = vec4(albedo, 1.0); | |
} | |
`, | |
}); | |
// ------- Pack Settings into UniformsGroups ------------------------------- | |
const N = maxBatchInstances; | |
this.ditherUniformsGroup = new UniformsGroup(); | |
this.ditherUniformsGroup.setName("DitherSettings") | |
.setUsage( StaticDrawUsage ); | |
const packedSettings0 = Array.from({length: N}) | |
.map(() => { | |
return new Uniform(new Vector4( | |
initDitherSettings.ditherOffset, | |
initDitherSettings.ditherThreshold, | |
initDitherSettings.resolution.x, | |
initDitherSettings.resolution.y | |
)); | |
}); | |
const packedSettings1 = Array.from({length: N}) | |
.map(() => { | |
return new Uniform(new Vector4( | |
color.r, | |
color.g, | |
color.b, | |
initDitherSettings.invertMask, | |
)); | |
}); | |
// add all uniforms groups | |
this.ditherUniformsGroup.add(packedSettings0); | |
this.ditherUniformsGroup.add(packedSettings1); | |
// ----------------------------------------------------------------------- | |
/*this.uniformsGroups = [ | |
this.ditherUniformsGroup | |
]*/ | |
//console.log(packedSettings1, packedSettings0, maxBatchInstances); | |
} | |
clone(): this { | |
throw new Error(`LodFadeBatchMaterial can't be cloned – needs to be created manually`); | |
} | |
setResolution(batchInstanceId: number, value: Vector2) { | |
const uniform = (this.ditherUniformsGroup.uniforms[0] as Uniform[])[batchInstanceId].value; | |
uniform.z = value.x; | |
uniform.w = value.y; | |
} | |
setDitherThreshold(batchInstanceId: number, value: number) { | |
(this.ditherUniformsGroup.uniforms[0] as Uniform[])[batchInstanceId].value.y = value; | |
} | |
setDitherOffset(batchInstanceId: number, value: number) { | |
(this.ditherUniformsGroup.uniforms[0] as Uniform[])[batchInstanceId].value.x = value; | |
} | |
setInvertMask(batchInstanceId: number, value: boolean) { | |
(this.ditherUniformsGroup.uniforms[0] as Uniform[])[batchInstanceId].value.z = Number(value); | |
//(this.ditherUniformsGroup.uniforms[1] as Uniform[])[batchInstanceId].value.w = Number(value); | |
} | |
setColor(batchInstanceId: number, value: Color) { | |
const uniform = (this.ditherUniformsGroup.uniforms[1] as Uniform[])[batchInstanceId].value; | |
uniform.setX(value.r); | |
uniform.setY(value.g); | |
uniform.setZ(value.b); | |
return; | |
/* | |
//console.log(this.colorUniforms[ batchInstanceId ].value) | |
this.colorUniforms[ batchInstanceId ].value.setX(value.r); | |
this.colorUniforms[ batchInstanceId ].value.setY(value.g); | |
this.colorUniforms[ batchInstanceId ].value.setZ(value.g); | |
//(this.ditherUniformsGroup.uniforms[1] as Uniform[]).forEach(uniform => uniform.value = new Color("red")); | |
this.needsUpdate = true; | |
this.uniformsNeedUpdate = true;*/ | |
//const uniform = (this.ditherUniformsGroup.uniforms[1] as Uniform[])[batchInstanceId].value; | |
// uniform.set(1.0, 0.0, 0.0) // testing, even this doesn't work! | |
/*uniform.x = value.r; | |
uniform.y = value.g; | |
uniform.z = value.b*/ | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment