Hi,
I am a threejs newbie and have been struggling with this problem for the past few days.
I am trying to improve my online furniture showcaser and wanted to add AO to make it more realsitic.
Sadly I havent gotten the results I was hoping for. The problem is that the shadow cast by the AO is too dark and that its being applied to surfaces that should be free of any shadow. Since I am a newbie I’m not sure if AO even is the best way to approach this or if there is a better alternative.
The only place I want the shadow to be seen is between the doors ( you can see the shadow but it is inconcistent and blurry ) and in the corners.
Edit: had to add the code
<!DOCTYPE html>
<html lang="en">
<head>
<title>three.js webgl - postprocessing - Screen Space Ambient Occlusion</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
<style>
body {
background-color: #FFFFFF;
}
</style>
</head>
<body>
<!-- Import maps polyfill -->
<!-- Remove this when import maps will be widely supported -->
<script async src="https://unpkg.com/es-module-shims@1.3.6/dist/es-module-shims.js"></script>
<script type="importmap">
{
"imports": {
"three": "https://threejs.org/build/three.module.js"
}
}
</script>
<script type="module">
import * as THREE from 'three';
import { GUI } from 'https://threejs.org/examples/jsm/libs/lil-gui.module.min.js';
import { EffectComposer } from 'https://threejs.org/examples/jsm/postprocessing/EffectComposer.js';
import { SSAOPass } from 'https://threejs.org/examples/jsm/postprocessing/SSAOPass.js';
import { FXAAShader } from 'https://threejs.org/examples/jsm/shaders/FXAAShader.js';
import { RenderPass } from 'https://threejs.org/examples/jsm/postprocessing/RenderPass.js';
import { ShaderPass } from 'https://threejs.org/examples/jsm/postprocessing/ShaderPass.js';
import { TAARenderPass } from 'https://threejs.org/examples/jsm/postprocessing/TAARenderPass.js';
import { CopyShader } from 'https://threejs.org/examples/jsm/shaders/CopyShader.js';
import { OrbitControls } from 'https://threejs.org/examples/jsm/controls/OrbitControls.js';
let container;
let camera, scene, renderer;
let composer;
let group;
let controls;
let light;
let fxaaPass;
let taaRenderPass;
let renderPass;
let copyPass;
var settings = { Z:10 };
init();
animate();
function init() {
container = document.createElement( 'div' );
document.body.appendChild( container );
renderer = new THREE.WebGLRenderer( { antialias: true, alpha: true } );
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );
camera = new THREE.PerspectiveCamera( 65, window.innerWidth / window.innerHeight, 10, 7000 );
camera.position.z = 500;
scene = new THREE.Scene();
scene.background = new THREE.Color( 0xffffff );
scene.add( new THREE.DirectionalLight() );
scene.add( new THREE.HemisphereLight( 0xffffff, 0xffffff, 0.5 ) );
group = new THREE.Group();
scene.add( group );
const light2 = new THREE.PointLight( 0xffffff, 0.5 );
light2.position.z = 70;
light2.position.y = - 70;
light2.position.x = - 70;
scene.add( light2 );
light = new THREE.PointLight( 0xffffff, 0.5 );
light.position.z = 70;
light.position.y = - 70;
light.position.x = - 70;
light.position.x = camera.position.x;
light.position.y = camera.position.y;
light.position.z = camera.position.z;
scene.add( light );
const geometry = new THREE.BoxGeometry( 400, 300, 1 );
const material = new THREE.MeshStandardMaterial();
material.shininess = 0;
var imageTexture = THREE.ImageUtils.loadTexture( "https://Schrank-direkt.de/3d/textures/U1191.JPG" );
imageTexture.wrapS = imageTexture.wrapT = THREE.RepeatWrapping;
imageTexture.repeat.set( 0.001, 0.001 );
imageTexture.anisotropy = 16;
// var material = new THREE.MeshPhongMaterial( { map: imageTexture, bumpScale: 1, specular: 0x000000, emissive: 0x303030, shininess: 0.1, shading: THREE.SmoothShading } );
const mesh = new THREE.Mesh( geometry, material );
mesh.position.x = 0;
mesh.position.y = 150;
mesh.position.z = 0;
mesh.rotation.x = 0;
mesh.rotation.y = 0;
mesh.rotation.z = 0;
mesh.scale.x = mesh.scale.y = mesh.scale.z = 1;
group.add( mesh );
const geometry1 = new THREE.BoxGeometry( 200, 300, 1 );
const mesh2L = new THREE.Mesh( geometry1, material );
mesh2L.position.x = 200;
mesh2L.position.y = 150;
mesh2L.position.z = 100;
mesh2L.rotation.x = 0;
mesh2L.rotation.y = Math.PI/2;
mesh2L.rotation.z = 0;
mesh2L.scale.x = mesh2L.scale.y = mesh2L.scale.z = 1;
group.add( mesh2L );
const mesh2R = new THREE.Mesh( geometry1, material );
mesh2R.position.x = -200;
mesh2R.position.y = 150;
mesh2R.position.z = 100;
mesh2R.rotation.x = 0;
mesh2R.rotation.y = Math.PI/2;
mesh2R.rotation.z = 0;
mesh2R.scale.x = mesh2R.scale.y = mesh2R.scale.z = 1;
group.add( mesh2R );
const geometryO = new THREE.BoxGeometry( 400, 200, 1 );
const mesh2O = new THREE.Mesh( geometryO, material );
mesh2O.position.x = 0;
mesh2O.position.y = 300;
mesh2O.position.z = 100;
mesh2O.rotation.x = Math.PI/2;
mesh2O.rotation.y = 0;
mesh2O.rotation.z = 0;
mesh2O.scale.x = mesh2O.scale.y = mesh2O.scale.z = 1;
group.add( mesh2O );
const geometry2 = new THREE.BoxGeometry( 100, 100, 1 );
const mesh3 = new THREE.Mesh( geometry2, material );
mesh3.position.x = -150;
mesh3.position.y = 50;
mesh3.position.z = 200;
group.add( mesh3 );
const mesh4 = new THREE.Mesh( geometry2, material );
mesh4.position.x = -49.5;
mesh4.position.y = 50;
mesh4.position.z = 200;
group.add( mesh4 );
const mesh5 = new THREE.Mesh( geometry2, material );
mesh5.position.x = -49.5;
mesh5.position.y = 150.2;
mesh5.position.z = 200;
group.add( mesh5 );
const mesh6 = new THREE.Mesh( geometry2, material );
mesh6.position.x = -150;
mesh6.position.y = 150.2;
mesh6.position.z = 200;
group.add( mesh6 );
// Ground
// var groundTexture = new THREE.TextureLoader().load( "https://threejs.org/examples/textures/land_ocean_ice_cloud_2048.jpg" );
// var groundTexture = new THREE.TextureLoader().load( "https://schrank-direkt.de/3D/textures/grass.jpg" );
var groundTexture = new THREE.TextureLoader().load( "grass.jpg" );
groundTexture.wrapS = groundTexture.wrapT = THREE.RepeatWrapping;
groundTexture.anisotropy = 16;
groundTexture.repeat.set( 20, 20 );
// var ground = new THREE.Mesh(new THREE.CircleGeometry(2000.0,32), new THREE.MeshBasicMaterial( { map:groundTexture, color:0xff0000} ));
var ground = new THREE.Mesh(new THREE.CircleGeometry(2000.0,32), new THREE.MeshBasicMaterial( { color:0x00A000 } ));
ground.position.y=-2;
ground.rotation.x = -Math.PI / 2;
ground.receiveShadow = true;
scene.add(ground);
for ( let i = 0; i < 0; i ++ ) {
const material = new THREE.MeshLambertMaterial( {
color: Math.random() * 0xffffff
} );
const mesh = new THREE.Mesh( geometry, material );
mesh.position.x = Math.random() * 400 - 200;
mesh.position.y = Math.random() * 400 - 200;
mesh.position.z = Math.random() * 400 - 200;
mesh.rotation.x = Math.random();
mesh.rotation.y = Math.random();
mesh.rotation.z = Math.random();
mesh.scale.setScalar( Math.random() * 10 + 2 );
group.add( mesh );
}
const width = window.innerWidth;
const height = window.innerHeight;
composer = new EffectComposer( renderer );
const ssaoPass = new SSAOPass( scene, camera, width, height );
ssaoPass.kernelRadius = 16;
ssaoPass.minDistance = 0.001;
ssaoPass.maxDistance = 0.03;
composer.addPass( ssaoPass );
taaRenderPass = new TAARenderPass( scene, camera );
taaRenderPass.unbiased = false;
taaRenderPass.accumulate = true;
taaRenderPass.sampleLevel = 4;
taaRenderPass.enabled = true;
composer.addPass( taaRenderPass );
renderPass = new RenderPass( scene, camera );
renderPass.enabled = false;
composer.addPass( renderPass );
copyPass = new ShaderPass( CopyShader );
composer.addPass( copyPass );
controls = new OrbitControls( camera, renderer.domElement );
controls.listenToKeyEvents( window ); // optional
//controls.enableDamping = true; // an animation loop is required when either damping or auto-rotation are enabled
//controls.dampingFactor = 0.05;
controls.screenSpacePanning = true; //false;
// controls.minDistance = 100;
// controls.maxDistance = 500;
// controls.maxPolarAngle = Math.PI / 2;
// Init gui
const gui = new GUI();
gui.add( ssaoPass, 'output', {
'Default': SSAOPass.OUTPUT.Default,
'SSAO Only': SSAOPass.OUTPUT.SSAO,
'SSAO Only + Blur': SSAOPass.OUTPUT.Blur,
'Beauty': SSAOPass.OUTPUT.Beauty,
'Depth': SSAOPass.OUTPUT.Depth,
'Normal': SSAOPass.OUTPUT.Normal
} ).onChange( function ( value ) {
ssaoPass.output = parseInt( value );
} );
gui.add( ssaoPass, 'kernelRadius' ).min( 0 ).max( 64 );
gui.add( ssaoPass, 'kernelSize' ).min( 0 ).max( 64 );
gui.add( ssaoPass, 'minDistance' ).min( 0.0 ).max( 0.1 );
gui.add( ssaoPass, 'maxDistance' ).min( 0.0 ).max( 3 );
gui.add( camera, 'near' ).min( 0.0 ).max( 100 ).onChange( setCameraNear );
gui.add( camera, 'far' ).min( 0.0 ).max( 30000 ).onChange( setCameraFar );
gui.add( settings, 'Z' ).min( 0.0 ).max( 10000 ).onChange( setCameraZ );
window.addEventListener( 'resize', onWindowResize );
}
function setCameraNear( value ){
camera.near = value;
camera.updateProjectionMatrix();
render();
}
function setCameraFar( value ){
camera.far = value;
camera.updateProjectionMatrix();
render();
}
function setCameraZ( value ){
camera.position.z = value;
render();
}
function onWindowResize() {
const width = window.innerWidth;
const height = window.innerHeight;
camera.aspect = width / height;
camera.updateProjectionMatrix();
renderer.setSize( width, height );
composer.setSize( width, height );
}
function animate() {
requestAnimationFrame( animate );
render();
}
function render() {
light.position.x = camera.position.x;
light.position.y = camera.position.y;
light.position.z = camera.position.z;
composer.render();
}
</script>
</body>
</html>