Hi,
I am trying to create a meatball-style effect with PNG images. The images are transparent with black shapes. My current idea is to have two scenes one where I place the textures in and then render it with a ShaderMaterial onto the first scene.
But I don’t know how to calculate the meatball effect in the fragment shader. I am unsure if this is the best solution to archive this effect. I would like them both to be always the same size.
An example of the meatball effect style I am after:
I am also experiencing some weird resizing issues where the scene2 resizes faster than the scene1. It is noticeable when you open the developer tools and the scene height narrows.
Here is my ThreeJs code and a CodeSandbox demo.
const size = { width: window.innerWidth, height: window.innerHeight };
// Renderer
const renderer = new THREE.WebGLRenderer({ preserveDrawingBuffer: true, transparent: true, stencil: false });
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
renderer.setSize(size.width, size.height);
appElement.appendChild(renderer.domElement);
renderer.setClearColor(0x000000, 0);
// Camera
const camera = new THREE.PerspectiveCamera(70, size.width / size.height, 0.1, 1000);
const zoom = 600;
camera.position.z = zoom;
camera.aspect = size.width / size.height;
camera.fov = 2 * Math.atan(size.height / 2 / zoom) * (180 / Math.PI);
// Scene's
const scene = new THREE.Scene();
const scene2 = new THREE.Scene();
scene.background = new THREE.Color("#FFCA27");
scene2.background = new THREE.Color("#FF0000");
// Render Target
const settings = { minFilter: THREE.LinearFilter, magFilter: THREE.LinearFilter, format: THREE.RGBFormat, stencilBuffer: false };
const renderTarget = new THREE.WebGLRenderTarget(size.width, size.height, settings);
// Shader Material
const shaderMaterial = new THREE.ShaderMaterial({
vertexShader: `
varying vec2 vUv;
void main(){
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
vUv = uv;
}
`,
fragmentShader: `
uniform sampler2D uTexture;
uniform float uTime;
varying vec2 vUv;
void main( void ) {
vec4 texture = texture2D( uTexture, vUv ).rgba;
float threshold = 0.1;
gl_FragColor = vec4( texture);
}
`,
uniforms: {
uTexture: { value: renderTarget.texture },
uTime: { value: 0 },
uResolution: { value: new THREE.Vector2(size.width, size.height) },
},
transparent: true,
});
function createPlane(texture, scale = 1) {
const width = texture.image.width * scale;
const height = texture.image.height * scale;
const geometry = new THREE.PlaneGeometry(width, height);
const material = new THREE.MeshBasicMaterial({ map: texture, transparent: true });
const plane = new THREE.Mesh(geometry, material);
return plane;
}
loadTextures(["./assets/texture1.png", "./assets/texture2.png"]).then((textures) => {
const [texture1, texture2] = textures;
const plane_1 = createPlane(texture1, 0.1);
const plane_2 = createPlane(texture2, 0.1);
plane_2.position.x = 140;
scene2.add(plane_1);
scene2.add(plane_2);
});
// Full-screen quad geometry
const fullScreenQuad = new THREE.PlaneGeometry(size.width, size.height);
const postProcessMesh = new THREE.Mesh(fullScreenQuad, shaderMaterial);
scene.add(postProcessMesh);
function render() {
renderer.setRenderTarget(renderTarget);
renderer.render(scene2, camera);
renderer.setRenderTarget(null);
renderer.render(scene, camera);
window.requestAnimationFrame(render);
}
render();