Periodic circular opening and closing box

Since about two weeks I’m a little bit more interested in shaders.
So far I was interested in the geometries, see hofk (Klaus Hoffmeister) · GitHub

From my collection, this example invisibleParts @prisoner849 came to mind. I then made something similar with a simple fragmentShader. See ShaderExamples_09

Since I don’t have much experience with shaders yet, it would be interesting to know if something could be optimized.
:thinking:



<!DOCTYPE html>
<!-- https://discourse.threejs.org/t/periodic-circular-opening-and-closing-box/32322 -->
<!-- Periodic circular opening and closing box  -->
<head>
  <title> ShaderExamples_09 </title>
  <meta charset="utf-8" />
  <style>
    body {
        overflow:hidden;
        margin: 0;
    }
  </style>
</head>

<body> </body>

<script type="module">

// @author hofk

import * as THREE from '../jsm/three.module.135.js';
import { OrbitControls } from '../jsm/OrbitControls.135.js';
import { GLTFLoader } from '../jsm/GLTFLoader.135.js';

const camera = new THREE.PerspectiveCamera( 65, window.innerWidth / window.innerHeight, 0.01, 1000 );
camera.position.set( 1, 1, 1.3 );

const scene = new THREE.Scene();

const renderer = new THREE.WebGLRenderer( { antialias: true } );
renderer.setSize( window.innerWidth, window.innerHeight );
renderer.setClearColor( 0x3e9724 );
document.body.appendChild( renderer.domElement );

const light = new THREE.PointLight( 0xffffff, 1.2 );
light.position.set( 5, 5, 5 );
scene.add( light );

const controls = new OrbitControls( camera, renderer.domElement );

const forestTexture = new THREE.TextureLoader( ).load( 'myForest.png' ); // self photographed, free
const texturLoader = new THREE.TextureLoader( );
const squirrelTexture = texturLoader.load( 'mySquirrel.png' );  // self photographed, free

let frShPart1, openingPosition, frShPart2 ;
shaderParts( );  

let shaderMaterial = [ ];

for ( let i = 0; i < 3; i ++ ) {

    shaderMaterial[ i ] = new THREE.ShaderMaterial({ 
        uniforms: { 
                v_Uv: { value: new THREE.Vector2( ) },
                u_time: { value:  1.5 },
                u_Texture: { value: forestTexture },
        },
        vertexShader: vertexShader( ),
        fragmentShader: frShPart1 + openingPosition[ i ] + frShPart2, 
        side: THREE.DoubleSide,
        transparent: true,
    });

}

const loader = new GLTFLoader( );
const modelSqu = new THREE.Object3D( );
loader.load( 'Lowpoly Squirrel by Tipatat Chennavasin/model.gltf', function ( gltf ) { //  (CC-BY) Poly by Google
	const box = new THREE.Box3( ).setFromObject( gltf.scene );
	const c = box.getCenter( new THREE.Vector3( ) );
	const size = box.getSize( new THREE.Vector3( ) ); 
	gltf.scene.position.set( -c.x, size.y / 2 - c.y, -c.z );
	modelSqu.add( gltf.scene );
    modelSqu.scale.set( 1.5, 1.5, 1.5 );
	modelSqu.position.y = -0.5;
	scene.add( modelSqu );
	}
);

const boxMaterials = [

    shaderMaterial[ 0 ],
    new THREE.MeshBasicMaterial( { map: squirrelTexture, side: THREE.DoubleSide, } ),
    shaderMaterial[ 1 ],
    new THREE.MeshBasicMaterial( { color: 0x3f5b50, side: THREE.DoubleSide, } ),
    shaderMaterial[ 2 ],
    new THREE.MeshBasicMaterial( { map: forestTexture, side: THREE.DoubleSide, } ),
    
];

const boxGeometry = new THREE.BoxBufferGeometry( );
const boxMesh = new THREE.Mesh( boxGeometry, boxMaterials );
scene.add( boxMesh );

animate();

function animate() {

    requestAnimationFrame( animate );
    shaderMaterial[ 0 ].uniforms.u_time.value += 0.01;
    shaderMaterial[ 1 ].uniforms.u_time.value += 0.01;
    shaderMaterial[ 2 ].uniforms.u_time.value += 0.01;
	modelSqu.rotation.y += 0.01;   
    renderer.render( scene, camera );

}

function vertexShader ( ) { //   return `    `;   a multiline template literal 
    return `
        varying vec2 v_Uv;
        uniform float u_time;
        uniform sampler2D u_Texture;
        
        void main( )	{

				v_Uv = uv;
				vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );
				gl_Position = projectionMatrix * mvPosition;
            
		} 
    `;
}

function shaderParts( ) {
    frShPart1 = 
    `   varying vec2 v_Uv;
        uniform float u_time;
        uniform sampler2D u_Texture;   
        float circle( vec2 pos, vec2 c, float r ) {     // pos: position   c: center  r: radius
            vec2 dist = pos - vec2( c.x, c.y );         // distance
            return step( 0.5 * r, dot( dist, dist ) );
        }
        void main( ) {
            
            vec2 position = v_Uv;
            float radius = 1.3;         
            vec2 center = vec2( 0.85 + 0.5 *( 1.0 + sin( u_time ) ) );
    `;
    
    openingPosition = [
    
        ` position = vec2( 1.0 - v_Uv.x, 0.0 + v_Uv.y ); `, // top left
        ` position = vec2( 0.0 + v_Uv.x, 1.0 - v_Uv.y ); `, // bottom right
        ` position = vec2( 0.0 + v_Uv.x, 0.0 + v_Uv.y ); `, // top right 
        ` position = vec2( 1.0 - v_Uv.x, 1.0 - v_Uv.y ); `, // bottom left - not used here!
    
    ];
    
    frShPart2 = 
        `   float aCirc = circle( position, center, radius );            
            vec3 colorTex = texture2D( u_Texture, v_Uv ).rgb;           
            float aTex = texture2D( u_Texture, v_Uv ).a;           
            vec4 color = vec4( colorTex, aTex ) * vec4( vec3( 1.0 ), aCirc );
            gl_FragColor = color;           
        }
    `;
}

</script>
</html>
1 Like

Funny enough, I just saw someone doing a very similar effect but with a noise gradient!

1 Like

Direct link

A very simple variant without much effort.
Box with dynamic holes → ShaderExamples_09a


<!DOCTYPE html>
<!-- https://discourse.threejs.org/t/periodic-circular-opening-and-closing-box/32322/3 -->
<!-- box with dinamic holes -->
<head>
  <title> ShaderExamples_09a </title>
  <meta charset="utf-8" />
  <style>
    body {
        overflow:hidden;
        margin: 0;
    }
  </style>
</head>

<body> </body>

<script type="module">

// @author hofk

import * as THREE from '../jsm/three.module.135.js';
import { OrbitControls } from '../jsm/OrbitControls.135.js';

const camera = new THREE.PerspectiveCamera( 70, window.innerWidth / window.innerHeight, 0.01, 1000 );
camera.position.set( 1, 1, 2 );
const scene = new THREE.Scene();
const renderer = new THREE.WebGLRenderer( { antialias: true } );
renderer.setSize( window.innerWidth, window.innerHeight );
renderer.setClearColor( 0x488348);
document.body.appendChild( renderer.domElement );
const controls = new OrbitControls( camera, renderer.domElement );

const tex = new THREE.TextureLoader( ).load( 'uv_grid_opengl.jpg' );

const shaderMaterial = new THREE.ShaderMaterial({
    uniforms: { 
            v_Uv: { value: new THREE.Vector2( ) },
            u_time: { value: 0.5 },
            u_Texture: { value: tex },
    },
    vertexShader: vertexShader( ), 
    fragmentShader: fragmentShader( ),
    side: THREE.DoubleSide,
    transparent: true,
});

const boxGeometry = new THREE.BoxBufferGeometry(  );
const boxMesh = new THREE.Mesh( boxGeometry, shaderMaterial );
scene.add( boxMesh );

animate();

function animate() {

    requestAnimationFrame( animate );
    shaderMaterial.uniforms.u_time.value += 0.01;
    renderer.render( scene, camera );

}

function vertexShader ( ) { //   return `    `;   a multiline template literal 
    return `
        varying vec2 v_Uv;
        uniform float u_time;
        uniform sampler2D u_Texture;        
        void main( )    {
                v_Uv = uv;
                vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );
                gl_Position = projectionMatrix * mvPosition;         
        }
    `;
}

function fragmentShader( ) {
    return `
        varying vec2 v_Uv;
        uniform float u_time;
        uniform sampler2D u_Texture;      
        float circle( vec2 c, float r ) { // st: position   c: center  r: radius
            vec2 dist = v_Uv - vec2( c.x, c.y ); // distance
            return step( 0.5 * r, dot( dist, dist ) );
        }
        void main( ) {            
            vec2 position = v_Uv;
            float radius = 0.6 * ( 1.0 + sin( u_time ) ) - 0.05;           
            vec2 center = vec2( 0.5 );
            vec3 colorCirc = vec3( circle( center, radius ) ); 
            float aCirc = circle( center, radius ); 
            vec3 colorTex = texture2D( u_Texture, v_Uv ).rgb;           
            float aTex = texture2D( u_Texture, v_Uv ).a; 
            vec4 color = vec4( colorTex, aTex ) * vec4( vec3( 1.0 ), aCirc );
            gl_FragColor = color;
        }
    `;
}
</script>
</html>
3 Likes