- PVSM.RU - https://www.pvsm.ru -
В этой статье я хочу продолжить рассказ о моих экспериментах с 3D монитором.В первой статье [1] было описано как выводить стерео изображение из видео потока (в VLC виде плеере) сейчас я расскажу как получить стерео картинку прямо в вашем браузере. Для демо я взял замечательную библиотеку Three.js [2] об ней уже много писали на Хабре, она позволяет быстро и просто создавать красивые web приложения на WebGL. Ниже я покажу как сделать так чтобы пользователь увидел глубокую 3D картинку а не плоскую проекцию.
В качестве исходных данных возьмем самый простой пример из Three.js [2]
— это будет вращающийся кубик [3]. Чтобы 3D эффект был ярче — я добавил к вращению еще поступательное движение по направлению к наблюдателю.
Для того чтобы получить 2 ракурса для нашей трех-мерной картинки — делаем такой трюк
в цикле отрисовки каждого фрейма:
— отрисовываем сцену с камеры не на экран а в текстуру
— сдвигаем камеру (как бы в позицию второго глаза)
— отрисовываем сцену в другую текстуру
— теперь у нас есть картинки для левого и правого глаза — нам остается их правильно смешать чтобы левый глаз увидел левую картинку а правый — правую на 3D мониторе.
теперь опишем это в коде
(базовый код примера webgl_geometry_cube приводить нет смысла, опишу лишь то что я добавил)
//инициализация вспомогательных текстур и сцены для их отрисовки
function initORTscene() {
//projection to screen
rtTexture = new THREE.WebGLRenderTarget(window.innerWidth, window.innerHeight, { minFilter: THREE.LinearFilter, magFilter: THREE.NearestFilter, format: THREE.RGBFormat });
ltTexture = new THREE.WebGLRenderTarget(window.innerWidth, window.innerHeight, { minFilter: THREE.LinearFilter, magFilter: THREE.NearestFilter, format: THREE.RGBFormat });
//текстуры будут растеризовываться при помощи шейдера - я его описывал в предыдущей статье
materialScreen = new THREE.ShaderMaterial({
uniforms: { lRacurs: { type: "t", value: ltTexture }, rRacurs: { type: "t", value: rtTexture }, height: { type: "f", value: window.innerHeight } },
vertexShader: document.getElementById('vertexShader').textContent,
fragmentShader: document.getElementById('fragmentShader').textContent,
depthWrite: false
});
//рисовать будем на прямоугольнике который занимает все окно
var plane = new THREE.PlaneGeometry(window.innerWidth, window.innerHeight);
var offscreenMesh = new THREE.Mesh(plane, materialScreen);
offscreenMesh.position.z = -1; //a little behind
sceneORT = new THREE.Scene();
//перспектива нам здесь не нужна, поэтому тут будет камера которая возьмет ортографическую проекцию
cameraORT = new THREE.OrthographicCamera(window.innerWidth / -2, window.innerWidth / 2, window.innerHeight / 2, window.innerHeight / -2, -10000, 10000);
sceneORT.add(offscreenMesh);
}
шейдеры
в них мы передаем 2 текстуры и высоту кадра в пикселях экрана
вершинный шейдер вычисляет позицию точки и передает ее в пиксельный шейдер
там мы вычисляем четная ли у нас линия по вертикали или нет, для четных линий берем одну текстуру — для нечетных другую (в этой статье [1] я писал как такой подход позволяет формировать 3D картинку)
<script id="fragmentShader" type="x-shader/x-fragment">
varying vec2 vUv;
uniform sampler2D rRacurs;
uniform sampler2D lRacurs;
uniform float height;
void main() {
//odd from left racurs, even from right
float d = mod((floor(height*(vUv.y+1.0))),2.0); //odd or even, height - is new uniform to get viewport height
if(d > 0.1) {
gl_FragColor = texture2D( rRacurs, vUv );
} else {
gl_FragColor = texture2D( lRacurs, vUv );
}
}
</script>
<script id="vertexShader" type="x-shader/x-vertex">
varying vec2 vUv;
void main() {
vUv = uv;
gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
}
</script>
теперь отрисуем нашу сцену
var x = camera.position.x;
var faceWidth = 5; //расстояние между глаз
//отрисовка правой текстуры
camera.position.x = x + faceWidth / 2;
renderer.render(scene, camera, rtTexture, true);
//отрисовка левой текстуры
camera.position.x = x - faceWidth / 2;
renderer.render(scene, camera, ltTexture, true);
camera.position.x = x;
// а теперь отрисуем прямоугольник с двумя текстурами прямо на экран
renderer.render(sceneORT, cameraORT);
Вот и все.
Счастливые владельцы пассивных 3D мониторов могут посмотреть демо [4] (на обычном мониторе это конечно не так красиво). Код можно найти на github [5]
Хочу заметить, тут конечно не 30 строк кода, но не больше 70 — и это все что нужно чтоб реализовать 3D картинку.
Параметр faceWidth можно менять — чем он больше — тем сильней 3D, но значительны геометрические искажения.
Данный код можно использовать для любой сцены, написанной на three.js(WebGL), чтоб добавить к ней настоящий 3D, например вот ссылка на игру [6], которую я написал в рамках изучения javascript, — в 3D выглядит вполне неплохо.
Автор: mangelov
Источник [7]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/news/54816
Ссылки в тексте:
[1] В первой статье: http://habrahabr.ru/post/211468/
[2] Three.js : http://threejs.org
[3] вращающийся кубик: http://threejs.org/examples/#webgl_geometry_cube
[4] демо: http://mikhail-angelov.github.io/html5-3D-demo/index.html
[5] github: https://github.com/mikhail-angelov/html5-3D-demo
[6] ссылка на игру: http://mikhail-angelov.github.io/shooter/
[7] Источник: http://habrahabr.ru/post/212297/
Нажмите здесь для печати.