Алгоритмы смещения пикселей холста с субпиксельным сглаживанием в javascript

Я пытаюсь найти способ сделать псевдо 3D, искажая текстуры с помощью холста javascript.

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

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

Проблема в том, что этот метод сдвига пикселей изображения текстуры очень бинарен и создает слегка «зазубренный» край из-за того, что он просто смещает «полные» пиксели.

По сравнению с PhotoShop или некоторыми из примеров ImageMagick здесь метод javascript выглядит гораздо менее реалистичным . Это связано с возможностями обработки субпикселей PS и IM, благодаря которым можно получить медианы для межпиксельных данных.

Вопрос: кто-нибудь может предложить шаг, который можно интегрировать в мой алгоритм для получения гауссовой/алиасинговой гладкости на выходе?

Возможно, я могу просто запустить данные изображения через БПФ и обратно? есть ли примеры этого в действии?

Я немного в тупике и был бы очень признателен за некоторые указатели.


person Alex    schedule 16.12.2012    source источник
comment
Первое, что я бы попробовал, это передискретизировать: визуализировать на холсте (2x, 2x) или (4x, 4x), а затем уменьшить его либо с помощью putImage, глядя на значение imageSmoothingEnabled, конечно, либо «вручную». (здесь я описываю: stackoverflow.com/ questions/14424648/nice-ellipse-on-a-canvas/ как быстро обрабатывать ImageData)   -  person GameAlchemist    schedule 27.01.2013
comment
хорошая идея! к сожалению, это довольно интенсивный процесс js, который я делаю, поэтому увеличение размера холста - это действительно последнее средство (больше пикселей, больше процессора)   -  person Alex    schedule 04.02.2013
comment
Я бы все же попробовал, потому что в настольных браузерах процессор довольно дешевый, а больше всего стоит getImageData/putImageData. А на медленных устройствах... все равно ничего не поделаешь... Используй массив производительности, о котором я рассказываю в своем посте. 1) Можете ли вы опубликовать самый внутренний код? и 2) фото или два, чтобы мы могли увидеть, насколько "плохо" это выглядит? Кстати, я нахожу эту идею очень забавной, она мне чем-то напоминает вот это: 29a.ch/2010/3/24/normal-mapping-with-javascript-and-canvas-tag   -  person GameAlchemist    schedule 04.02.2013
comment
Привет, @VincentPiel, в настоящее время довольно сложно изолировать, поскольку код в значительной степени основан на гораздо более крупном приложении MVC, но концепция в значительной степени такая же, как показано в моей ссылке soundstep.com/blog/2012/04/25/javascript-displacement-mapping (я перешел на что-то другое для несколько недель). Вы должны увидеть искажение, о котором я говорю по этой ссылке.   -  person Alex    schedule 05.02.2013


Ответы (1)


1) Вы упоминаете два очень разных алгоритма: отображение смещения - это трехмерная техника, поэтому она включает в себя «Z» и проекцию, а другой - более простой алгоритм сдвига 2D-пикселей.

(В предоставленной ссылке на звуковой шаг используется слово «отображение смещения», но это метод смещения пикселей.)

2) Каким бы ни был размер вашего проекта MVC, алгоритм должен быть изолированным и иметь сигнатуру вида:

var pixelShift = function (sourceCanvas, shiftCanvas, xOffset, yOffset) 

и либо вернуть новый холст, либо изменить sourceCanvas на месте.

Если такой функции нет, пожалуйста, не говорите о MVC, если только M не означает «беспорядок». ;-) -

3) Алгоритм на самом деле довольно прост, вы должны перебрать целевой пиксель и посмотреть цвет пикселя, из которого они должны исходить (а не наоборот):

var pixelShift = function (sourceCanvas, shiftCanvas, xOffset, yOffset) {
    var shiftXY           = { xS:0, yS:0     };
    var shiftCanvasWidth  = shiftCanvas.width ;
    var shiftCanvasHeight = shiftCanvas.height;
    for ( var x=0 ; x < shiftCanvasWidth ; x ++) {
        for ( var y = 0 ; y < shiftCanvasHeight ; y++ ) {
            readShift ( shiftCanvas, x, y, shiftXY );
            var sourceColor = readPixelColor ( sourceCanvas, 
                                                 xOffset + shiftXY.xS, 
                                                         yOffset + shiftXY.yS) ;
            writePixel(sourceCanvas, xOffset + x , yOffset + y, sourceColor );
        }
     }
 };

 // sourceColor represents the color within a 32 bits integer (r,g,b,a * 8 bits).

было бы слишком долго писать все здесь, но:

-- в цикле pixelShift вы должны иметь дело не с исходным холстом, а с 32-битным массивом производительности.

-- канва сдвига должна быть преобразована ОДИН РАЗ в массив Int8Array и сохранена как эта.
размер этого массива равен shiftWidth * shiftHeight
нечетный индекс содержит сдвиг x, четный содержит сдвиг y.
массив предварительно -processed и содержит значение сдвига - 128.
для этого массива сдвига:

shiftX (x,y) = shiftArray [ 2 * (x + y * shiftWidth)     ] ;                                               
shiftY (x,y) = shiftArray [ 2 * (x + y * shiftWidth) + 1 ] ;  

-- Таким образом, pixelShift должен выглядеть так:

var pixelShift = function (sourceCanvas, 
                               shiftArray, shiftWidth, shiftHeight, xOffset, yOffset) {  
          [ Get a 32 bit performance array out of the canvas's target area ]  
          [ process this array using the shiftArray ]  
          [ write back the processed array onto the canvas's target area ]  
 }

-- И основной цикл может быть обработан линейным образом:

        var areaSize = shiftWidth * shiftHeight ;
        for ( pixelIndex=0 ; pixelIndex < areaSize ; pixelIndex++ ) {
            var linearShift = shiftArray [ 2*pixelIndex ] 
                                       + shiftWidth * shiftArray [ 2*pixelIndex + 1 ]  ;
            targetAreaArray [ pixelIndex ] = targetAreaArray [ pixelIndex + linearShift ] ;
            }

-- Rq : вы можете выполнить проверку границ (pixelIndex + linearShift) в пределах [0, areaSize[.

-- Я думаю, что теперь вы не можете работать быстрее.
Узким местом в производительности будут getImageData и putImageData, которые вам нужны для получения/помещения целевой области, но, насколько я знаю, нет другого способа получить бинарное представление на Canvas, чем эти две функции sloooooow.

person GameAlchemist    schedule 05.02.2013