マイペースなプログラミング日記

DTMやプログラミングにお熱なd-kamiがマイペースに書くブログ

エンボス

久しぶりにWebGLで遊んでみた。とはいっても二次元の画像処理だけど。やったのはエンボスフィルタだと思われる。以下が元の画像とフィルター後の画像。その下にソースコードを載せる

元画像

フィルター後

<!DOCTYPE html>
<html>
  <head>
    <title></title>
    <script>
    
window.onload = function() {
    var image = new Image();
    image.src = "./bakeneko.png";
    
    image.addEventListener('load', function() {
        render(image);
    }, false);
}

function render(image){
    var canvas = document.getElementById("canvas");
    var gl = canvas.getContext("experimental-webgl");
    var vertexShader = createShader(gl, "2d-vertex-shader", gl.VERTEX_SHADER);
    var fragmentShader = createShader(gl, "2d-fragment-shader", gl.FRAGMENT_SHADER);
    
    program = createProgram(gl, vertexShader, fragmentShader);

    setTexCood(gl, program);
    setTexture(gl, image);
    setResolution(gl, program, canvas);
    setImageSize(gl, program, image);
    setPosition(gl, program, image);

    gl.drawArrays(gl.TRIANGLES, 0, 6);
}

function setTexCood(gl, program){
    var texCoordLocation = gl.getAttribLocation(program, "a_texCoord");

    var texCoordBuffer = gl.createBuffer();
    gl.bindBuffer(gl.ARRAY_BUFFER, texCoordBuffer);
    gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
        0.0,  0.0,
        1.0,  0.0,
        0.0,  1.0,
        0.0,  1.0,
        1.0,  0.0,
        1.0,  1.0]), gl.STATIC_DRAW);

    gl.enableVertexAttribArray(texCoordLocation);
    gl.vertexAttribPointer(texCoordLocation, 2, gl.FLOAT, false, 0, 0);
}

function setTexture(gl, image){
    var texture = gl.createTexture();
    gl.bindTexture(gl.TEXTURE_2D, texture);

    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.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image);
}

function setResolution(gl, program, canvas){
    var resolutionLocation = gl.getUniformLocation(program, "u_resolution");
    gl.uniform2f(resolutionLocation, canvas.width, canvas.height);
}

function setImageSize(gl, program, image){
    var sizeLocation = gl.getUniformLocation(program, "u_size");
    gl.uniform2f(sizeLocation, image.width, image.height);
}

function setPosition(gl, program, image){
    var positionLocation = gl.getAttribLocation(program, "a_position");
    var buffer = gl.createBuffer();
    gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
    gl.enableVertexAttribArray(positionLocation);
    gl.vertexAttribPointer(positionLocation, 2, gl.FLOAT, false, 0, 0);
    
    setRectangle(gl, 0, 0, image.width, image.height);
}

function setRectangle(gl, x, y, width, height) {
  var x1 = x;
  var x2 = x + width;
  var y1 = y;
  var y2 = y + height;
  
  gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
     x1, y1,
     x2, y1,
     x1, y2,
     x1, y2,
     x2, y1,
     x2, y2]), gl.STATIC_DRAW);
}

function createShader(gl, shaderId, type) {
    var shader = gl.createShader(type);
    var source = document.getElementById(shaderId).innerHTML;
    
    gl.shaderSource(shader, source);
    gl.compileShader(shader);
    
    return shader;
}

function createProgram(gl, vertexShader, fragmentShader){
    var program = gl.createProgram();

    gl.attachShader(program, vertexShader);
    gl.attachShader(program, fragmentShader);
    gl.linkProgram(program);

    if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
        alert(gl.getShaderInfoLog(vertexShader));
        alert(gl.getShaderInfoLog(fragmentShader));
    }
    
    gl.useProgram(program);
    
    return program;
}

    </script>
    
    <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() {
    vec2 zeroToOne = a_position / u_resolution;
    vec2 zeroToTwo = zeroToOne * 2.0;
    vec2 clipSpace = zeroToTwo - 1.0;

    gl_Position = vec4(clipSpace * vec2(1, -1), 0, 1);
    v_texCoord = a_texCoord;
}
    </script>

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

uniform sampler2D u_image;
uniform vec2 u_size;
varying vec2 v_texCoord;

void main() {
    float weight[9];
    vec2 offset[9];
    
    float dx = 1.0 / u_size.x;
    float dy = 1.0 / u_size.y;
    
    for(int i = 0; i < 3; i++){
        for(int j = 0; j < 3; j++){
            offset[i + j * 3] = vec2(float(-(2 / 2 + i)) * dx, float(-(2 / 2 + i)) * dy);
        }
    }
    
    for(int i = 0; i < 3; i++){
        weight[i * 3] = -1.0;
        weight[i * 3 + 1] = 0.0;
        weight[i * 3 + 2] = 1.0;
    }
    
    vec3 col = vec3(0.0);
    
    for(int j = 0; j < 9; j++){
        col += texture2D(u_image, v_texCoord + offset[j]).rgb * weight[j];
    }

    col += texture2D(u_image, v_texCoord).rgb;
    
    gl_FragColor = vec4(col, 1.0);
}
      </script>
      
  </head>
  <body>
    <canvas id="canvas" width="128" height="128"></canvas>
  </body>
</html>