2011-12-28 142 views
5

我打算在JS中創建一個簡單的照片編輯器。我的主要問題是,是否可以創建實時渲染的濾鏡?例如,調整亮度和飽和度。我需要的是一個2D圖像,我可以使用GPU應用濾鏡。使用WebGL 2D圖像處理

我讀過的所有教程都非常複雜,並沒有真正解釋API的含義。請指向正確的方向。謝謝。

回答

3

您可以爲您打算使用的每項操作製作自定義像素着色器。只需學習一些GLSL並按照「Learning WebGL」教程來掌握基本的WebGL。

您可以使用着色器渲染圖像,修改可以包含的參數以控制不同的視覺樣式,然後當用戶單擊「確定」時,您可以讀回像素以將其存儲爲當前圖像。

只要記住避免跨域圖像,因爲這將禁用像素的回讀。

此外,請查看quick reference card(PDF)瞭解着色器操作的快速信息。

+0

現在我可以在畫布上呈現平面圖像。是否可以在沒有3D部分的情況下編寫它?至於學習資料,我應該怎麼看?似乎有OpenGL ES,正常的OpenGL,GLSL,WebGL以及所有不同的版本。 – 2011-12-29 05:41:25

+0

你可以繪製你的紋理與地形投影屏幕對齊四。從學習資料中,您可以關注GLSL,這是您要用於效果的語言 – Chiguireitor 2011-12-29 16:37:08

17

我打算寫一篇教程並將其發佈到我的博客上,但我不知道什麼時候我會有時間完成,所以這裏是我所擁有的 Here's a more detailed set of posts on my blog

WebGL實際上是一個光柵化庫。我需要屬性(數據流),制服(變量),並期望您提供2d的「剪輯空間」座標和像素的顏色數據。

下面是WebGL的2d的一個簡單的例子(一些細節留出)

// Get A WebGL context 
var gl = canvas.getContext("experimental-webgl"); 

// setup GLSL program 
vertexShader = createShaderFromScriptElement(gl, "2d-vertex-shader"); 
fragmentShader = createShaderFromScriptElement(gl, "2d-fragment-shader"); 
program = createProgram(gl, vertexShader, fragmentShader); 
gl.useProgram(program); 

// look up where the vertex data needs to go. 
var positionLocation = gl.getAttribLocation(program, "a_position"); 

// Create a buffer and put a single clipspace rectangle in 
// it (2 triangles) 
var buffer = gl.createBuffer(); 
gl.bindBuffer(gl.ARRAY_BUFFER, buffer); 
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([ 
    -1.0, -1.0, 
    1.0, -1.0, 
    -1.0, 1.0, 
    -1.0, 1.0, 
    1.0, -1.0, 
    1.0, 1.0]), gl.STATIC_DRAW); 
gl.enableVertexAttribArray(positionLocation); 
gl.vertexAttribPointer(positionLocation, 2, gl.FLOAT, false, 0, 0); 

// draw 
gl.drawArrays(gl.TRIANGLES, 0, 6); 

這裏的2個着色

<script id="2d-vertex-shader" type="x-shader/x-vertex"> 
attribute vec2 a_position; 
void main() { 
    gl_Position = vec4(a_position, 0, 1); 
} 
</script> 

<script id="2d-fragment-shader" type="x-shader/x-fragment"> 
void main() { 
    gl_FragColor = vec4(0,1,0,1); // green 
} 
</script> 

這將繪製一個綠色矩形畫布的整體尺寸。

在WebGL中,您有責任提供一個提供剪輯空間座標的頂點着色器。無論畫布的大小如何,剪輯空間座標總是從-1到+1。 如果你想圖3d它給你提供從3D轉換到2d着色器,因爲WebGL是唯一一個光柵化API

在一個簡單的例子,如果你想以像素爲單位工作,你可以通過在一個矩形使用像素代替剪輯空間座標和轉換着色器

例如裁剪空間:

<script id="2d-vertex-shader" type="x-shader/x-vertex"> 
attribute vec2 a_position; 

uniform vec2 u_resolution; 

void main() { 
    // convert the rectangle from pixels to 0.0 to 1.0 
    vec2 zeroToOne = a_position/u_resolution; 

    // convert from 0->1 to 0->2 
    vec2 zeroToTwo = zeroToOne * 2.0; 

    // convert from 0->2 to -1->+1 (clipspace) 
    vec2 clipSpace = zeroToTwo - 1.0; 

    gl_Position = vec4(clipSpace, 0, 1); 
} 
</script> 

現在我們可以通過改變數據繪製矩形我們提供

// set the resolution 
var resolutionLocation = gl.getUniformLocation(program, "u_resolution"); 
gl.uniform2f(resolutionLocation, canvas.width, canvas.height); 

// setup a rectangle from 10,20 to 80,30 in pixels 
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([ 
    10, 20, 
    80, 20, 
    10, 30, 
    10, 30, 
    80, 20, 
    80, 30]), gl.STATIC_DRAW); 

您會注意到WebGL認爲右下角爲0,0。爲了讓它成爲更傳統的用於2D圖形的右上角,我們只需翻轉y座標。

gl_Position = vec4(clipSpace * vec2(1, -1), 0, 1); 

你想操縱你需要傳遞的紋理圖像。以同樣的方式在畫布的大小表示通過clipspace座標紋理是由去從0到1

<script id="2d-vertex-shader" type="x-shader/x-vertex"> 
attribute vec2 a_position; 
attribute vec2 a_texCoord; 

uniform vec2 u_resolution; 

varying vec2 v_texCoord; 

void main() { 
    // convert the rectangle from pixels to 0.0 to 1.0 
    vec2 zeroToOne = a_position/u_resolution; 

    // convert from 0->1 to 0->2 
    vec2 zeroToTwo = zeroToOne * 2.0; 

    // convert from 0->2 to -1->+1 (clipspace) 
    vec2 clipSpace = zeroToTwo - 1.0; 

    gl_Position = vec4(clipSpace, 0, 1); 

    // pass the texCoord to the fragment shader 
    // The GPU will interpolate this value between points. 
    v_texCoord = a_texCoord; 
} 
</script> 

<script id="2d-fragment-shader" type="x-shader/x-fragment"> 
precision float mediump; 

// our texture 
uniform sampler2D u_image; 

// the texCoords passed in from the vertex shader. 
varying vec2 v_texCoord; 

void main() { 
    gl_FragColor = texture2D(u_image, v_texCoord); 
} 
</script> 

要繪製的圖像紋理座標引用需要加載圖像並自該異步我們需要發生稍微改變我們的代碼。如果你想要做的圖像處理你只需要改變你的shader採取所有我們的代碼,並把它放在一個調用的函數「渲染」

var image = new Image(); 
image.src = "http://someimage/on/our/server"; // MUST BE SAME DOMAIN!!! 
image.onload = function() { 
    render(); 
} 

function render() { 
    ... 
    // all the code we had before except gl.draw 


    // look up where the vertex data needs to go. 
    var texCoordLocation = gl.getAttribLocation(program, "a_texCoord"); 

    // provide texture coordinates for the rectangle. 
    var texCoordBuffer = gl.createBuffer(); 
    gl.bindBuffer(gl.ARRAY_BUFFER, texCoordBuffer); 
    gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([ 
     1.0, 1.0, 
     0.0, 1.0, 
     0.0, 0.0, 
     1.0, 1.0, 
     0.0, 0.0, 
     1.0, 0.0]), gl.STATIC_DRAW); 
    gl.enableVertexAttribArray(texCoordLocation); 
    gl.vertexAttribPointer(texCoordLocation, 2, gl.FLOAT, false, 0, 0); 

    var texture = gl.createTexture(); 
    gl.bindTexture(gl.TEXTURE_2D, texture); 
    gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image); 
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); 
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); 
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST); 
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST); 

    gl.draw(...) 

。例如,交換紅色和藍色

void main() { 
    gl_FragColor = texture2D(u_image, v_texCoord).bgra; 
} 

或混合與旁邊的像素。

uniform vec2 u_textureSize; 

void main() { 
    vec2 onePixel = vec2(1.0, 1.0)/u_textureSize; 
    gl_FragColor = (texture2D(u_image, v_texCoord) + 
        texture2D(u_image, v_texCoord + vec2(onePixel.x, 0.0)) + 
        texture2D(u_image, v_texCoord + vec2(-onePixel.x, 0.0)))/3.0; 
} 

,我們在紋理

var textureSizeLocation = gl.getUniformLocation(program, "u_textureSize"); 
... 
gl.uniform2f(textureSizeLocation, image.width, image.height); 

等的大小通...點擊下面的最後一個環節進行卷積樣品。

這裏用一個稍微不同的發展

Draw Rect in Clip Space

Draw Rect in Pixels

Draw Rect with origin at top left

Draw a bunch of rects in different colors

Draw an image

工作版本

Draw an image red and blue swapped

Draw an image with left and right pixels averaged

Draw an image with a 3x3 convolution

Draw an image with multiple effects

+0

感謝您的詳細解釋。我在這裏遇到一些小問題,希望你知道。 http://stackoverflow.com/questions/8679596/extra-space-in-webgl-despite-same-dimension – 2011-12-31 03:08:35

+0

還有一個問題。如何根據用戶輸入動態調用功能組合?例如,模糊+飽和度或只是單獨銳化。在你的例子中,我看到你使用內核,這是我不明白的。謝謝。 – 2011-12-31 03:57:04

+0

我從這裏得到了內核的東西(http://docs.gimp.org/en/plug-in-convmatrix.html)和這裏(http://www.codeproject.com/KB/graphics/ImageConvolution。aspx) 至於添加它們,你有兩件事我的頭頂。 #1)您可以嘗試生成着色器。 #2)您可以渲染到幀緩衝區併爲每個步驟重複 – gman 2011-12-31 06:19:09

0

試試看glfx(http://evanw.github.com/glfx.js/) 我認爲這正是你所需要的。 你可以使用一套預定義的着色器或輕鬆添加你的;) 享受! glfx非常簡單!

<script src="glfx.js"></script> 
<script> 

window.onload = function() { 
    // try to create a WebGL canvas (will fail if WebGL isn't supported) 
    try { 
     var canvas = fx.canvas(); 
    } catch (e) { 
     alert(e); 
     return; 
    } 

    // convert the image to a texture 
    var image = document.getElementById('image'); 
    var texture = canvas.texture(image); 

    // apply the ink filter 
    canvas.draw(texture).ink(0.25).update(); 

    // replace the image with the canvas 
    image.parentNode.insertBefore(canvas, image); 
    image.parentNode.removeChild(image); 
}; 

</script> 
<img id="image" src="image.jpg">