Три js 'просмотра кадрирования' с использованием области просмотра и ножниц - PullRequest
0 голосов
/ 06 августа 2020

Обзор:

  • У меня есть сцена с позиционированной перспективной камерой и несколькими активами, которые производят желаемый результат
  • Я не хочу перемещать / изменять поле обзора камеры, так как это потребует от меня динамической настройки всех элементов сцены (см. изображение вида отладки камеры)
  • Я хочу «обрезать вид» сцены от любого полного размера до меньшего ( не всегда в одинаковых пропорциях. См. результирующее изображение холста

examples

I have tried combinations of:

renderer.setScissor(0, 0, 320, 240)
renderer.setScissorTest(true)
renderer.setViewport(0, 0, 320, 240)
renderer.setSize(320, 240)
renderer.getContext().canvas.width = 320
renderer.getContext().canvas.height = 240
renderer.getContext().canvas.style.width = '320px'
renderer.getContext().canvas.style.height = '240px'
  • Applying the scissor gives me example what I want to see, because only those items are rendered, but the whole view is still the same size
  • Applying the viewport scales the WHOLE image down,
  • Adjusting the canvas crops relative to the whole image rather than the points I want to crop from.

I CAN do blitting (copy the exact pixels I want onto a separate canvas, but I was hoping there was another simpler way.

Any ideas?

I've added a codepen example here: https://codepen.io/faysvas/pen/KKzPQpa

const makeCube = (scene, color, x) => {
    const material = new THREE.MeshPhongMaterial({ color })
    const cube = new THREE.Mesh(new THREE.BoxGeometry(1, 1, 1), material)
    scene.add(cube)
    cube.position.x = x
    return cube;
}
const addSceneContents = (scene) => {
    const light = new THREE.DirectionalLight(0xffffff, 1)
    light.position.set(-1, 2, 4)
    scene.add(light)
    return [
        makeCube(scene, 0x44aa88, 0),
        makeCube(scene, 0x8844aa, -2),
        makeCube(scene, 0xaa8844, 2)
    ]
}
const main = () => {
    const canvas = document.querySelector("#c")
    const renderer = new THREE.WebGLRenderer({ canvas })
    renderer.setSize(512, 512, false)
    const fov = 75
    const aspect = 1
    const near = 0.1
    const far = 5
    const camera = new THREE.PerspectiveCamera(fov, aspect, near, far)
    camera.position.z = 2

    const scene = new THREE.Scene()
    scene.background = new THREE.Color( 0xff0000 )
    let cubes = addSceneContents(scene)

    resizeTo320x240(renderer, canvas) 


    const render = (time) => {
        time *= 0.001
        cubes.forEach((cube, ndx) => {
            const speed = 1 + ndx * 0.1
            const rot = time * speed
            cube.rotation.x = rot
            cube.rotation.y = rot
        })
        renderer.render(scene, camera)
        requestAnimationFrame(render)
    }
    requestAnimationFrame(render)
}
/*
This function should 'crop' from the whole scene without 
distorting the perspective of the camera and ensuring the 
canvas is 320x240
e.g. I want the canvas to be the same size and output of 
red cropped view below. Eg, no black and the canvas (and 
it's red contents) should be in the top left of the corner 
of the screen
*/
const resizeTo320x240 = (renderer, canvas) => {
    console.log('code goes here')
    const desiredWidth = 320
    const desiredHeight = 240
    const currentSize = renderer.getSize(new THREE.Vector2())
    const x = (currentSize.x / 2) - (desiredWidth/2)
    const y = (currentSize.y / 2) - (desiredHeight/2)

    renderer.setScissor(x, y, desiredWidth, desiredHeight)
    renderer.setScissorTest(true)
    //renderer.setViewport(x, y, desiredWidth, desiredHeight)
}
main()
html, body {
   margin: 0;
   height: 100%;
   background-color: grey;
}
#c {
   display: block;
   background-color: red;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r119/three.min.js"></script>
<canvas id="c"></canvas>

1 Ответ

0 голосов
/ 06 августа 2020

Я разобрался. Рендерер или область просмотра не подходят для решения этой проблемы, вместо этого сама камера имеет возможность смещать или обрезать свой собственный вывод.

const makeCube = (scene, color, x) => {
    const material = new THREE.MeshPhongMaterial({ color })
    const cube = new THREE.Mesh(new THREE.BoxGeometry(1, 1, 1), material)
    scene.add(cube)
    cube.position.x = x
    return cube;
}
const addSceneContents = (scene) => {
    const light = new THREE.DirectionalLight(0xffffff, 1)
    light.position.set(-1, 2, 4)
    scene.add(light)
    return [
        makeCube(scene, 0x44aa88, 0),
        makeCube(scene, 0x8844aa, -2),
        makeCube(scene, 0xaa8844, 2)
    ]
}
const main = () => {
    const canvas = document.querySelector("#c")
    const renderer = new THREE.WebGLRenderer({ canvas })
    renderer.setSize(512, 512, false)
    const fov = 75
    const aspect = 1
    const near = 0.1
    const far = 5
    const camera = new THREE.PerspectiveCamera(fov, aspect, near, far)
    camera.position.z = 2

    const scene = new THREE.Scene()
    scene.background = new THREE.Color( 0xff0000 )
    let cubes = addSceneContents(scene)

    resizeTo320x240(renderer, canvas, camera) 


    const render = (time) => {
        time *= 0.001
        cubes.forEach((cube, ndx) => {
            const speed = 1 + ndx * 0.1
            const rot = time * speed
            cube.rotation.x = rot
            cube.rotation.y = rot
        })
        renderer.render(scene, camera)
        requestAnimationFrame(render)
    }
    requestAnimationFrame(render)
}
/*
This function should 'crop' from the whole scene without 
distorting the perspective of the camera and ensuring the 
canvas is 320x240
e.g. I want the canvas to be the same size and output of 
red cropped view below. Eg, no black and the canvas (and 
it's red contents) should be in the top left of the corner 
of the screen
*/
const resizeTo320x240 = (renderer, canvas, camera) => {
    console.log('code goes here')
    const desiredWidth = 320
    const desiredHeight = 240
    
    const currentSize = renderer.getSize(new THREE.Vector2())
    const x = (currentSize.x / 2) - (desiredWidth/2)
    const y = (currentSize.y / 2) - (desiredHeight/2)
    
    // Set the size of the renderer to the correct desired output size
    renderer.setSize(desiredWidth, desiredHeight, false)
  
    // The camera its is one that should be cropped
    // This is referred to as the view offset in three.js
    camera.setViewOffset(currentSize.x,currentSize.y,x,y, desiredWidth, desiredHeight)
}
main()
html, body {
   margin: 0;
   height: 100%;
   background-color: grey;
}
#c {
   display: block;
   background-color: red;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r119/three.min.js"></script>
<canvas id="c"></canvas>
...