On three.js, I have difficulty finding the best solution to render a metallic material. I tried several methods including these: https://youtu.be/aJun0Q0CG_A?feature=shared and also the one used here: https://github.com/mrdoob/three.js/blob/master/examples/webgl_materials_physical_clearcoat.html.
However, I find that the metallic appearance is not very realistic.
Perhaps the error comes from the positioning and intensity of the light points that I use or from some parameters to adjust.
Here is how my code is configured:
import * as THREE from 'three';
import { OrbitControls } from './jsm/controls/OrbitControls.js';
import { FlakesTexture } from './jsm/textures/FlakesTexture.js';
import { RGBELoader } from './jsm/loaders/RGBELoader.js';
let scene, camera, renderer, controls;
let pointLight, pointLight2, pointLight3;
let numCubesX = 20;
let numCubesY = 10;
let cubeSize=1;
let wallWidth = numCubesX; // Largeur du mur (axe X)
let wallHeight = 5; // Hauteur du mur (axe Y)
const cubes = [];
let roomPlane;
let colorfloor;
let weight;
let pondfloorrarity=0.2;
let pondcubesrarity=0.4;
let pondpaletterarity=0.4;
let resol = 4;
function init() {
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.01, 10000);
camera.position.set(numCubesX / 2, numCubesY / 1.5, 15);
camera.outputEncoding = THREE.LinearToneMapping;
renderer = new THREE.WebGLRenderer({ antialias: true },{ alpha: true });
renderer.shadowMap.enabled = true;
renderer.physicallyCorrectLights = true;
renderer.toneMapping=THREE.ACESFilmicToneMapping;
renderer.toneMappingExposure=0.5;
renderer.outputColorSpace=THREE.LinearSRGBColorSpace;
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
//document.body.appendChild(renderer.domElement);
controls = new OrbitControls(camera, renderer.domElement);
pointLight = new THREE.PointLight(0xffffff, 9000, 100);
pointLight.castShadow = true;
pointLight.position.set(0-60, numCubesY/2, 40);
scene.add(pointLight);
pointLight2 = new THREE.PointLight(0xffffff, 9000, 100);
pointLight2.castShadow = true;
pointLight2.position.set(numCubesX+60, numCubesY/2, 40);
scene.add(pointLight2);
pointLight3 = new THREE.PointLight(0xffffff, 7000, 100);
pointLight3.castShadow = true;
pointLight3.position.set(numCubesX/2, numCubesY*1.5, 3);
scene.add(pointLight3);
pointLight.shadow.mapSize.width = 4096; // Nouvelle résolution de l'ombre
pointLight.shadow.mapSize.height = 4096; // Nouvelle résolution de l'ombre
pointLight2.shadow.mapSize.width = 4096; // Nouvelle résolution de l'ombre
pointLight2.shadow.mapSize.height = 4096; // Nouvelle résolution de l'ombre
pointLight3.shadow.mapSize.width = 4096; // Nouvelle résolution de l'ombre
pointLight3.shadow.mapSize.height = 4096; // Nouvelle résolution de l'ombre
}
// Créer les cubes
function createCubes() {
function hexToCSSColor(hex) {
let r = (hex >> 16) & 255;
let g = (hex >> 8) & 255;
let b = hex & 255;
return `rgb(${r}, ${g}, ${b})`;
}
let palettes = [
[0xd4af37, 0xe5e4e2, 0xcd7f32],
[0xabcdef, 0x123456, 0x789abc],
].map(palette => palette.map(hexToCSSColor));
// Définir les poids pour chaque palette
let weights = [
[1, 3, 6],
[1, 3, 6],
];
let paletteWeights = [3, 1];
let colorWeights = {}; // Ajoutez cette ligne pour stocker les poids des couleurs
let cubeCounts = {}; // Ajoutez cette ligne pour stocker le nombre de cubes de chaque couleur
let floorRarity = 0; // Ajoutez cette ligne pour stocker la rareté du sol
// Créer un tableau pondéré pour chaque palette
let weightedPalettes = palettes.map((palette, i) => {
let weightedPalette = [];
palette.forEach((color, j) => {
weight = weights[i][j];
colorWeights[color] = weight; // Stocker le poids de la couleur
for (let k = 0; k < weight; k++) {
weightedPalette.push(color);
}
});
return weightedPalette;
});
// Créer un tableau pondéré pour les indices de palette
let weightedPaletteIndices = [];
paletteWeights.forEach((weight, i) => {
for (let j = 0; j < weight; j++) {
weightedPaletteIndices.push(i);
}
});
// Choisir un indice de palette pondéré aléatoirement
let weightedPaletteIndex = weightedPaletteIndices[Math.floor(Math.random() * weightedPaletteIndices.length)];
let weightedPalette = weightedPalettes[weightedPaletteIndex];
let paletteRarity = paletteWeights[weightedPaletteIndex]; // Ajoutez cette ligne pour calculer la rareté de la palette
let envmaploader=new THREE.PMREMGenerator(renderer);
//new RGBELoader().setPath('./').load('venice_sunset_4k.hdr', function (hdrmap){
//new RGBELoader().setPath('./').load('kloofendal_43d_clear_puresky_4k.hdr', function (hdrmap){
//new RGBELoader().setPath('./').load('rooitou_park_4k.hdr', function (hdrmap){
//new RGBELoader().setPath('./').load('mealie_road_4k.hdr', function (hdrmap){
//new RGBELoader().setPath('./').load('quarry_01_4k.hdr', function (hdrmap){
//new RGBELoader().setPath('./').load('blinds_4k.hdr', function (hdrmap){
new RGBELoader().setPath('./').load('autumn_crossing_4k.hdr', function (hdrmap){
//new RGBELoader().setPath('./').load('aloe_farm_shade_house_4k.hdr', function (hdrmap){
//new RGBELoader().setPath('./').load('industrial_workshop_foundry_4k.hdr', function (hdrmap){
//new RGBELoader().setPath('./').load('satara_night_no_lamps_4k.hdr', function (hdrmap){
//new RGBELoader().setPath('./').load('trekker_monument_4k.hdr', function (hdrmap){
//new RGBELoader().setPath('./').load('pine_attic_4k.hdr', function (hdrmap){
//new RGBELoader().setPath('./').load('stadium_01_4k.hdr', function (hdrmap){
//new RGBELoader().setPath('./').load('mpumalanga_veld_puresky_4k.hdr', function (hdrmap){
//new RGBELoader().setPath('./').load('kiara_1_dawn_4k.hdr', function (hdrmap){
//new RGBELoader().setPath('./').load('pink_sunrise_4k.hdr', function (hdrmap){
//new RGBELoader().setPath('./').load('harties_4k.hdr', function (hdrmap){
//new RGBELoader().setPath('./').load('floral_tent_4k.hdr', function (hdrmap){
let envmap=envmaploader.fromCubemap(hdrmap);
let texture = new THREE.CanvasTexture(new FlakesTexture());
texture.wrapS = THREE.RepeatWrapping;
texture.wrapT = THREE.RepeatWrapping;
texture.repeat.x=200;
texture.repeat.y=50;
colorfloor = weightedPalette[Math.floor(Math.random() * weightedPalette.length)];
floorRarity = colorWeights[colorfloor]; // Calculez la rareté du sol
const floormaterial = {
clearcoat: 1,
clearcoatRoughness: 0,
metalness: 0.9,
roughness: 0,
color: colorfloor,
normalMap: texture,
normalScale: new THREE.Vector2(0.01, 0.01),
envMap: envmap.texture,
}
let floor_material = new THREE.MeshPhysicalMaterial(floormaterial);
const roomPlaneGeometry = new THREE.PlaneGeometry(wallWidth, 5*wallHeight);
roomPlane = new THREE.Mesh(roomPlaneGeometry, floor_material);
roomPlane.rotation.x = -Math.PI / 2; // Pour le placer horizontalement
roomPlane.position.set(numCubesX / 2, -cubeSize/2, 2.5*wallHeight); // Positionnez le plan au sol derrière la caméra
roomPlane.receiveShadow = true;
roomPlane.castShadow = true;
scene.add(roomPlane);
for (let i = 0; i < numCubesX; i++) {
for (let j = 0; j < numCubesY; j++) {
const cubeHeight = Math.random() * 2; // Longueur Z aléatoire pour l'effet de relief
let color = weightedPalette[Math.floor(Math.random() * weightedPalette.length)];
cubeCounts[color] = (cubeCounts[color] || 0) + 1; // Incrémentez le nombre de cubes de cette couleur
const cubeMaterial = {
clearcoat: 1,
clearcoatRoughness: 0,
metalness: 0.9,
roughness: 0,
color: color,
normalMap: texture,
normalScale: new THREE.Vector2(0.01, 0.01),
envMap: envmap.texture,
}
let cubeGeometry = new THREE.BoxGeometry(0.9, 0.9, cubeHeight);
let material = new THREE.MeshPhysicalMaterial(cubeMaterial);
let cube = new THREE.Mesh(cubeGeometry, material);
cube.position.set(i, j, cubeHeight / 2);
cube.receiveShadow = true;
cube.castShadow = true;
scene.add(cube);
cubes.push(cube); // Ajoutez le cube au tableau
}
}
// Calculez la rareté totale des cubes
let cubeRarity = 0;
for (let color in cubeCounts) {
cubeRarity += cubeCounts[color] * colorWeights[color];
}
// Calculez la rareté de la scène
let sceneRarity = (pondcubesrarity*cubeRarity) * (pondfloorrarity*floorRarity)* (pondpaletterarity*paletteRarity);
// Affichez la rareté de la scène
console.log('Rareté des cubes : ' + cubeRarity);
console.log('Rareté de la palette : ' + paletteRarity);
console.log('Rareté du sol : ' + floorRarity);
console.log('Rareté de la scène : ' + sceneRarity);
})
}
let animationId;
function animate() {
animationId=requestAnimationFrame(animate);
camera.lookAt(new THREE.Vector3(numCubesX / 2, numCubesY / 2,0));
renderer.render(scene, camera);
//composer.render(); // Remplacez 'renderer.render(scene, camera);' par ceci
}
function captureImage() {
// Sauvegarder la taille d'origine
let originalSize = new THREE.Vector2();
renderer.getSize(originalSize);
// Définir la taille du rendu à une valeur plus élevée
let highResSize = originalSize.clone().multiplyScalar(resol); // 2 fois la taille d'origine
renderer.setSize(highResSize.x, highResSize.y, true);
renderer.domElement.style.width = `${highResSize.x}px`;
renderer.domElement.style.height = `${highResSize.y}px`;
// Attendre que la scène soit rendue à la nouvelle résolution
requestAnimationFrame(() => {
// Capturer une image de la scène
let dataURL = renderer.domElement.toDataURL('image/png');
// Créer une nouvelle image et l'ajouter au document
let img = new Image();
img.style.width = `${originalSize.x}px`;
img.style.height = `${originalSize.y}px`;
img.src = dataURL;
document.body.appendChild(img);
// Réinitialiser la taille du rendu à sa valeur d'origine
renderer.setSize(originalSize.x, originalSize.y, true);
renderer.domElement.style.width = `${originalSize.x}px`;
renderer.domElement.style.height = `${originalSize.y}px`;
cancelAnimationFrame(animationId);
});
}
init();
createCubes();
animate();
// Attendre une seconde, puis capturer une image de la scène
setTimeout(captureImage, 1000);
Don't pay attention to the //, I've tried so many solutions that my code can turn out to be a bit messy.
Does anyone have a solution to get the best possible results with three.js?
Here's a photo of what I currently get when I use an hdr image with RGBELoader...enter image description here
I would like to have a rendering so that we can believe that it is gold, silver, or other precious material.