I have a website where I want to create canvasses with different sizes, and render a result from three.js on there.
Those newly created canvasses don’t have to be made with three,js, but I’m not against that either.
What is important is that they are ‘webgl’ so things stay fast.
Here an example of the different targets:
My problem so far was that downscaling looked terrible, here is an example:
Here is the same thing downscaled in photoshop:
I tried a implementation with a offscreen_scale_factor but this didn’t really help.
I also tried a renderer that renderers the result on a plane, also this quality was bad.
As last I tried creating a webgl canvas that renderer the renderer.domElement as a texture, but nothing shows up.
I also saw there is CanvasRenderingContext2D: imageSmoothingQuality property - Web APIs | MDN, but this is not supported in firefox. Also I truly believe it should be possible to achieve this without imageSmoothingQuality I guess since things can be drawn with a shader?
Anyway, I hope someone can help me.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>HSLAB</title>
<style>
body {
margin: 0;
overflow: hidden;
}
</style>
</head>
<body>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/0.158.0/three.min.js"></script>
<script src="hslab_logo.js"></script>
<script src="hslab_logo_loader.js"></script>
<div class="hslab_logo" style="width: 1024px; height: 256px; background-color: red;"></div>
<div class="hslab_logo" style="width: 512px; height: 128px; background-color: green;"></div>
<div class="hslab_logo" style="width: 256px; height: 64px; background-color: blue;"></div>
<div class="hslab_logo" style="width: 128px; height: 32px; background-color: yellow;"></div>
<div class="hslab_logo" style="width: 64px; height: 16px; background-color: magenta;"></div>
</body>
</html>
hslab_logo.js
"use strict";
let renderer;
(function(){
let scene, camera;
let texture1, texture2;
let mesh_zoom_area;
const OFFSCREEN_RENDER_WIDTH = 1024;
const OFFSCREEN_RENDER_HEIGHT = 1024;
// ---------------------------------------------------------
addEventListener("DOMContentLoaded", (event) => {
init();
});
function init() {
// Scene setup
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera(75, OFFSCREEN_RENDER_WIDTH / OFFSCREEN_RENDER_HEIGHT, 0.1, 1000);
camera.position.z = 5;
// Renderer setup
renderer = new THREE.WebGLRenderer();
renderer.setSize(OFFSCREEN_RENDER_WIDTH, OFFSCREEN_RENDER_HEIGHT);
renderer.setClearColor(0xffffff);
// Texture loading
texture1 = new THREE.TextureLoader().load('HSL_Mask_0002.png');
texture2 = new THREE.TextureLoader().load('HSL_RGB_0002.png');
texture1.minFilter = THREE.NearestFilter;
texture2.minFilter = THREE.NearestFilter;
texture1.magFilter = THREE.NearestFilter;
texture2.magFilter = THREE.NearestFilter;
// Mesh setup
const geometry = new THREE.PlaneGeometry(8, 8);
const material1 = new THREE.MeshBasicMaterial({ map: texture1, transparent: true, alphaTest: 0.5 });
const material2 = new THREE.MeshBasicMaterial({ map: texture2, transparent: true, alphaTest: 0.5 });
const mesh1 = new THREE.Mesh(geometry, material1);
const mesh2 = new THREE.Mesh(geometry, material2);
mesh2.position.z = -0.025;
// Zoom area setup
const zoom_geometry = new THREE.PlaneGeometry(3.5, 1.1);
const zoom_material = new THREE.MeshBasicMaterial({ color: 0x00ff00 });
mesh_zoom_area = new THREE.Mesh(zoom_geometry, zoom_material);
mesh_zoom_area.position.z = -0.035;
// Finally...
scene.add(mesh1);
scene.add(mesh2);
// window.addEventListener('resize', onWindowResize, false);
// And start...
// onWindowResize();
adjustCameraFov(camera);
animate();
}
/*
function onWindowResize() {
const logo_container = logo_containers[0];
const bounds = logo_container.getBoundingClientRect();
// TODO
bounds.width = 1024;
bounds.height = 1024;
// Adjust camera aspect ratio and update its projection matrix
camera.aspect = bounds.width / bounds.height;
camera.updateProjectionMatrix();
// Update renderer size
renderer.setSize(bounds.width, bounds.height);
// Custom camera adjustments
adjustCameraFov(camera);
}
*/
function adjustCameraFov(camera) {
camera.fov = map(camera.aspect, 1, 5, 50, 30);
if (camera.fov < 1) camera.fov = 1;
fitCameraToCenteredObject(camera, mesh_zoom_area, 1, undefined);
}
function animate() {
requestAnimationFrame(animate);
const millis = performance.now();
scene.rotation.y = Math.cos(millis * 0.0005) * Math.PI * 0.25;
renderer.render(scene, camera);
}
// Utility functions
function map(value, fromMin, fromMax, toMin, toMax) {
return (value - fromMin) / (fromMax - fromMin) * (toMax - toMin) + toMin;
}
function fitCameraToCenteredObject(camera, object, offset, orbitControls) {
const boundingBox = new THREE.Box3();
boundingBox.setFromObject( object );
var middle = new THREE.Vector3();
var size = new THREE.Vector3();
boundingBox.getSize(size);
// figure out how to fit the box in the view:
// 1. figure out horizontal FOV (on non-1.0 aspects)
// 2. figure out distance from the object in X and Y planes
// 3. select the max distance (to fit both sides in)
//
// The reason is as follows:
//
// Imagine a bounding box (BB) is centered at (0,0,0).
// Camera has vertical FOV (camera.fov) and horizontal FOV
// (camera.fov scaled by aspect, see fovh below)
//
// Therefore if you want to put the entire object into the field of view,
// you have to compute the distance as: z/2 (half of Z size of the BB
// protruding towards us) plus for both X and Y size of BB you have to
// figure out the distance created by the appropriate FOV.
//
// The FOV is always a triangle:
//
// (size/2)
// +--------+
// | /
// | /
// | /
// | F° /
// | /
// | /
// | /
// |/
//
// F° is half of respective FOV, so to compute the distance (the length
// of the straight line) one has to: `size/2 / Math.tan(F)`.
//
// FTR, from https://threejs.org/docs/#api/en/cameras/PerspectiveCamera
// the camera.fov is the vertical FOV.
const fov = camera.fov * ( Math.PI / 180 );
const fovh = 2*Math.atan(Math.tan(fov/2) * camera.aspect);
let dx = size.z / 2 + Math.abs( size.x / 2 / Math.tan( fovh / 2 ) );
let dy = size.z / 2 + Math.abs( size.y / 2 / Math.tan( fov / 2 ) );
let cameraZ = Math.max(dx, dy);
// offset the camera, if desired (to avoid filling the whole canvas)
if( offset !== undefined && offset !== 0 ) cameraZ *= offset;
camera.position.set( 0, 0, cameraZ );
// set the far plane of the camera so that it easily encompasses the whole object
const minZ = boundingBox.min.z;
const cameraToFarEdge = ( minZ < 0 ) ? -minZ + cameraZ : cameraZ - minZ;
camera.far = cameraToFarEdge * 3;
camera.updateProjectionMatrix();
if ( orbitControls !== undefined ) {
// set camera to rotate around the center
orbitControls.target = new THREE.Vector3(0, 0, 0);
// prevent camera from zooming out far enough to create far plane cutoff
orbitControls.maxDistance = cameraToFarEdge * 2;
}
}
})();
hslab_logo_loader.js
"use strict";
let logo_containers;
(function(){
addEventListener("DOMContentLoaded", (event) => {
init();
});
function init() {
onsole.log(renderer.domElement);
logo_containers = document.querySelectorAll(".hslab_logo");
if (logo_containers.length == 0) {
console.log("[Warning] No div found with class=\"hslab_logo\"!");
return;
}
logo_containers.forEach(container => {
});
}
})();
I removed the non working code since I had to many variations for that.
Here are the textures:
This one is white with alpha!:
^^^
Hope someone can help