【福利】乳搖動畫初探


咳,以探索技術的精神進行一些猥瑣的實現,先說明,如果你只想看最后乳搖的結果那就請ctrl+F4吧,因為網上有那些乳搖的APP,制作出來絕對比我這個初探的方法好,我這個只是介紹我實現乳搖的過程思路與方法。

關於乳搖如何實現,我第一個想法是使用metaball,因為是兩個球嘛,然而發現根本就不行,Fail。最終使用的是液化算法去實現。

好了,下面是對液化算法的介紹。

如果你從來不使用ps,暫時想不起來液化是什么不要緊,請看下圖

這個是采用液化使一只靜態的小狗有了動的感覺

總而言之,液化是使一張圖片的部分進行平滑的有規律的變化,這個變化有扭曲的平移之感

是如何實現的呢,來看算法,先給一張圖

局部變化以一個圈為變化環境,C點是圓心,r是半徑,當C移動到M這個點時,U移動到X。

通過以上這句話我們得出了這樣的結論:

液化的變化只在半徑為r的圓內發生,距離圓心越近,變化越明顯。這是不是和乳搖這一現象特別的吻合?

下面是算法公式

公式終於實現的是你知道X點的坐標,可以推算出U點的坐標,反之知道U點也可以推算出X的坐標

怎么推算出來的這個我也不知道,需要結合圓的范圍,進行插值處理,但是具體如何得到這個結論,感興趣的同學可以閱讀

Andreas Gustafsson 的 Interactive Image Warping一文,這個公式就是從這而來

 

好了,接下來該碼程序了

乳搖首先你得有圖

var sImg = new Image();
sImg.src = './dd.png';

var leftImage = new Image();
leftImage.src = './dd_left.png';

var rightImage = new Image();
rightImage.src = './dd_right.png';

var timer = null;

sImg.onload = function() {
    oGC.drawImage(sImg, 0, 0);
};

 

這里上來就有三張圖,其中一張是完整的,還有兩張分別是美女左右對半分開的【其實就是左胸和右胸】,因為需要這兩張圖進行乳搖【液化】后的還原。當然你也可以只用一張圖,先取出還原的像素存起來也是可以的,我在這里偷懶了

 

function liquify(imgData, cx, cy, mx, my, r) {
        var imgDataBuff = copyImageDataBuff(imgData);
        eachCircleDot(imgData, cx, cy, r, function(posi) {
            var tx = posi.x,
                ty = posi.y;
            var u = transFormula(cx, cy, mx, my, tx, ty, r);
            moveDot(imgData, imgDataBuff, posi, u);
            function transFormula(cx, cy, mx, my, tx, ty, r) {var relativity = sqr(r) - distanceSqr(tx, ty, cx, cy);
                var distanceMovedSqr = distanceSqr(mx, my, cx, cy);
                
                var rate = sqr(relativity / (relativity + distanceMovedSqr));
                
                var ux = (tx - rate*(mx-cx)),
                    uy = (ty - rate*(my-cy));
                return {
                    x: ux,
                    y: uy
                };
            }
        });
        return imgData;
    }

 

上面是液化算法的函數,結合上面的圖來看,參數分別是圖片像素data,圓心C的x軸和y軸,M點的x軸和y軸,圓的半徑r

copyImageDataBuff是將將要液化部分的像素copy一份,代碼如下

 

function copyImageDataBuff(imgData) {
    var data = imgData.data,
        imgDataBuff = [];
        
    for(var i in data) {
        imgDataBuff[i] = data[i];
    }
    
    return imgDataBuff;
}

 

eachCircleDot是將每個圓內的像素取出來進行處理

function eachCircleDot(imageData, ox, oy, r, callback) {
    var imgWidth = imageData.width,
        imgHeight = imageData.height,
        data = imageData.data,
        left = ox - r,
        right = ox + r,
        top = oy - r,
        bottom = oy + r;

    for(var x = left; x < right; x++) {
        for(var y = top; y < bottom; y++) {
            if(distanceSqr(x,y,ox,oy) <= sqr(r)) {
                callback({
                    x: x,
                    y: y
                });
            }
        }
    }
}

 

distanceSqr和sqr是求圓心距離和平方的函數,很簡單

function distanceSqr(x1, y1, x2, y2) {
    return sqr(x1 - x2) + sqr(y1 - y2);
}

function sqr(x) {
    return x * x;
}

transFormula這個方法就是液化公式的使用,傳入的是c點的x,y值、m點的x,y;t點就是上面圖中的x點,return出來u點的x,y值之后傳入moveDot,這個就是液化最終的表現函數

function moveDot(imgData, dataBuff, posi, u) {
    var imgWidth = imgData.width,
        imgHeight = imgData.height,
        data = imgData.data;

    u.x = Math.floor(u.x);
    u.y = Math.floor(u.y);

    data[(posi.y * imgWidth + posi.x) * 4] = dataBuff[(u.y * imgWidth + u.x) * 4];
    data[(posi.y * imgWidth + posi.x) * 4 + 1] = dataBuff[(u.y * imgWidth + u.x) * 4 + 1];
    data[(posi.y * imgWidth + posi.x) * 4 + 2] = dataBuff[(u.y * imgWidth + u.x) * 4 + 2];
    data[(posi.y * imgWidth + posi.x) * 4 + 3] = dataBuff[(u.y * imgWidth + u.x) * 4 + 3];
}

將公式算出的u點rgba信息換給之前的t點,也就是圖中的x點,完成液化

最終給一個成果圖

因為時間緣故只設置了左胸的搖動觸發,觸發代碼如下

var sX = 5;
var sY = 5;
var iX = -200;
var x = -10;
var iY = ev.clientY - oC.offsetTop;
if(iY > 296) { iY = 200; y = 10; } else { iY = -200; y = -10; } timer = setInterval(function() { oGC.drawImage(leftImage, 0, 0); // 只做了左半邊的效果 var d = oGC.getImageData(23, 140, 140, 200); var c = liquify(d, 60, 170, sX + 65, sY + 170, 58); oGC.putImageData(c, 23, 140); sX = sX + x; sY = sY + y; if(Math.abs(sX) > Math.abs(iX) || Math.abs(sY) > Math.abs(iY)) { clearInterval(timer); } }, 30);

這里面的數值都是自己測出來的,sX和sY是搖動的頻率,getImageData的xywh四個值也是試出來的的,意味着你想要胸變化的范圍大小,注意:

這個范圍必須要比液化公式中的圓大

iY和y是根據點擊在胸上方還是胸下方來確定搖動的方向

liquify傳入的參數已經介紹過了

——————————————————————————————————————————————————————————————

這次的乳搖還是很初步的,只是優化了速度,最早還有一個版本非常的卡,demo就不放出來了……一些幅度,方向都很簡單,而且是寫死的,如果你有興趣可以更多的去優化和添加功能~


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM