2016-08-18 25 views
6

Uczę się gry three.js, próbując eksperymentować z przekształcaniem obrazów.Utwórz efekt dopasowania obrazu w WebGL i three.js

bardzo podoba mi się efekt pokazany here.

Jakie kroki chciałbym wykonać, aby być w stanie przekształcić Podobne do tego?

Do tej pory mam:

// 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(); 
} 

co czyni mój obraz jako płaski kształt 2D ale utknąłem w jaki sposób można następnie zastosować efekty do obrazu. Zgadywanie tego musi się zdarzyć wewnątrz render, ale nie wiesz, jak to zrobić.

A może lepiej zrobię to w zwykłym WebGL?

Wszelkie porady/porady/samouczki są doceniane!

+1

używasz [THREE.MeshBasicMaterial] (http://threejs.org/docs/index.html?q=mater#Reference/Materials/MeshBasicMaterial). Zajrzyj do [THREE.ShaderMaterial] (http://threejs.org/docs/index.html?q=mater#Reference/Materials/ShaderMaterial). Taki efekt owijania mógłby zostać dokonany poprzez modyfikację współrzędnych uv w twoich shaderów (aby uczynić go responsywnym mogłeś przekazać bieżący koordynator myszy/czas jako jednolity) – mlkn

Odpowiedz

8

Czy jest jakiś powód do zrobienia tego w WebGL? Jasne, można robić takie rzeczy w WebGL, ale można też zrobić to w płótnie 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>

nie mam pojęcia, co ich dokładna formuła jest tylko pozornie technika jest inspirowane przez coś zwane slit scan.

Robienie tego w WebGL prawdopodobnie pozwoliłoby na bardziej szalone wypaczenia, ponieważ można łatwo wypaczyć na piksel zamiast tylko na linię (na piksel w kanwie API byłoby zbyt wolne). Three.js byłoby w porządku, ale nie ma powodu, aby użyć takiego dużą bibliotekę dla takiego małego efektu

Oto twgl wersji

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

Wow, niesamowite, dziękuję bardzo, że się rozejrzę :) – timothyclifford