javascript - WebGL2 将 Uint16Array 作为图像渲染到画布
问题描述
我正在尝试Uint16Array
使用 webgl2 纹理将 a 渲染到浏览器中的图像。我有一个工作示例小提琴,Uint8Array
并且正在努力升级到 16 位,因为 webgl 具有陡峭的学习曲线。
工作 8 位小提琴(与下面的片段相同):http: Uint8Array
//jsfiddle.net/njxvftc9/2/
非工作 16 位尝试:http: Uint16Array
//jsfiddle.net/njxvftc9/3/
// image data
var w = 128;
var h = 128;
var size = w * h * 4;
var img = new Uint8Array(size); // need Uint16Array
for (var i = 0; i < img.length; i += 4) {
img[i + 0] = 255; // r
img[i + 1] = i/64; // g
img[i + 2] = 0; // b
img[i + 3] = 255; // a
}
// program
var canvas = document.getElementById('cv');
var gl = canvas.getContext('webgl2');
var program = gl.createProgram();
//var color_buffer_float_16ui = gl.getExtension('EXT_color_buffer_float'); // add for 16-bit
// texture
var tex = gl.createTexture(); // create empty texture
gl.bindTexture(gl.TEXTURE_2D, tex);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
gl.texImage2D(
gl.TEXTURE_2D, // target
0, // mip level
gl.RGBA, // internal format -> gl.RGBA16UI
w, h, // width and height
0, // border
gl.RGBA, //format -> gm.RGBA_INTEGER
gl.UNSIGNED_BYTE, // type -> gl.UNSIGNED_SHORT
img // texture data
);
// buffer
var buffer = gl.createBuffer();
var bufferData = new Float32Array([
-1, -1,
1, -1,
1, 1,
1, 1,
-1, 1,
-1, -1
]);
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
gl.bufferData(gl.ARRAY_BUFFER, bufferData, gl.STATIC_DRAW);
// shaders
program.vs = gl.createShader(gl.VERTEX_SHADER);
gl.shaderSource(program.vs,
"attribute vec4 vertex;\n" + // incoming pixel input?
"varying vec2 pixelCoordinate;\n" + // variable used to pass position to fragment shader
"void main(){\n" +
" gl_Position = vertex;\n" + // set pixel output position to incoming position (pass through)
" pixelCoordinate = vertex.xy*0.5+0.5;\n" + // set coordinate for fragment shader
"}\n");
program.fs = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(program.fs,
"precision highp float;\n" + // ?
"uniform sampler2D tex;\n" + // ?
"varying vec2 pixelCoordinate;\n" + // receive pixel position from vertex shader
"void main(){\n" +
" gl_FragColor = texture2D(tex, pixelCoordinate);\n" + // lookup color in texture image at coordinate position and set color to
"}\n");
gl.compileShader(program.vs);
gl.compileShader(program.fs);
gl.attachShader(program,program.vs);
gl.attachShader(program,program.fs);
gl.deleteShader(program.vs);
gl.deleteShader(program.fs);
// program
gl.bindAttribLocation(program, 0, "vertex");
gl.linkProgram(program);
gl.useProgram(program);
gl.enableVertexAttribArray(0);
gl.vertexAttribPointer(0, 2, gl.FLOAT, false, 0, 0);
gl.clear(gl.COLOR_BUFFER_BIT);
gl.drawArrays(gl.TRIANGLES, 0, 6); // execute program
<canvas id="cv" width="100" height="100"></canvas>
我尝试了许多其他技术,参考了规范,尝试在着色器中转换为浮点,并尝试结合此处看到的方法:在 WebGL2 中渲染到 16 位无符号整数 2D 纹理,但没有成功。
我更喜欢 vanilla js,但只要输入是Uint16Array
,着色器就可以输出任何格式(浮点数等)。
任何在 webgl2 方面有更多经验的人都可以指出我正确的方向或在这里提供一个工作示例小提琴吗?我的代码中是否有一个简单的错误,我是否错过了一个更大的概念,这甚至可能吗?任何帮助是极大的赞赏!
解决方案
如果要将 16 位数据放入纹理中,则需要选择 16 位纹理内部格式。查看内部格式列表,您对 RGBA 的选择是
gl.RGBA16I // 16bit integers
gl.RGBA16UI // 16bit unsigned integers
gl.RGBA16F // 16bit half floats
看起来你选择gl.RGBA16UI
要在着色器中访问该纹理,这条线
uniform sampler2D tex;
必须改变这个
uniform usampler2D tex;
该语法仅适用于 GLSL 3.00 es,因此您需要添加#version 300 es
到两个着色器的顶部。
切换到#version 300 es
也意味着其他语法更改。attribute
变成in
,varying
变成out
在顶点着色器和in
片段着色器中。gl_FragColor
消失,您必须声明自己的输出,例如out vec4 fooColor;
。也texture2D
变得刚刚texture
您不能对整数纹理使用过滤,所以这两行
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
需要更改为
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
当你从纹理中获取数据时,它将是无符号整数,所以这条线
gl_FragColor = texture2D(tex, pixelCoordinate);
最终会变成类似的东西
out vec4 fooColor; // named this fooColor to make it clear you pick the name
void main() {
uvec4 unsignedIntValues = texture(tex, pixelCoordinate);
vec4 floatValues0To65535 = vec4(unsignedIntValues);
vec4 colorValues0To1 = floatValues0To65535 / 65535.0;
fooColor = colorValues0To1;
}
当然,您可以将该数学运算缩短为一行
fooColor = vec4(texture(tex, pixelCoordinate)) / 65535.0;
例子:
// image data
var w = 128;
var h = 128;
var size = w * h * 4;
var img = new Uint16Array(size); // need Uint16Array
for (var i = 0; i < img.length; i += 4) {
img[i + 0] = 65535; // r
img[i + 1] = i/64 * 256; // g
img[i + 2] = 0; // b
img[i + 3] = 65535; // a
}
// program
var canvas = document.getElementById('cv');
var gl = canvas.getContext('webgl2');
var program = gl.createProgram();
//var color_buffer_float_16ui = gl.getExtension('EXT_color_buffer_float'); // add for 16-bit
// texture
var tex = gl.createTexture(); // create empty texture
gl.bindTexture(gl.TEXTURE_2D, tex);
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, // target
0, // mip level
gl.RGBA16UI, // internal format -> gl.RGBA16UI
w, h, // width and height
0, // border
gl.RGBA_INTEGER, //format -> gm.RGBA_INTEGER
gl.UNSIGNED_SHORT, // type -> gl.UNSIGNED_SHORT
img // texture data
);
// buffer
var buffer = gl.createBuffer();
var bufferData = new Float32Array([
-1, -1,
1, -1,
1, 1,
1, 1,
-1, 1,
-1, -1
]);
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
gl.bufferData(gl.ARRAY_BUFFER, bufferData, gl.STATIC_DRAW);
// shaders
program.vs = gl.createShader(gl.VERTEX_SHADER);
gl.shaderSource(program.vs, `#version 300 es
in vec4 vertex; // incoming pixel input?
out vec2 pixelCoordinate; // variable used to pass position to fragment shader
void main(){
gl_Position = vertex; // set pixel output position to incoming position (pass through)
pixelCoordinate = vertex.xy*0.5+0.5; // set coordinate for fragment shader
}
`);
program.fs = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(program.fs, `#version 300 es
precision highp float; // ?
uniform highp usampler2D tex; // ?
in vec2 pixelCoordinate; // receive pixel position from vertex shader
out vec4 fooColor;
void main() {
uvec4 unsignedIntValues = texture(tex, pixelCoordinate);
vec4 floatValues0To65535 = vec4(unsignedIntValues);
vec4 colorValues0To1 = floatValues0To65535 / 65535.0;
fooColor = colorValues0To1;
}
`);
gl.compileShader(program.vs);
checkCompileError(program.vs);
gl.compileShader(program.fs);
checkCompileError(program.fs);
function checkCompileError(s) {
if (!gl.getShaderParameter(s, gl.COMPILE_STATUS)) {
console.error(gl.getShaderInfoLog(s));
}
}
gl.attachShader(program,program.vs);
gl.attachShader(program,program.fs);
gl.deleteShader(program.vs);
gl.deleteShader(program.fs);
// program
gl.bindAttribLocation(program, 0, "vertex");
gl.linkProgram(program);
gl.useProgram(program);
gl.enableVertexAttribArray(0);
gl.vertexAttribPointer(0, 2, gl.FLOAT, false, 0, 0);
gl.clear(gl.COLOR_BUFFER_BIT);
gl.drawArrays(gl.TRIANGLES, 0, 6); // execute program
<canvas id="cv" width="100" height="100"></canvas>
推荐阅读
- javascript - 表单 - 单击按钮时清除所有文本和其他组件元素
- php - 尝试调用函数时 CodeIgniter 出现水印问题
- javascript - 如何将标签和计算函数从 VueCLI 中的组件导出到主 App.vue
- c++ - 如何编写程序来计算这个方程
- azure - 删除 CosmosDB 中的“外键”关系?
- python - 绘制多个条形图。怎么修?
- java - 为什么查询谷歌云文件返回一个空列表?
- git - 在 git 中 rebase 时如何记录合并冲突解决方案?
- python - 对于 scipy 中的非中心卡方,输入“nc”是如何定义的?
- java - 用@DataJpaTest 注释的测试不是用@Autowired 注释的自动装配字段