2016-08-18 58 views
6

我在學習three.js,試圖嘗試變換圖像。在WebGL和three.js中創建圖像變形效果

我真的很喜歡所示here.

我將遵循哪些步驟能夠改變與此類似圖像效果?

到目前爲止,我有:

// instantiate a loader 
var loader = new THREE.TextureLoader(); 

// load a resource 
loader.load(
    // resource URL 
    'clouds.jpg', 
    // Function when resource is loaded 
    function (texture) { 
     init(new THREE.MeshBasicMaterial({ 
      map: texture 
     })); 
    }, 
    // Function called when download progresses 
    function (xhr) { 
     console.log((xhr.loaded/xhr.total * 100) + '% loaded'); 
    }, 
    // Function called when download errors 
    function (xhr) { 
     console.log('An error happened'); 
    } 
); 

var init = function(material) { 
    var scene = new THREE.Scene(); 
    var camera = new THREE.PerspectiveCamera(75, window.innerWidth/window.innerHeight, 0.1, 1000); 

    var renderer = new THREE.WebGLRenderer(); 
    renderer.setSize(window.innerWidth, window.innerHeight); 
    document.body.appendChild(renderer.domElement); 

    var rectLength = window.innerHeight; 
    var rectWidth = window.innerWidth; 
    var rectShape = new THREE.Shape(); 
    rectShape.moveTo(0,0); 
    rectShape.lineTo(0, rectWidth); 
    rectShape.lineTo(rectLength, rectWidth); 
    rectShape.lineTo(rectLength, 0); 
    rectShape.lineTo(0, 0); 
    var geometry = new THREE.ShapeGeometry(rectShape); 
    var cube = new THREE.Mesh(geometry, material); 
    scene.add(cube); 

    camera.position.z = 1; 

    var render = function() { 
     requestAnimationFrame(render); 
     renderer.render(scene, camera); 
    }; 

    render(); 
} 

這使得我的圖像作爲2D平面形狀,但我堅持我如何能那麼應用效果的圖像。猜測這需要在render內發生,但不確定如何。

還是我最好在普通的WebGL中做這件事?

任何提示/建議/教程非常感謝!

+1

您正在使用[THREE.MeshBasicMaterial]( http://threejs.org/docs/index.html?q=mater#Reference/Materials/MeshBasicMaterial)。看看[THREE.ShaderMaterial](http://threejs.org/docs/index.html?q=mater#Reference/Materials/ShaderMaterial)。這樣的包裝效果可以通過修改着色器中的uv座標來完成(使其響應您可以將當前鼠標座標/時間作爲統一) – mlkn

回答

8

是否有任何理由在WebGL中做到這一點?當然,你可以做的東西一樣,在WebGL的,但你也可以做到這一點在2D畫布

var img = new Image(); 
 
img.onload = start; 
 
img.src = "https://i.imgur.com/v38pV.jpg"; 
 

 
function start() { 
 

 
    var canvas = document.querySelector("canvas"); 
 
    var ctx = canvas.getContext("2d"); 
 

 
    function mix(a, b, l) { 
 
    return a + (b - a) * l; 
 
    } 
 
    
 
    function upDown(v) { 
 
    return Math.sin(v) * 0.5 + 0.5; 
 
    } 
 
    
 
    function render(time) { 
 
    time *= 0.001; 
 

 
    resize(canvas); 
 

 
    var t1 = time; 
 
    var t2 = time * 0.37; 
 

 
    // for each line in the canvas 
 
    for (var dstY = 0; dstY < canvas.height; ++dstY) { 
 
     
 
     // v is value that goes 0 to 1 down the canvas 
 
     var v = dstY/canvas.height; 
 
     
 
     // compute some amount to offset the src 
 
     var off1 = Math.sin((v + 0.5) * mix(3, 12, upDown(t1))) * 300; 
 
     var off2 = Math.sin((v + 0.5) * mix(3, 12, upDown(t2))) * 300; 
 
     var off = off1 + off2; 
 
     
 
     // compute what line of the source image we want 
 
     // NOTE: if off = 0 then it would just be stretching 
 
     // the image down the canvas. 
 
     var srcY = dstY * img.height/canvas.height + off; 
 
     
 
     // clamp srcY to be inside the image 
 
     srcY = Math.max(0, Math.min(img.height - 1, srcY)); 
 

 
     // draw a single line from the src to the canvas 
 
     ctx.drawImage(
 
     img, 
 
     0, srcY, img.width, 1, 
 
     0, dstY, canvas.width, 1); 
 
    }  
 
    
 
    requestAnimationFrame(render); 
 
    } 
 
    requestAnimationFrame(render); 
 

 
    function resize(canvas) { 
 
    var width = canvas.clientWidth; 
 
    var height = canvas.clientHeight; 
 
    if (width != canvas.width || height != canvas.height) { 
 
     canvas.width = width; 
 
     canvas.height = height; 
 
    } 
 
    } 
 
}
body { margin: 0; } 
 
canvas { width: 100vw; height: 100vh; display: block; }
<canvas></canvas>

我不知道自己的精確公式是什麼,但顯然該技術是由一些所謂的靈感slit scan

在WebGL中做它可能會允許更瘋狂的翹曲,因爲你可以很容易地扭曲每個像素而不是每行(canvas中的每個像素API會太慢)。 three.js所將被罰款,但沒有理由用這麼大的圖書館這樣一個小的影響

這裏有一個twgl版本

var vs = ` 
 
attribute vec4 position; 
 

 
varying vec2 v_texcoord; 
 

 
void main() { 
 
    gl_Position = position; 
 
    v_texcoord = position.xy * .5 + .5; 
 
} 
 
`; 
 

 
var fs1 = ` 
 
precision mediump float; 
 

 
uniform float time; 
 
uniform sampler2D tex; 
 

 
varying vec2 v_texcoord; 
 

 
float upDown(float v) { 
 
    return sin(v) * .5 + .5; 
 
} 
 

 
void main() { 
 
    float t1 = time; 
 
    float t2 = time * 0.37; 
 

 
    float v = v_texcoord.y; 
 

 
    float off1 = sin((v + 0.5) * mix(1., 6., upDown(t1))) * .2; 
 
    float off2 = sin((v + 0.5) * mix(1., 3., upDown(t2))) * .2; 
 
    float off = off1 + off2; 
 

 
    // like the canvas2d example if off = 0 then the image will just 
 
    // be flattly stretched down the canvas. "off" is an offset in 
 
    // texture coordinates of which part of the source image to use 
 
    // for the current destination. 
 
    
 
    // In the canvas example off was in pixels since in +1 means use the 
 
    // src image 1 pixel lower than we would have used and -1 = one pixel higher 
 

 
    // In shaders we work in texture coords which go from 0 to 1 regardless 
 
    // of the size of the texture. So for example if the texture was 100 pixels 
 
    // tall then off = 0.01 would offset by 1 pixel. We didn't pass in 
 
    // the size of the canvas nor the size of the texture but of course we 
 
    // we could if we thought that was important. 
 

 
    vec2 uv = vec2(
 
    v_texcoord.x, 
 
    1. - (v + off)); 
 

 
    gl_FragColor = texture2D(tex, uv); 
 
} 
 
`; 
 

 
var fs2 = ` 
 
precision mediump float; 
 

 
uniform float time; 
 
uniform sampler2D tex; 
 

 
varying vec2 v_texcoord; 
 

 
float upDown(float v) { 
 
    return sin(v) * .5 + .5; 
 
} 
 

 
#define PI radians(180.) 
 

 
mat2 rot(float a) { 
 
    float c = cos(a); 
 
    float s = sin(a); 
 
    return mat2(c, s, -s, c); 
 
} 
 

 
float bounce(float v) { 
 
    v = fract(v * .2); 
 
    return mix(v, 2. - v, step(1., v)); 
 
} 
 

 
void main() { 
 
    float t1 = time; 
 
    float t2 = time * 0.37; 
 
    float t3 = time * 0.1; 
 
    float t4 = time * 1.23; 
 

 
    vec2 tc = rot(time * 0.1) * (v_texcoord - 0.25) ; 
 
    vec2 xy = fract(tc * mix(.5, 3., upDown(t4))) * 2. - 1.; 
 
    float a = fract(abs(atan(xy.x, xy.y))/PI + t3); 
 
    float r = bounce(length(xy) + t1); 
 

 
    r = pow(r, mix(0.2, 1., upDown(t2))); 
 
    
 
    vec2 uv = vec2(a, r); 
 

 
    gl_FragColor = texture2D(tex, uv); 
 
} 
 
`; 
 

 
var gl = document.querySelector("canvas").getContext("webgl"); 
 
var programInfo1 = twgl.createProgramInfo(gl, [vs, fs1]); 
 
var programInfo2 = twgl.createProgramInfo(gl, [vs, fs2]); 
 
var bufferInfo = twgl.createBufferInfoFromArrays(gl, { 
 
    position: { 
 
    numComponents: 2, 
 
    data: [ 
 
     -1, -1, 
 
     1, -1, 
 
     -1, 1, 
 
     -1, 1, 
 
     1, -1, 
 
     1, 1, 
 
    ], 
 
    }, 
 
}); 
 
     
 
var texture = twgl.createTexture(gl, { 
 
    src: "https://i.imgur.com/v38pV.jpg", 
 
    crossOrigin: '', 
 
}); 
 

 
var uniforms = { 
 
    tex: texture, 
 
    time: 0, 
 
}; 
 
    
 
var programIndex = 0; 
 
var programInfos = [ 
 
    programInfo1, 
 
    programInfo2, 
 
]; 
 
    
 
function nextProgram() { 
 
    programIndex = (programIndex + 1) % programInfos.length; 
 
} 
 
window.addEventListener('keydown', nextProgram); 
 
window.addEventListener('mousedown', nextProgram); 
 
window.addEventListener('touchstart', nextProgram); 
 
    
 
     
 
function render(time) { 
 
    uniforms.time = time * 0.001; 
 

 
    twgl.resizeCanvasToDisplaySize(gl.canvas); 
 
    gl.viewport(0, 0, gl.canvas.width, gl.canvas.height); 
 

 
    var programInfo = programInfos[programIndex]; 
 
    gl.useProgram(programInfo.program); 
 
     
 
    twgl.setBuffersAndAttributes(gl, programInfo, bufferInfo); 
 
    twgl.setUniforms(programInfo, uniforms); 
 
    twgl.drawBufferInfo(gl, gl.TRIANGLES, bufferInfo); 
 
     
 
    requestAnimationFrame(render); 
 
} 
 
requestAnimationFrame(render);
body { margin: 0; } 
 
canvas { width: 100vw; height: 100vh; display: block; } 
 
.top { position: absolute; left: 5px; top: 5px; color: white; }
<script src="https://twgljs.org/dist/twgl.min.js"></script> 
 
<canvas></canvas> 
 
<div class="top">click to switch effects</div>

+0

哇太棒了,非常感謝,我會看看:) – timothyclifford