Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/emissive #63

Merged
merged 4 commits into from
Jan 22, 2025
Merged
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
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
"access": "public"
},
"peerDependencies": {
"three": ">=0.170.0"
"three": ">=0.172.0"
},
"devDependencies": {
"@types/three": "^0.172.0",
Expand Down
11 changes: 10 additions & 1 deletion src/lib/helpers/math.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,13 @@ function safeModulo(n: number, m: number): number {
return ((n % m) + m) % m;
}

export { nextPowerOfTwo, safeModulo };
function clamp(x: number, min: number, max: number): number {
if (x < min) {
return min;
} else if (x > max) {
return max;
}
return x;
}

export { clamp, nextPowerOfTwo, safeModulo };
10 changes: 1 addition & 9 deletions src/lib/physics/voxelmap-collisions.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { EVoxelStatus } from '..';
import { clamp } from '../helpers/math';
import * as THREE from '../libs/three-usage';

import { type IVoxelmapCollider } from './i-voxelmap-collider';
Expand Down Expand Up @@ -60,15 +61,6 @@ type EntityCollisionOutput = {
missingVoxels?: THREE.Vector3Like[];
};

function clamp(x: number, min: number, max: number): number {
if (x < min) {
return min;
} else if (x > max) {
return max;
}
return x;
}

function removeVoxelIdDuplicates(list: THREE.Vector3Like[]): THREE.Vector3Like[] {
const map: Record<string, THREE.Vector3Like> = {};
for (const voxelId of list) {
Expand Down
1 change: 1 addition & 0 deletions src/lib/terrain/voxelmap/i-voxelmap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ type Color = {
interface IVoxelMaterial {
readonly color: Color;
readonly shininess?: number;
readonly emissiveness?: number;
}

type VoxelsChunkSize = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,7 @@ float computeNoise() {
struct VoxelMaterial {
vec3 color;
float shininess;
vec3 emissive;
};
VoxelMaterial getVoxelMaterial(const vec3 modelNormal) {
Expand All @@ -240,6 +241,7 @@ VoxelMaterial getVoxelMaterial(const vec3 modelNormal) {
if (uDisplayMode == ${EVoxelsDisplayMode.NORMALS}u) {
voxelMaterial.color = 0.5 + 0.5 * modelNormal;
voxelMaterial.shininess = 0.0;
voxelMaterial.emissive = vec3(0);
} else {
float noise = 0.0;
#ifdef ${cstVoxelNoise}
Expand All @@ -250,11 +252,16 @@ VoxelMaterial getVoxelMaterial(const vec3 modelNormal) {
ivec2 texelCoords = ivec2(voxelMaterialId % ${this.texture.image.width}u, voxelMaterialId / ${this.texture.image.width}u);
vec4 fetchedTexel = texelFetch(uTexture, texelCoords, 0);
voxelMaterial.color = fetchedTexel.rgb + noise;
voxelMaterial.shininess = uShininessStrength * ${VoxelsRenderableFactoryBase.maxShininess.toFixed(1)} * fetchedTexel.a * (1.0 + 10.0 * noise);
float emissive = step(0.5, fetchedTexel.a) * (2.0 * fetchedTexel.a - 1.0);
voxelMaterial.shininess = 0.0001 + step(fetchedTexel.a, 0.5) * uShininessStrength * ${VoxelsRenderableFactoryBase.maxShininess.toFixed(1)} * 2.0 * fetchedTexel.a * (1.0 + 10.0 * noise);
voxelMaterial.emissive = emissive * voxelMaterial.color;
voxelMaterial.color *= (1.0 - emissive);
}
if (uDisplayMode == ${EVoxelsDisplayMode.GREY}u) {
voxelMaterial.color = vec3(0.75);
voxelMaterial.emissive = vec3(0);
}
return voxelMaterial;
Expand Down Expand Up @@ -290,6 +297,9 @@ void main() {
'#include <lights_phong_fragment>': `
#include <lights_phong_fragment>
material.specularShininess = voxelMaterial.shininess;
`,
'#include <emissivemap_fragment>': `
totalEmissiveRadiance = voxelMaterial.emissive;
`,
});
};
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { nextPowerOfTwo } from '../../../../helpers/math';
import { clamp, nextPowerOfTwo } from '../../../../helpers/math';
import { vec3ToString } from '../../../../helpers/string';
import { type PackedUintFragment } from '../../../../helpers/uint-packing';
import * as THREE from '../../../../libs/three-usage';
Expand Down Expand Up @@ -167,8 +167,25 @@ abstract class VoxelsRenderableFactoryBase {
textureData[4 * materialId + 1] = 255 * material.color.g;
textureData[4 * materialId + 2] = 255 * material.color.b;
const shininess = material.shininess ?? 0;
// shininess cannot be 0 or it creates visual artifacts. Clamp it.
textureData[4 * materialId + 3] = Math.max(1, (255 * shininess) / VoxelsRenderableFactoryBase.maxShininess);
const emissiveness = material.emissiveness ?? 0;

if (shininess < 0) {
throw new Error(`A material cannot have negative shininess.`);
}
if (emissiveness < 0) {
throw new Error(`A material cannot have negative emissiveness.`);
}
if (emissiveness > 0 && shininess > 0) {
throw new Error(`A material cannot both have shininess and emissiveness`);
}

if (emissiveness > 0) {
// store emissiveness
textureData[4 * materialId + 3] = 128 + clamp(127 * emissiveness, 0, 127);
} else {
// store shininess
textureData[4 * materialId + 3] = clamp((127 * shininess) / VoxelsRenderableFactoryBase.maxShininess, 0, 127);
}
});
const texture = new THREE.DataTexture(textureData, textureWidth, textureHeight);
texture.needsUpdate = true;
Expand Down
8 changes: 7 additions & 1 deletion src/test/map/color-mapping.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,13 @@ class ColorMapping {
for (leveled.g = 0; leveled.g < this.valuesCountPerChannel; leveled.g++) {
for (leveled.r = 0; leveled.r < this.valuesCountPerChannel; leveled.r++) {
const color = this.buildColorFromLeveled(leveled);
this.materialsList.push({ color, shininess: 200 * Math.random() });

const isEmissive = Math.random() > 0.7;
this.materialsList.push({
color,
shininess: isEmissive ? 0 : 200 * Math.random(),
emissiveness: isEmissive ? 0.5 : 0,
});
}
}
}
Expand Down
5 changes: 5 additions & 0 deletions src/test/test-terrain-base.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import * as THREE from 'three-usage-test';
import GUI from 'lil-gui';

import { type IHeightmap, type IHeightmapSample, type IVoxelMap, type TerrainViewer } from '../lib';

Expand All @@ -19,6 +20,8 @@ abstract class TestTerrainBase extends TestBase {
readonly visibilityFrustum: THREE.Frustum;
} = null;

protected readonly gui = new GUI();

public constructor(voxelMap: IHeightmap & IVoxelMap & ITerrainMap) {
super();

Expand Down Expand Up @@ -121,9 +124,11 @@ abstract class TestTerrainBase extends TestBase {
dirLight.target.position.set(0, 0, 0);
dirLight.position.set(100, 50, 100);
this.scene.add(dirLight);
this.gui.add(dirLight, 'intensity', 0, 3).name('Directional light');

const ambientLight = new THREE.AmbientLight(0xffffff);
this.scene.add(ambientLight);
this.gui.add(ambientLight, 'intensity', 0, 3).name('Ambient light');

if (this.enableShadows) {
const planeReceivingShadows = new THREE.Mesh(new THREE.PlaneGeometry(200, 200), new THREE.MeshPhongMaterial());
Expand Down
4 changes: 1 addition & 3 deletions src/test/test-terrain.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import GUI from 'lil-gui';
import * as THREE from 'three-usage-test';

import {
Expand Down Expand Up @@ -173,15 +172,14 @@ return vec4(sampled.rgb / sampled.a, 1);
});
this.promisesQueue = new PromisesQueue(this.voxelmapViewer.maxPatchesComputedInParallel + 5);

const gui = new GUI();
{
const texturingOptions = {
textured: EVoxelsDisplayMode.TEXTURED,
normals: EVoxelsDisplayMode.NORMALS,
grey: EVoxelsDisplayMode.GREY,
};

const voxelsFolder = gui.addFolder('Voxels');
const voxelsFolder = this.gui.addFolder('Voxels');
voxelsFolder.open();
const parameters = {
shadows: this.enableShadows,
Expand Down
Loading