LoginSignup
4
7

More than 5 years have passed since last update.

webGLでyuv2rgb変換(10bit)

Last updated at Posted at 2017-02-25

連続投稿です。前の記事
webGLでto変換
では、8bitを想定していましたが、時代は10bitです。で、10bit化したところ、ハマりましたので記事にします。

gl.texImage2D(gl.TEXTURE_2D, 0, gl.LUMINANCE, w, h, 0, gl.LUMINANCE, gl.UNSIGNED_SHORT, bufY);
とすると、
GL ERROR :GL_INVALID_OPERATION : glTexImage2D: invalid internalformat/format/type combination GL_LUMINANCE/GL_LUMINANCE/GL_UNSIGNED_SHORT
と怒られ、gl.LUMINANCE16もdame
openGLでなくwebGLだからか、
http://stackoverflow.com/questions/31808920/displaying-16bit-unsigned-integers-in-opengl
もダメ。

ポイント

美しくないが以下の通り解決。

16bitのまま動作させる方法がない
http://stackoverflow.com/questions/6413744/looking-to-access-16-bit-image-data-in-javascript-webgl
ので、8bit2つとして後から合成します。ちょうど、formatとして2つの8ビットコンポーネントからなる「gl.LUMINANCE_ALPHA」があるのでそれを使うことにする。

変更

  • Uint8Arrayのバッファサイズを、以下のように2倍で確保
var dataY = new Uint8Array(w*h*2);
var dataU = new Uint8Array(w2*h2*2);
var dataV = new Uint8Array(w2*h2*2);

− 10ビットデータとして描画(自分で入力画像を生成するなら、読み出すなら不要)

    val = 1023 - parseInt(i * 1023 / w);
    dataY[i*2 + w*2 * j + 0] = val & 255;
    dataY[i*2 + w*2 * j + 1] = val >> 8;
  • gl.LUMINANCEをgl.LUMINANCE_ALPHA
gl.texImage2D(gl.TEXTURE_2D, 0, gl.LUMINANCE_ALPHA, w, h, 0, gl.LUMINANCE_ALPHA, gl.UNSIGNED_BYTE, dataY);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.LUMINANCE_ALPHA, w2, h2, 0, gl.LUMINANCE_ALPHA, gl.UNSIGNED_BYTE, dataU);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.LUMINANCE_ALPHA, w2, h2, 0, gl.LUMINANCE_ALPHA, gl.UNSIGNED_BYTE, dataV);
  • 上下バイト(以下では、rコンポーネントとaコンポーネント)を組み合わせて描画
      yuv.x = ((texture2D(samplerY, v_texCoord).r + texture2D(samplerY, v_texCoord).a*256.0)/4.0 - (16.0 / 255.0)); 
      yuv.y = ((texture2D(samplerU, v_texCoord).r + texture2D(samplerU, v_texCoord).a*256.0)/4.0 - 0.5);
      yuv.z = ((texture2D(samplerV, v_texCoord).r + texture2D(samplerV, v_texCoord).a*256.0)/4.0 - 0.5);

ソース全文(10ビット版)

前記事にも書きましたが、ほぼ98%以下のwebGLtestさんのソースと同じです。
http://jsdo.it/webGLtest/Opoi

8ビット版は前記事参照
webGLでto変換

index_10b.html;
<head>
  <title> YUV2RGB sample </title>
  <script type="x-shader/x-vertex" id="vs">
    uniform vec2 u_resolution;
    attribute vec2 a_position;
    attribute vec2 a_texCoord;
    varying vec2 v_texCoord;
    void main(void) {
      v_texCoord = a_texCoord;
      vec2 pos = a_position / u_resolution;
      gl_Position = vec4(pos, 0, 1);
  }
  </script>
  <script type="x-shader/x-fragment" id="fs">
    precision mediump float; 
    uniform sampler2D samplerY;
    uniform sampler2D samplerU;
    uniform sampler2D samplerV;
    const mat3 kColorConv = mat3( 1.164, 1.164, 1.164, 0.0, -0.213, 2.112, 1.793, -0.533, 0.0 ); 
    varying vec2 v_texCoord;
    void main(void) {
      mediump vec3 yuv;
      lowp vec3 rgb;
// 8bit
//      yuv.x = (texture2D(samplerY, v_texCoord).x - (16.0 / 255.0)); 
//      yuv.y = (texture2D(samplerU, v_texCoord).x - 0.5);
//      yuv.z = (texture2D(samplerV, v_texCoord).x - 0.5);

//10bit
      yuv.x = ((texture2D(samplerY, v_texCoord).r + texture2D(samplerY, v_texCoord).a*256.0)/4.0 - (16.0 / 255.0)); 
      yuv.y = ((texture2D(samplerU, v_texCoord).r + texture2D(samplerU, v_texCoord).a*256.0)/4.0 - 0.5);
      yuv.z = ((texture2D(samplerV, v_texCoord).r + texture2D(samplerV, v_texCoord).a*256.0)/4.0 - 0.5);
      rgb = kColorConv * yuv;
      gl_FragColor = vec4(rgb, 1.0);
    }
  </script>
</head>

<body>
  <canvas id="target" />
  <script type="text/javascript" src="./yuv2rgb_10b.js"></script>
</body>

yuv2rgb_10b.js
// canvasエレメントを取得
let c = document.getElementById('target');
c.width = 320;
c.height = 240;
// webglコンテキストを取得
let gl = c.getContext('webgl') || c.getContext('experimental-webgl');
// canvasを初期化する色を設定する
gl.clearColor(0.0, 1.0, 1.0, 1.0);
// canvasを初期化
gl.clear(gl.COLOR_BUFFER_BIT);
// 頂点シェーダとフラグメントシェーダの生成
const v_shader = create_shader('vs');
const f_shader = create_shader('fs');
// プログラムオブジェクトの生成とリンク
let prg = create_program(v_shader, f_shader);

gl.viewport(0, 0, c.width, c.height);
let resolutionUniformLocation = gl.getUniformLocation(prg, "u_resolution");
gl.uniform2f(resolutionUniformLocation, gl.canvas.width, gl.canvas.height);

// y要素のtexture
const w = 320, h = 240;
var dataY = new Uint8Array(w*h); // 8bit
var dataY = new Uint8Array(w*h*2); // 10bit
for (var i = 0; i < w; ++i) {
  for (var j = 0; j < h; ++j) {
//8bit
    //    dataY[i + w * j] = 255 - parseInt(i * 255 / w);
//10bit
    val = 1023 - parseInt(i * 1023 / w);
    dataY[i*2 + w*2 * j + 0] = val & 255;
    dataY[i*2 + w*2 * j + 1] = val >> 8;
  }
}
// cbcr要素のtexture
const w2 = w / 2;
const h2 = h / 2;
//dataU = new Uint8Array(w2*h2); // 8bit
//dataV = new Uint8Array(w2*h2); // 8bit
dataU = new Uint8Array(w2*h2*2); // 10bit
dataV = new Uint8Array(w2*h2*2); // 10bit
for (var i = 0; i < w2; ++i) {
  for (var j = 0; j < h2; ++j) {
//8bit
//    dataU[i + w2 * j] = 255 - parseInt(j * 255 / h2);
//    dataV[i + w2 * j] = parseInt(j * 255 / h2);
//10bit
    valu = 1023 - parseInt(j * 1023 / h2);
    valv = parseInt(j * 1023 / h2);
    dataU[i*2 + w2*2 * j    ] = valu & 255;
    dataU[i*2 + w2*2 * j + 1] = valu >> 8;
    dataV[i*2 + w2*2 * j    ] = valv & 255;
    dataV[i*2 + w2*2 * j + 1] = valv >> 8;
  }
}
// テクスチャの設定
gl.activeTexture(gl.TEXTURE0);
let texture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, texture);
// gl.texImage2D(gl.TEXTURE_2D, 0, gl.LUMINANCE, w, h, 0, gl.LUMINANCE, gl.UNSIGNED_BYTE, dataY); // 8bit
gl.texImage2D(gl.TEXTURE_2D, 0, gl.LUMINANCE_ALPHA, w, h, 0, gl.LUMINANCE_ALPHA, gl.UNSIGNED_BYTE, dataY); // 10bit
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
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.uniform1i(gl.getUniformLocation(prg, 'samplerY'), 0);
gl.activeTexture(gl.TEXTURE1);
texture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, texture);
// gl.texImage2D(gl.TEXTURE_2D, 0, gl.LUMINANCE, w2, h2, 0, gl.LUMINANCE, gl.UNSIGNED_BYTE, dataU); // 8bit
gl.texImage2D(gl.TEXTURE_2D, 0, gl.LUMINANCE_ALPHA, w2, h2, 0, gl.LUMINANCE_ALPHA, gl.UNSIGNED_BYTE, dataU); // 10bit
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
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.uniform1i(gl.getUniformLocation(prg, 'samplerU'), 1);
gl.activeTexture(gl.TEXTURE2);
texture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, texture);
// gl.texImage2D(gl.TEXTURE_2D, 0, gl.LUMINANCE, w2, h2, 0, gl.LUMINANCE, gl.UNSIGNED_BYTE, dataV); // 8bit
gl.texImage2D(gl.TEXTURE_2D, 0, gl.LUMINANCE_ALPHA, w2, h2, 0, gl.LUMINANCE_ALPHA, gl.UNSIGNED_BYTE, dataV); // 10bit
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
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.uniform1i(gl.getUniformLocation(prg, 'samplerV'), 2);

// 頂点情報の登録
let attLocation = gl.getAttribLocation(prg, 'a_position');
gl.enableVertexAttribArray(attLocation);
const vertex_position = new Float32Array([
  -w, -h,
  w, -h,
  w, h,
  -w, h,
]);
gl.bindBuffer(gl.ARRAY_BUFFER, gl.createBuffer());
gl.bufferData(gl.ARRAY_BUFFER, vertex_position, gl.STATIC_DRAW);
gl.vertexAttribPointer(attLocation, 2, gl.FLOAT, false, 0, 0);

// テクスチャ一頂点情報の登録
let uvLocation = gl.getAttribLocation(prg, 'a_texCoord');
gl.enableVertexAttribArray(uvLocation);
const uv_position = new Float32Array([
  0.0, 1.0,
  1.0, 1.0,
  1.0, 0.0,
  0.0, 0.0
]);
gl.bindBuffer(gl.ARRAY_BUFFER, gl.createBuffer());
gl.bufferData(gl.ARRAY_BUFFER, uv_position, gl.STATIC_DRAW);
gl.vertexAttribPointer(uvLocation, 2, gl.FLOAT, false, 0, 0);
// 描画
gl.drawArrays(gl.TRIANGLE_FAN, 0, 4);
// 表示更新
gl.flush();

// シェーダを生成する関数
function create_shader(id) {
  // シェーダを格納する変数
  let shader;
  // HTMLからscriptタグへの参照を取得
  let scriptElement = document.getElementById(id);
  // scriptタグが存在しない場合は抜ける
  if (!scriptElement) { return; }
  // scriptタグのtype属性をチェック
  switch (scriptElement.type) {
    // 頂点シェーダの場合
    case 'x-shader/x-vertex':
      shader = gl.createShader(gl.VERTEX_SHADER);
      break;
    // フラグメントシェーダの場合
    case 'x-shader/x-fragment':
      shader = gl.createShader(gl.FRAGMENT_SHADER);
      break;
    default:
      return;
  }
  // 生成されたシェーダにソースを割り当てる
  gl.shaderSource(shader, scriptElement.text);
  // シェーダをコンパイルする
  gl.compileShader(shader);
  // シェーダが正しくコンパイルされたかチェック
  if (gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
    // 成功していたらシェーダを返して終了
    return shader;
  } else {
    // 失敗していたらエラーログをアラートする
    alert(gl.getShaderInfoLog(shader));
  }
}

// プログラムオブジェクトを生成しシェーダをリンクする関数
function create_program(vs, fs) {
  // プログラムオブジェクトの生成
  var program = gl.createProgram();
  // プログラムオブジェクトにシェーダを割り当てる
  gl.attachShader(program, vs);
  gl.attachShader(program, fs);
  // シェーダをリンク
  gl.linkProgram(program);
  // シェーダのリンクが正しく行なわれたかチェック
  if (gl.getProgramParameter(program, gl.LINK_STATUS)) {
    // 成功していたらプログラムオブジェクトを有効にする
    gl.useProgram(program);
    // プログラムオブジェクトを返して終了
    return program;
  } else {
    // 失敗していたらエラーログをアラートする
    alert(gl.getProgramInfoLog(program));
  }
}

表示例

スクリーンショット 2017-02-25 15.35.41.png

綺麗なのは、webGLtestさんのサンプルの画像が綺麗なだけで。。。
http://jsdo.it/webGLtest/Opoi

参考情報

Webgl Fundamentals
https://webglfundamentals.org/webgl/lessons/webgl-fundamentals.html

第十一回 WebGLスクール「キューブ環境マッピング」
http://qiita.com/konweb/items/262b107b2ea9a6113776#_reference-c79295fe4df8c81ed4bf

wgld.org
https://wgld.org/sitemap.html

4
7
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
7