Центрирование контекста холста после масштабирования

1

Я пытаюсь использовать холст в качестве текстуры в three.js. Поскольку three.js требует, чтобы текстуры были мощью двух, ширина и высота холста установлены на [512, 512], однако я хочу, чтобы выходной холст был немощным из двух. Чтобы масштабировать масштаб, я масштабирую высоту по соотношению сторон, и это перемещает центр объектов, направленных вниз. Я попытался преобразовать высоту под некоторые произвольные числа (например, ctx.transform(0, -height/(1 -(height/width))) но не смог найти сладкое пятно. Кто-нибудь знает, как справиться с этим?

var camera, scene, renderer, mesh, material, ctx, texture, canvas, dimensions;
init();
animate();

function init() {
    canvas = document.createElement("canvas")
    canvas.width = 512;
    canvas.height = 512;
    ctx = canvas.getContext("2d");
    texture = new THREE.CanvasTexture(canvas)
    
    
    renderer = new THREE.WebGLRenderer();
    renderer.setSize(window.innerWidth,window.innerHeight);
    document.body.appendChild(renderer.domElement);

    camera = new THREE.OrthographicCamera(-1, 1, 1, -1, 0, 1);
    scene = new THREE.Scene();
    material = new THREE.MeshBasicMaterial({map: texture});
    
    var geometry = new THREE.PlaneBufferGeometry( 2, 2 );
    mesh = new THREE.Mesh(geometry, material);
    scene.add(mesh);
    dimensions = renderer.getSize()
}

function animate() {
		
    ctx.save()
    ctx.scale(1, dimensions.width / dimensions.height)
    // ctx.translate(0, -height * ??);
    ctx.fillStyle = "#FF0000"
    ctx.fillRect((canvas.width / 2) - 25, (canvas.height / 2) - 25, 50, 50);
    ctx.restore()
    
    texture.needsUpdate = true;
    requestAnimationFrame(animate);
    renderer.render(scene, camera);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/94/three.min.js"></script>

Квадрат должен быть масштабирован (еще квадрат) и центрирован

  • 0
    Более безопасный способ сделать это - использовать второй холст, на котором вы можете нарисовать свой спрайт любого размера. Технически, один квадратный texture canvas и один sprite canvas любой формы. Затем просто сделайте пюре из другого холста, изменив его размер в процессе. Я не вижу квадрата в вашем фрагменте, если честно, поэтому я не вижу, что вы пытаетесь достичь.
  • 0
    Эй, спасибо за ответ. Отображение в стеке потока не отображает отображение в вопросе правильно, поэтому я поместил его в скрипку здесь: jsfiddle.net/m0pnj6de/4 . Я хочу масштабировать его так, чтобы сохранить исходные формы и позиции, поэтому я бы хотел, чтобы квадрат был центрирован, и, ну, в общем, квадрат. Идея спрайта может сработать, но я бы предпочел уменьшить количество вызовов, если это возможно
Теги:
canvas
three.js

1 ответ

1
Лучший ответ

Мы можем решить вашу проблему, перейдя в центр и изменив размер вашего рендерера каждый раз, когда вы рисуете, так что вы можете увидеть квадрат оставшегося квадрата, и, очистив ваш холст, каждая ничья повернется так, что многократные ничьи не прилипают и путают позицию площади.

function init() {
	
    canvas = document.createElement( 'canvas' );
    ctx = canvas.getContext( '2d' );
    
    renderer = new THREE.WebGLRenderer;
    renderer.setSize( innerWidth, innerHeight);
	
	texture = new THREE.Texture( canvas );
    camera = new THREE.OrthographicCamera( -1, 1, 1, -1, 0, 1 );
    scene = new THREE.Scene;
    
    scene.add( new THREE.Mesh(
    	new THREE.PlaneBufferGeometry( 2, 2 ),
    	new THREE.MeshBasicMaterial({ map: texture })
    ) );
    
    document.body.appendChild( renderer.domElement );
    
}
function animate() {
	
	// Always update your renderer size.
	// This might be better off in the window.onresize event.
    renderer.setSize( innerWidth, innerHeight );
    
    // This will clear all draw data from your canvas, so we can draw on an empty canvas
    canvas.width = 512;
    canvas.height = 512;
    
	ctx.save();
    // Move the origin to the center of the canvas
    ctx.translate( canvas.width / 2, canvas.height / 2 );
    // Scale the canvas like you did, except we know the size as we just set it.
    ctx.scale( 1, innerWidth / innerHeight );
    ctx.fillStyle = "#FF0000";
    // Draw the rectangle as if you want its center at the origin
    ctx.fillRect( -25, -25, 50, 50 );
    ctx.restore();
    
    texture.needsUpdate = true;
    requestAnimationFrame( animate );
    renderer.render( scene, camera );
    
}

// I have removed some of the globals we don't need
// as the animate() function only needs access to some.
// I mostly do this because it clarifies what the functions share of resources.
	
var renderer, scene, camera, texture, canvas, ctx;

init();
animate();
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/95/three.min.js"></script>

Если честно, это похоже на то, что вы пытаетесь узнать из-за того, как работает OrthographicCamera. Лучше установить камеру в область просмотра вместо текстуры (я понимаю, для целей это достойный способ проверить ваше масштабирующее мастерство). В этом случае ваша сетка растягивается до неестественной пропорции, и ваша текстура корректирует ее. Тем не менее, вы должны, вероятно, установить свою камеру и сетку в обычном режиме и убедиться, что ваша камера делает правильные вещи и масштабируется в вашем окне просмотра (точно так же, как значение .aspect в PerspectiveCamera покажет вам правильный визуальный .aspect)

function dispatch_draw( time ){
  
  window.dispatchEvent( new CustomEvent( 'draw' ) );
	
	window.requestAnimationFrame( dispatch_draw );
	
}
function event_draw(){

	renderer.render( scene, camera );
	
}
function event_resize(){
	
	dimensions.set( innerWidth, innerHeight );
	renderer.setSize( dimensions.x, dimensions.y );
	
	camera.left = -dimensions.x / 2;
	camera.right = dimensions.x / 2;
	camera.top = dimensions.y / 2;
	camera.bottom = -dimensions.y / 2;
	camera.near = -1;
	camera.far = 1;
	camera.updateProjectionMatrix();

}

const renderer = new THREE.WebGLRenderer;
const camera = new THREE.OrthographicCamera;
const scene = new THREE.Scene;
const mesh = new THREE.Mesh(
	new THREE.PlaneGeometry( 200, 200 ),
	new THREE.MeshBasicMaterial({ color: 0x00ff00 })
);
const dimensions = new THREE.Vector2( innerWidth, innerHeight );

scene.add( mesh );

window.addEventListener( 'resize', event_resize );
window.addEventListener( 'draw', event_draw );

event_resize();
dispatch_draw();

document.body.appendChild( renderer.domElement );
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/95/three.min.js"></script>
  • 1
    Извините за поздний ответ, но спасибо за ответ, там много хорошей информации

Ещё вопросы

Сообщество Overcoder
Наверх
Меню