#javascript #three.js #3d
#javascript #three.js #3D
Вопрос:
Я создал эту вращающуюся книгу, используя THREE.js . К сожалению, у меня очень мало знаний об OpenGL, и поэтому я не могу понять, как сделать затухающее отражение.
window.onload = function() {
// Create the renderer and add it to the page's body element
var renderer = new THREE.WebGLRenderer({ alpha: true, antialias: true });
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.shadowMap.enabled = true;
document.body.appendChild(renderer.domElement);
// Create the scene to hold the object
var scene = new THREE.Scene();
// Create the camera
var camera = new THREE.PerspectiveCamera(
35, // Field of view
window.innerWidth / window.innerHeight, // Aspect ratio
0.1, // Near plane distance
1000 // Far plane distance
);
// Position the camera
camera.position.set(-15, 10, 20);
camera.lookAt(scene.position);
// Add the lights
var light = new THREE.PointLight(0xffffff, 0.4);
light.position.set(10, 10, 10);
scene.add(light);
ambientLight = new THREE.AmbientLight(0xbbbbbb);
scene.add(ambientLight);
// Load the textures (book images)
var textureLoader = new THREE.TextureLoader();
//front
var bookCoverTexture = textureLoader.load("https://i.imgur.com/sBzG9bm.jpeg");
//side
var bookSpineTexture = textureLoader.load("https://i.imgur.com/ZFgZgnx.png");
//back
var bookBackTexture = textureLoader.load("https://i.imgur.com/s4Kbbr9.jpeg");
//pages
var bookPagesTexture = textureLoader.load("https://i.imgur.com/2Mul6cU.jpeg");
//top bottom pages
var bookPagesTopBottomTexture = textureLoader.load(
"https://i.imgur.com/OXCKChN.jpg"
);
// Use the linear filter for the textures to avoid blurriness
bookCoverTexture.minFilter = bookSpineTexture.minFilter = bookBackTexture.minFilter = bookPagesTexture.minFilter = bookPagesTopBottomTexture.minFilter =
THREE.LinearFilter;
// Create the materials
var bookCover = new THREE.MeshLambertMaterial({
color: 0xffffff,
map: bookCoverTexture
});
var bookSpine = new THREE.MeshLambertMaterial({
color: 0xffffff,
map: bookSpineTexture
});
var bookBack = new THREE.MeshLambertMaterial({
color: 0xffffff,
map: bookBackTexture
});
var bookPages = new THREE.MeshLambertMaterial({
color: 0xffffff,
map: bookPagesTexture
});
var bookPagesTopBottom = new THREE.MeshLambertMaterial({
color: 0xffffff,
map: bookPagesTopBottomTexture
});
var materials = [
bookPages, // Right side
bookSpine, // Left side
bookPagesTopBottom, // Top side
bookPagesTopBottom, // Bottom side
bookCover, // Front side
bookBack // Back side
];
// Create the book and add it to the scene
var book = new THREE.Mesh(
new THREE.BoxGeometry(7, 10, 2.2, 4, 4, 1),
materials
);
book.layers.enable(1);
scene.add(book);
var geometry = new THREE.PlaneBufferGeometry(20, 20);
var mirror = new THREE.Reflector(geometry, {
clipBias: 0.003,
textureWidth: 1024 * window.devicePixelRatio,
textureHeight: 1024 * window.devicePixelRatio,
color: 0x889999,
recursion: 1
});
mirror.rotateX(-Math.PI / 2);
mirror.position.y = -5.2;
mirror.material.transparent = true;
mirror.material.alpha = 0.1;
scene.add(mirror);
// Create the orbit controls for the camera
controls = new THREE.OrbitControls(camera, renderer.domElement);
controls.enableDamping = true;
controls.dampingFactor = 0.25;
controls.enablePan = false;
controls.enableZoom = false;
// Begin the animation
animate();
/*
Animate a frame
*/
function animate() {
book.rotation.y = 0.01;
// Update the orbit controls
controls.update();
// Render the frame
renderer.render(scene, camera);
// Keep the animation going
requestAnimationFrame(animate);
}
};
<script src="https://unpkg.com/three@0.111.0/build/three.js"></script>
<script src="https://unpkg.com/three@0.111.0/examples/js/controls/OrbitControls.js"></script>
<script src="https://unpkg.com/three@0.111.0/examples/js/objects/Reflector.js"></script>
<div style="padding: 20px; position: absolute;">
</div>
После некоторых исследований я наткнулся на этот пост для создания затухающего отражения, но я не могу сделать это самостоятельно.
https://discourse.threejs.org/t/creating-a-fading-reflection/3831
Был бы признателен за небольшую помощь.
Ответ №1:
Я обновил ваш код модифицированной версией THREE.Reflector
с three.js
форума.
window.onload = function() {
// Create the renderer and add it to the page's body element
var renderer = new THREE.WebGLRenderer({ alpha: true, antialias: true });
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.shadowMap.enabled = true;
document.body.appendChild(renderer.domElement);
// Create the scene to hold the object
var scene = new THREE.Scene();
// Create the camera
var camera = new THREE.PerspectiveCamera(
35, // Field of view
window.innerWidth / window.innerHeight, // Aspect ratio
0.1, // Near plane distance
1000 // Far plane distance
);
// Position the camera
camera.position.set(-15, 10, 20);
camera.lookAt(scene.position);
// Add the lights
var light = new THREE.PointLight(0xffffff, 0.4);
light.position.set(10, 10, 10);
scene.add(light);
ambientLight = new THREE.AmbientLight(0xbbbbbb);
scene.add(ambientLight);
// Load the textures (book images)
var textureLoader = new THREE.TextureLoader();
//front
var bookCoverTexture = textureLoader.load("https://i.imgur.com/sBzG9bm.jpeg");
//side
var bookSpineTexture = textureLoader.load("https://i.imgur.com/ZFgZgnx.png");
//back
var bookBackTexture = textureLoader.load("https://i.imgur.com/s4Kbbr9.jpeg");
//pages
var bookPagesTexture = textureLoader.load("https://i.imgur.com/2Mul6cU.jpeg");
//top bottom pages
var bookPagesTopBottomTexture = textureLoader.load(
"https://i.imgur.com/OXCKChN.jpg"
);
// Use the linear filter for the textures to avoid blurriness
bookCoverTexture.minFilter = bookSpineTexture.minFilter = bookBackTexture.minFilter = bookPagesTexture.minFilter = bookPagesTopBottomTexture.minFilter =
THREE.LinearFilter;
// Create the materials
var bookCover = new THREE.MeshLambertMaterial({
color: 0xffffff,
map: bookCoverTexture
});
var bookSpine = new THREE.MeshLambertMaterial({
color: 0xffffff,
map: bookSpineTexture
});
var bookBack = new THREE.MeshLambertMaterial({
color: 0xffffff,
map: bookBackTexture
});
var bookPages = new THREE.MeshLambertMaterial({
color: 0xffffff,
map: bookPagesTexture
});
var bookPagesTopBottom = new THREE.MeshLambertMaterial({
color: 0xffffff,
map: bookPagesTopBottomTexture
});
var materials = [
bookPages, // Right side
bookSpine, // Left side
bookPagesTopBottom, // Top side
bookPagesTopBottom, // Bottom side
bookCover, // Front side
bookBack // Back side
];
// Create the book and add it to the scene
var book = new THREE.Mesh(
new THREE.BoxGeometry(7, 10, 2.2, 4, 4, 1),
materials
);
book.layers.enable(1);
scene.add(book);
var geometry = new THREE.PlaneBufferGeometry(20, 20);
var mirror = new THREE.Reflector(geometry, {
clipBias: 0.003,
textureWidth: 1024 * window.devicePixelRatio,
textureHeight: 1024 * window.devicePixelRatio,
color: 0x889999,
recursion: 1
});
mirror.rotateX(-Math.PI / 2);
mirror.position.y = -5.2;
mirror.material.transparent = true;
mirror.material.alpha = 0.1;
scene.add(mirror);
// Create the orbit controls for the camera
controls = new THREE.OrbitControls(camera, renderer.domElement);
controls.enableDamping = true;
controls.dampingFactor = 0.25;
controls.enablePan = false;
controls.enableZoom = false;
// Begin the animation
animate();
/*
Animate a frame
*/
function animate() {
book.rotation.y = 0.01;
// Update the orbit controls
controls.update();
// Render the frame
renderer.render(scene, camera);
// Keep the animation going
requestAnimationFrame(animate);
}
};
<script src="https://unpkg.com/three@0.116.1/build/three.js"></script>
<script src="https://unpkg.com/three@0.116.1/examples/js/controls/OrbitControls.js"></script>
<script>
THREE.Reflector = function ( geometry, options ) {
THREE.Mesh.call( this, geometry );
this.type = 'Reflector';
var scope = this;
options = options || {};
var color = ( options.color !== undefined ) ? new THREE.Color( options.color ) : new THREE.Color( 0x7F7F7F );
var textureWidth = options.textureWidth || 512;
var textureHeight = options.textureHeight || 512;
var clipBias = options.clipBias || 0;
var shader = options.shader || THREE.Reflector.ReflectorShader;
//
var reflectorPlane = new THREE.Plane();
var normal = new THREE.Vector3();
var reflectorWorldPosition = new THREE.Vector3();
var cameraWorldPosition = new THREE.Vector3();
var rotationMatrix = new THREE.Matrix4();
var lookAtPosition = new THREE.Vector3( 0, 0, - 1 );
var clipPlane = new THREE.Vector4();
var viewport = new THREE.Vector4();
var view = new THREE.Vector3();
var target = new THREE.Vector3();
var q = new THREE.Vector4();
var textureMatrix = new THREE.Matrix4();
var virtualCamera = new THREE.PerspectiveCamera();
var parameters = {
minFilter: THREE.LinearFilter,
magFilter: THREE.LinearFilter,
format: THREE.RGBFormat,
stencilBuffer: false
};
var renderTarget = new THREE.WebGLRenderTarget( textureWidth, textureHeight, parameters );
renderTarget.depthBuffer = true;
renderTarget.depthTexture = new THREE.DepthTexture();
renderTarget.depthTexture.type = THREE.UnsignedShortType;
if ( ! THREE.Math.isPowerOfTwo( textureWidth ) || ! THREE.Math.isPowerOfTwo( textureHeight ) ) {
renderTarget.texture.generateMipmaps = false;
}
var material = new THREE.ShaderMaterial( {
uniforms: THREE.UniformsUtils.clone( shader.uniforms ),
fragmentShader: shader.fragmentShader,
vertexShader: shader.vertexShader,
transparent: true
} );
material.uniforms.tDiffuse.value = renderTarget.texture;
material.uniforms.tDepth.value = renderTarget.depthTexture;
material.uniforms.color.value = color;
material.uniforms.textureMatrix.value = textureMatrix;
this.material = material;
this.onBeforeRender = function ( renderer, scene, camera ) {
reflectorWorldPosition.setFromMatrixPosition( scope.matrixWorld );
cameraWorldPosition.setFromMatrixPosition( camera.matrixWorld );
rotationMatrix.extractRotation( scope.matrixWorld );
normal.set( 0, 0, 1 );
normal.applyMatrix4( rotationMatrix );
view.subVectors( reflectorWorldPosition, cameraWorldPosition );
// Avoid rendering when reflector is facing away
if ( view.dot( normal ) > 0 ) return;
view.reflect( normal ).negate();
view.add( reflectorWorldPosition );
rotationMatrix.extractRotation( camera.matrixWorld );
lookAtPosition.set( 0, 0, - 1 );
lookAtPosition.applyMatrix4( rotationMatrix );
lookAtPosition.add( cameraWorldPosition );
target.subVectors( reflectorWorldPosition, lookAtPosition );
target.reflect( normal ).negate();
target.add( reflectorWorldPosition );
virtualCamera.position.copy( view );
virtualCamera.up.set( 0, 1, 0 );
virtualCamera.up.applyMatrix4( rotationMatrix );
virtualCamera.up.reflect( normal );
virtualCamera.lookAt( target );
virtualCamera.far = camera.far; // Used in WebGLBackground
virtualCamera.updateMatrixWorld();
virtualCamera.projectionMatrix.copy( camera.projectionMatrix );
this.material.uniforms.cameraNear.value = camera.near;
this.material.uniforms.cameraFar.value = camera.far;
// Update the texture matrix
textureMatrix.set(
0.5, 0.0, 0.0, 0.5,
0.0, 0.5, 0.0, 0.5,
0.0, 0.0, 0.5, 0.5,
0.0, 0.0, 0.0, 1.0
);
textureMatrix.multiply( virtualCamera.projectionMatrix );
textureMatrix.multiply( virtualCamera.matrixWorldInverse );
textureMatrix.multiply( scope.matrixWorld );
// Now update projection matrix with new clip plane, implementing code from: http://www.terathon.com/code/oblique.html
// Paper explaining this technique: http://www.terathon.com/lengyel/Lengyel-Oblique.pdf
reflectorPlane.setFromNormalAndCoplanarPoint( normal, reflectorWorldPosition );
reflectorPlane.applyMatrix4( virtualCamera.matrixWorldInverse );
clipPlane.set( reflectorPlane.normal.x, reflectorPlane.normal.y, reflectorPlane.normal.z, reflectorPlane.constant );
var projectionMatrix = virtualCamera.projectionMatrix;
q.x = ( Math.sign( clipPlane.x ) projectionMatrix.elements[ 8 ] ) / projectionMatrix.elements[ 0 ];
q.y = ( Math.sign( clipPlane.y ) projectionMatrix.elements[ 9 ] ) / projectionMatrix.elements[ 5 ];
q.z = - 1.0;
q.w = ( 1.0 projectionMatrix.elements[ 10 ] ) / projectionMatrix.elements[ 14 ];
// Calculate the scaled plane vector
clipPlane.multiplyScalar( 2.0 / clipPlane.dot( q ) );
// Replacing the third row of the projection matrix
projectionMatrix.elements[ 2 ] = clipPlane.x;
projectionMatrix.elements[ 6 ] = clipPlane.y;
projectionMatrix.elements[ 10 ] = clipPlane.z 1.0 - clipBias;
projectionMatrix.elements[ 14 ] = clipPlane.w;
// Render
renderTarget.texture.encoding = renderer.outputEncoding;
scope.visible = false;
var currentRenderTarget = renderer.getRenderTarget();
var currentXrEnabled = renderer.xr.enabled;
var currentShadowAutoUpdate = renderer.shadowMap.autoUpdate;
renderer.xr.enabled = false; // Avoid camera modification
renderer.shadowMap.autoUpdate = false; // Avoid re-computing shadows
renderer.setRenderTarget( renderTarget );
renderer.state.buffers.depth.setMask( true ); // make sure the depth buffer is writable so it can be properly cleared, see #18897
if ( renderer.autoClear === false ) renderer.clear();
renderer.render( scene, virtualCamera );
renderer.xr.enabled = currentXrEnabled;
renderer.shadowMap.autoUpdate = currentShadowAutoUpdate;
renderer.setRenderTarget( currentRenderTarget );
// Restore viewport
var bounds = camera.bounds;
if ( bounds !== undefined ) {
var size = renderer.getSize();
var pixelRatio = renderer.getPixelRatio();
viewport.x = bounds.x * size.width * pixelRatio;
viewport.y = bounds.y * size.height * pixelRatio;
viewport.z = bounds.z * size.width * pixelRatio;
viewport.w = bounds.w * size.height * pixelRatio;
renderer.state.viewport( viewport );
}
scope.visible = true;
};
this.getRenderTarget = function () {
return renderTarget;
};
};
THREE.Reflector.prototype = Object.create( THREE.Mesh.prototype );
THREE.Reflector.prototype.constructor = THREE.Reflector;
THREE.Reflector.ReflectorShader = {
uniforms: {
'color': {
type: 'c',
value: null
},
'tDiffuse': {
type: 't',
value: null
},
'tDepth': {
type: 't',
value: null
},
'textureMatrix': {
type: 'm4',
value: null
},
'cameraNear': {
type: 'f',
value: 0
},
'cameraFar': {
type: 'f',
value: 0
},
},
vertexShader: [
'uniform mat4 textureMatrix;',
'varying vec4 vUv;',
'void main() {',
' vUv = textureMatrix * vec4( position, 1.0 );',
' gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );',
'}'
].join( 'n' ),
fragmentShader: [
'#include <packing>',
'uniform vec3 color;',
'uniform sampler2D tDiffuse;',
'uniform sampler2D tDepth;',
'uniform float cameraNear;',
'uniform float cameraFar;',
'varying vec4 vUv;',
'float blendOverlay( float base, float blend ) {',
' return( base < 0.5 ? ( 2.0 * base * blend ) : ( 1.0 - 2.0 * ( 1.0 - base ) * ( 1.0 - blend ) ) );',
'}',
'vec3 blendOverlay( vec3 base, vec3 blend ) {',
' return vec3( blendOverlay( base.r, blend.r ), blendOverlay( base.g, blend.g ), blendOverlay( base.b, blend.b ) );',
'}',
'float readDepth( sampler2D depthSampler, vec4 coord ) {',
' float fragCoordZ = texture2DProj( depthSampler, coord ).x;',
' float viewZ = perspectiveDepthToViewZ( fragCoordZ, cameraNear, cameraFar );',
' return viewZToOrthographicDepth( viewZ, cameraNear, cameraFar );',
'}',
'void main() {',
' vec4 base = texture2DProj( tDiffuse, vUv );',
' float depth = readDepth( tDepth, vUv );',
' gl_FragColor = vec4( blendOverlay( base.rgb, color ), 1.0 - ( depth * 50000.0 ) );',
'}'
].join( 'n' )
};
</script>
<div style="padding: 20px; position: absolute;">
</div>
Комментарии:
1. Он работает отлично, спасибо!. Я вижу, вы изменили стандартный отражатель, чтобы добавить поддержку затухания. Как вы думаете, есть ли какой-то другой способ сделать это, не переписывая все целиком? Просто для моих знаний (хотя код отлично подходит для моего варианта использования).
2. Нет,
Reflector
для достижения этого эффекта требуется текстура глубины. Это не поддерживается в версии по умолчаниюReflector
(чтобы избежать соответствующих накладных расходов, поскольку большинство вариантов использования не требуют затухания).3. Большое спасибо за это. Не могли бы вы также объяснить, как добавить размытие только к отражателю, а не ко всей сцене. Вот с чем я борюсь.