連続投稿です。前の記事
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変換
<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>
// 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));
}
}
表示例
綺麗なのは、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