Files
apt-nl-map/static/Magic4/js/three.js-dev/examples/webgl_materials_envmaps_parallax.html
2024-12-04 10:21:04 +08:00

413 lines
11 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<title>js webgl - box projected cubemap environment mapping</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
<link type="text/css" rel="stylesheet" href="main.css">
</head>
<body>
<div id="container"></div>
<div id="info">
<a href="https://threejs.org" target="_blank" rel="noopener">three.js</a> - box projected cubemap environment mapping. <br> created by <a href="https://codercat.tk" target="_blank" rel="noopener">codercat</a>
</div>
<script type="module">
import * as THREE from '../build/three.module.js';
import { GUI } from './jsm/libs/dat.gui.module.js';
import { OrbitControls } from './jsm/controls/OrbitControls.js';
import { RectAreaLightHelper } from './jsm/helpers/RectAreaLightHelper.js';
import { RectAreaLightUniformsLib } from './jsm/lights/RectAreaLightUniformsLib.js';
// shader injection for box projected cube environment mapping
const worldposReplace = /* glsl */`
#define BOX_PROJECTED_ENV_MAP
#if defined( USE_ENVMAP ) || defined( DISTANCE ) || defined ( USE_SHADOWMAP )
vec4 worldPosition = modelMatrix * vec4( transformed, 1.0 );
#ifdef BOX_PROJECTED_ENV_MAP
vWorldPosition = worldPosition.xyz;
#endif
#endif
`;
const envmapPhysicalParsReplace = /* glsl */`
#if defined( USE_ENVMAP )
#define BOX_PROJECTED_ENV_MAP
#ifdef BOX_PROJECTED_ENV_MAP
uniform vec3 cubeMapSize;
uniform vec3 cubeMapPos;
varying vec3 vWorldPosition;
vec3 parallaxCorrectNormal( vec3 v, vec3 cubeSize, vec3 cubePos ) {
vec3 nDir = normalize( v );
vec3 rbmax = ( .5 * cubeSize + cubePos - vWorldPosition ) / nDir;
vec3 rbmin = ( -.5 * cubeSize + cubePos - vWorldPosition ) / nDir;
vec3 rbminmax;
rbminmax.x = ( nDir.x > 0. ) ? rbmax.x : rbmin.x;
rbminmax.y = ( nDir.y > 0. ) ? rbmax.y : rbmin.y;
rbminmax.z = ( nDir.z > 0. ) ? rbmax.z : rbmin.z;
float correction = min( min( rbminmax.x, rbminmax.y ), rbminmax.z );
vec3 boxIntersection = vWorldPosition + nDir * correction;
return boxIntersection - cubePos;
}
#endif
#ifdef ENVMAP_MODE_REFRACTION
uniform float refractionRatio;
#endif
vec3 getLightProbeIndirectIrradiance( /*const in SpecularLightProbe specularLightProbe,*/ const in GeometricContext geometry, const in int maxMIPLevel ) {
vec3 worldNormal = inverseTransformDirection( geometry.normal, viewMatrix );
#ifdef ENVMAP_TYPE_CUBE
#ifdef BOX_PROJECTED_ENV_MAP
worldNormal = parallaxCorrectNormal( worldNormal, cubeMapSize, cubeMapPos );
#endif
vec3 queryVec = vec3( flipEnvMap * worldNormal.x, worldNormal.yz );
// TODO: replace with properly filtered cubemaps and access the irradiance LOD level, be it the last LOD level
// of a specular cubemap, or just the default level of a specially created irradiance cubemap.
#ifdef TEXTURE_LOD_EXT
vec4 envMapColor = textureCubeLodEXT( envMap, queryVec, float( maxMIPLevel ) );
#else
// force the bias high to get the last LOD level as it is the most blurred.
vec4 envMapColor = textureCube( envMap, queryVec, float( maxMIPLevel ) );
#endif
envMapColor.rgb = envMapTexelToLinear( envMapColor ).rgb;
#elif defined( ENVMAP_TYPE_CUBE_UV )
vec4 envMapColor = textureCubeUV( envMap, worldNormal, 1.0 );
#else
vec4 envMapColor = vec4( 0.0 );
#endif
return PI * envMapColor.rgb * envMapIntensity;
}
// Trowbridge-Reitz distribution to Mip level, following the logic of http://casual-effects.blogspot.ca/2011/08/plausible-environment-lighting-in-two.html
float getSpecularMIPLevel( const in float roughness, const in int maxMIPLevel ) {
float maxMIPLevelScalar = float( maxMIPLevel );
float sigma = PI * roughness * roughness / ( 1.0 + roughness );
float desiredMIPLevel = maxMIPLevelScalar + log2( sigma );
// clamp to allowable LOD ranges.
return clamp( desiredMIPLevel, 0.0, maxMIPLevelScalar );
}
vec3 getLightProbeIndirectRadiance( /*const in SpecularLightProbe specularLightProbe,*/ const in vec3 viewDir, const in vec3 normal, const in float roughness, const in int maxMIPLevel ) {
#ifdef ENVMAP_MODE_REFLECTION
vec3 reflectVec = reflect( -viewDir, normal );
// Mixing the reflection with the normal is more accurate and keeps rough objects from gathering light from behind their tangent plane.
reflectVec = normalize( mix( reflectVec, normal, roughness * roughness) );
#else
vec3 reflectVec = refract( -viewDir, normal, refractionRatio );
#endif
reflectVec = inverseTransformDirection( reflectVec, viewMatrix );
float specularMIPLevel = getSpecularMIPLevel( roughness, maxMIPLevel );
#ifdef ENVMAP_TYPE_CUBE
#ifdef BOX_PROJECTED_ENV_MAP
reflectVec = parallaxCorrectNormal( reflectVec, cubeMapSize, cubeMapPos );
#endif
vec3 queryReflectVec = vec3( flipEnvMap * reflectVec.x, reflectVec.yz );
#ifdef TEXTURE_LOD_EXT
vec4 envMapColor = textureCubeLodEXT( envMap, queryReflectVec, specularMIPLevel );
#else
vec4 envMapColor = textureCube( envMap, queryReflectVec, specularMIPLevel );
#endif
envMapColor.rgb = envMapTexelToLinear( envMapColor ).rgb;
#elif defined( ENVMAP_TYPE_CUBE_UV )
vec4 envMapColor = textureCubeUV( envMap, reflectVec, roughness );
#endif
return envMapColor.rgb * envMapIntensity;
}
#endif
`;
// scene size
const WIDTH = window.innerWidth;
const HEIGHT = window.innerHeight;
// camera
const VIEW_ANGLE = 45;
const ASPECT = WIDTH / HEIGHT;
const NEAR = 1;
const FAR = 800;
let camera, cubeCamera, scene, renderer;
let cameraControls;
let groundPlane, wallMat;
init();
function init() {
const container = document.getElementById( 'container' );
// renderer
renderer = new THREE.WebGLRenderer( { antialias: true } );
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( WIDTH, HEIGHT );
container.appendChild( renderer.domElement );
// gui controls
const gui = new GUI();
const params = {
'box projected': true
};
const bpcemGui = gui.add( params, 'box projected' );
bpcemGui.onChange( function ( value ) {
if ( value ) {
groundPlane.material = boxProjectedMat;
} else {
groundPlane.material = defaultMat;
}
render();
} );
// scene
scene = new THREE.Scene();
// camera
camera = new THREE.PerspectiveCamera( VIEW_ANGLE, ASPECT, NEAR, FAR );
camera.position.set( 280, 106, - 5 );
cameraControls = new OrbitControls( camera, renderer.domElement );
cameraControls.target.set( 0, - 10, 0 );
cameraControls.maxDistance = 400;
cameraControls.minDistance = 10;
cameraControls.addEventListener( 'change', render );
cameraControls.update();
// cube camera for environment map
const cubeRenderTarget = new THREE.WebGLCubeRenderTarget( 512, {
format: THREE.RGBFormat,
generateMipmaps: true,
minFilter: THREE.LinearMipmapLinearFilter
} );
cubeCamera = new THREE.CubeCamera( 1, 1000, cubeRenderTarget );
cubeCamera.position.set( 0, - 100, 0 );
scene.add( cubeCamera );
// ground floor ( with box projected environment mapping )
const loader = new THREE.TextureLoader();
const rMap = loader.load( 'textures/lava/lavatile.jpg' );
rMap.wrapS = THREE.RepeatWrapping;
rMap.wrapT = THREE.RepeatWrapping;
rMap.repeat.set( 2, 1 );
const defaultMat = new THREE.MeshPhysicalMaterial( {
roughness: 1,
envMap: cubeRenderTarget.texture,
roughnessMap: rMap
} );
const boxProjectedMat = new THREE.MeshPhysicalMaterial( {
color: new THREE.Color( '#ffffff' ),
roughness: 1,
envMap: cubeRenderTarget.texture,
roughnessMap: rMap
} );
boxProjectedMat.onBeforeCompile = function ( shader ) {
//these parameters are for the cubeCamera texture
shader.uniforms.cubeMapSize = { value: new THREE.Vector3( 200, 200, 100 ) };
shader.uniforms.cubeMapPos = { value: new THREE.Vector3( 0, - 50, 0 ) };
//replace shader chunks with box projection chunks
shader.vertexShader = 'varying vec3 vWorldPosition;\n' + shader.vertexShader;
shader.vertexShader = shader.vertexShader.replace(
'#include <worldpos_vertex>',
worldposReplace
);
shader.fragmentShader = shader.fragmentShader.replace(
'#include <envmap_physical_pars_fragment>',
envmapPhysicalParsReplace
);
};
groundPlane = new THREE.Mesh( new THREE.PlaneGeometry( 200, 100, 100 ), boxProjectedMat );
groundPlane.rotateX( - Math.PI / 2 );
groundPlane.position.set( 0, - 49, 0 );
scene.add( groundPlane );
// walls
const diffuseTex = loader.load( 'textures/brick_diffuse.jpg', function () {
updateCubeMap();
} );
const bumpTex = loader.load( 'textures/brick_bump.jpg', function () {
updateCubeMap();
} );
wallMat = new THREE.MeshPhysicalMaterial( {
map: diffuseTex,
bumpMap: bumpTex,
bumpScale: 0.3,
} );
const planeGeo = new THREE.PlaneGeometry( 100, 100 );
const planeBack1 = new THREE.Mesh( planeGeo, wallMat );
planeBack1.position.z = - 50;
planeBack1.position.x = - 50;
scene.add( planeBack1 );
const planeBack2 = new THREE.Mesh( planeGeo, wallMat );
planeBack2.position.z = - 50;
planeBack2.position.x = 50;
scene.add( planeBack2 );
const planeFront1 = new THREE.Mesh( planeGeo, wallMat );
planeFront1.position.z = 50;
planeFront1.position.x = - 50;
planeFront1.rotateY( Math.PI );
scene.add( planeFront1 );
const planeFront2 = new THREE.Mesh( planeGeo, wallMat );
planeFront2.position.z = 50;
planeFront2.position.x = 50;
planeFront2.rotateY( Math.PI );
scene.add( planeFront2 );
const planeRight = new THREE.Mesh( planeGeo, wallMat );
planeRight.position.x = 100;
planeRight.rotateY( - Math.PI / 2 );
scene.add( planeRight );
const planeLeft = new THREE.Mesh( planeGeo, wallMat );
planeLeft.position.x = - 100;
planeLeft.rotateY( Math.PI / 2 );
scene.add( planeLeft );
//lights
const width = 50;
const height = 50;
const intensity = 10;
RectAreaLightUniformsLib.init();
const blueRectLight = new THREE.RectAreaLight( 0x9aaeff, intensity, width, height );
blueRectLight.position.set( 99, 5, 0 );
blueRectLight.lookAt( 0, 5, 0 );
scene.add( blueRectLight );
const blueRectLightHelper = new RectAreaLightHelper( blueRectLight );
blueRectLight.add( blueRectLightHelper );
const redRectLight = new THREE.RectAreaLight( 0xf3aaaa, intensity, width, height );
redRectLight.position.set( - 99, 5, 0 );
redRectLight.lookAt( 0, 5, 0 );
scene.add( redRectLight );
const redRectLightHelper = new RectAreaLightHelper( redRectLight );
redRectLight.add( redRectLightHelper );
render();
}
function updateCubeMap() {
//disable specular highlights on walls in the environment map
wallMat.roughness = 1;
groundPlane.visible = false;
cubeCamera.position.copy( groundPlane.position );
cubeCamera.update( renderer, scene );
wallMat.roughness = 0.6;
groundPlane.visible = true;
render();
}
function render() {
renderer.render( scene, camera );
}
</script>
</body>
</html>