使用 Three.js 和 Matcap 材质打造创意 3D 卡片效果

0 阅读1分钟

作者:初始Threejs

使用 Three.js 的 WebGL 小实验。突出其在 Three.js 和 Matcap 材质上的创意应用。

图片实现代码

HTML:

<body></body>

CSS:

body {  overflow: hidden;}

JAVASCRIPT:


const settings = {  sizes: {    width: window.innerWidth,    height: window.innerHeight  },  boxDimensions: {    h: 1.4,    w: 1,  }}//texturesconst textureLoader = new THREE.TextureLoader();const photoTexture02 = textureLoader.load('https://assets.codepen.io/4201020/city2.png?format=auto');const photoTexture03 = textureLoader.load('https://assets.codepen.io/4201020/shopp-e-1731593813681771088199459824.png');const photoTexture = textureLoader.load('https://assets.codepen.io/4201020/shopp-e-1731594468645280783813006762.png');photoTexture.wrapS = THREE.RepeatWrapping;photoTexture.wrapT = THREE.RepeatWrapping;photoTexture.repeat.set( .1, .1 );// initconst scene = new THREE.Scene();const camera = new THREE.PerspectiveCamera(75, settings.sizes.width / settings.sizes.height, 0.1, 1000);camera.position.set(0, 0, 3);camera.lookAt(0, 0, 0);scene.add(camera);
const sun = new THREE.AmbientLight(0xffffff, .5);scene.add(sun);
const planeGeometry = new THREE.PlaneGeometry(settings.boxDimensions.w, settings.boxDimensions.h);
const renderer = new THREE.WebGLRenderer( { antialias: true } );renderer.setSize( window.innerWidth, window.innerHeight );renderer.setAnimationLoop( animation );document.body.appendChild( renderer.domElement );
function RoundedPortalPhotoPlane(geometry, photoTexture) {  const material = new THREE.MeshMatcapMaterial({    matcap: photoTexture,    transparent: true,  })
  material.onBeforeCompile = (shader) => {    shader.vertexShader = shader.vertexShader.replace(      '#include <common>',      `          #include <common>          varying vec4 vPosition;          varying vec2 vUv;      `  );    shader.vertexShader = shader.vertexShader.replace(        '#include <fog_vertex>',        `            #include <fog_vertex>            vPosition = mvPosition;            vUv = uv;        `    );    shader.fragmentShader = shader.fragmentShader.replace(      `#include <common>`,      `      #include <common>      varying vec4 vPosition;      varying vec2 vUv;      float roundedBoxSDF(vec2 CenterPosition, vec2 Size, float Radius) {          return length(max(abs(CenterPosition)-Size+Radius,0.0))-Radius;        }      `    );    shader.fragmentShader = shader.fragmentShader.replace(      `#include <dithering_fragment>`,      `      #include <dithering_fragment>
      // The pixel space scale of the rectangle.      vec2 size = vec2(1.01.0);      // How soft the edges should be (in pixels). Higher values could be used to simulate a drop shadow.      float edgeSoftness  = 0.001;      // The radius of the corners (in pixels).      float radius = 0.08;      // Calculate distance to edge.      float distance  = roundedBoxSDF(vUv.xy - (size/2.0), size/2.0, radius);      // Smooth the result (free antialiasing).      float smoothedAlpha =  1.0-smoothstep(0.0, edgeSoftness * 2.0,distance);      // Return the resultant shape.      //vec4 quadColor = mix(vec4(1.0, 1.0, 1.0, 1.0), vec4(0.0, 0.2, 1.0, smoothedAlpha), smoothedAlpha);      gl_FragColor = vec4(outgoingLight, smoothedAlpha);      `    );        console.log(shader.fragmentShader);
  };  const plane = new THREE.Mesh(    geometry,    material  );
  return plane;}
// planesconst planeGroup = new THREE.Group();
const photoPlane01 = new RoundedPortalPhotoPlane(  planeGeometry,  photoTexture02);photoPlane01.position.set(-1, 0, 1);photoPlane01.rotation.y = Math.PI * 0.1;photoPlane01.name = 'plane1';planeGroup.add(photoPlane01);
const photoPlane02 = new RoundedPortalPhotoPlane(  planeGeometry,  photoTexture);
photoPlane02.position.set(0, 0, 0.5);photoPlane02.name = 'plane2';planeGroup.add(photoPlane02);


const photoPlane03 = new RoundedPortalPhotoPlane(  planeGeometry,  photoTexture03);photoPlane03.position.set(1, 0, 1);photoPlane03.rotation.y = Math.PI * -0.1;photoPlane03.name = 'plane3';planeGroup.add(photoPlane03);scene.add(planeGroup);
const mouse = new THREE.Vector2();let xParallaxFactor = -0.3;let yParallaxFactor = 0.3;//mouse eventswindow.addEventListener('mousemove', (event) => {  mouse.x = event.clientX / settings.sizes.width * 2 - 1; //could also do it this way: map(event.clientX, 0, sizes.width, -1, 1)  mouse.y = - (event.clientY / settings.sizes.height) * 2 + 1; //could also do it this way: map(event.clientY, 0, sizes.height, 1, -1)});
// animationconst clock = new THREE.Clock()let previousTime = 0;
function animation( time ) {
const elapsedTime = clock.getElapsedTime()  const deltaTime = elapsedTime - previousTime;  previousTime = elapsedTime;
  const parallaxX = mouse.x * xParallaxFactor;  const parallaxY = mouse.y * yParallaxFactor;    planeGroup.rotation.y += (parallaxX - planeGroup.rotation.y) * 3 * deltaTime;    planeGroup.rotation.x += (parallaxY - planeGroup.rotation.x) * 3 * deltaTime;
	renderer.render( scene, camera );
}

源码:

codepen.io/smcnally000…

体验:

codepen.io/smcnally000…