更新
在下面我的问题结尾处查看基本原理
使用WebGL2
,我可以通过非规范化坐标访问纹理像素(抱歉,此术语不正确)。这意味着我不必像在texture2D()
中那样将它们缩小到0-1。
但是,片段着色器的输入仍然是归一化值中的vec2/3
。
是否可以在Vertex和Frag着色器中声明输入/输出变量,从而不必缩放坐标?
顶点着色器中的某处:
...
out vec2 TextureCoordinates;
在片段着色器中的某处:
...
in vec2 TextureCoordinates;
我希望TextureCoordinates
成为ivec2
并已经缩放。
这个问题和我在webgl上的所有其他问题都与使用WebGL进行通用计算有关。我们正在尝试使用WebGL进行张量(多维矩阵)操作。
我们以几种方式将数据映射到纹理。我们遵循的最简单的方法是-假设我们可以将数据作为平面数组访问-沿纹理的宽度将其布局并提高纹理的高度,直到完成。
由于我们的思维,逻辑和计算都基于张量/矩阵索引-在片段着色器内部-我们必须将X-Y纹理坐标映射回索引/从索引映射回索引。这里的中间步骤是为纹理像素的给定位置计算偏移量。然后从该偏移量可以从其步幅计算矩阵索引。
使用整数坐标计算webgl 1中非常大的纹理的偏移量似乎要比webgl2花费更长的时间。见下文:
WebGL 1偏移量计算
int coordsToOffset(vec2 coords, int width, int height) {
float s = coords.s * float(width);
float t = coords.t * float(height);
int offset = int(t) * width + int(s);
return offset;
}
vec2 offsetToCoords(int offset, int width, int height) {
int t = offset / width;
int s = offset - t*width;
vec2 coords = (vec2(s,t) + vec2(0.5,0.5)) / vec2(width, height);
return coords;
}
存在int坐标的WebGL 2偏移量计算
int coordsToOffset(ivec2 coords, int width) {
return coords.t * width + coords.s;
}
ivec2 offsetToCoords(int offset, int width) {
int t = offset / width;
int s = offset - t*width;
return ivec2(s,t);
}
很明显,对于一系列大型纹理操作,仅在偏移/坐标计算上,我们就节省了数十万次操作。
答案 0 :(得分:1)
尚不清楚您为什么想要做自己想做的事情。最好问类似“我正在尝试绘制图像/实现后处理发光/做射线追踪/ ...,并希望使用未归一化的纹理坐标的事情,因为”,然后我们可以告诉您是否可以使用您的解决方案以及解决方法。
在任何情况下,都支持通过int
或unsigned int
或ivec2/3/4
或uvec2/3/4
作为变量,但不插值。您必须将它们声明为flat
。
仍然,您可以将未归一化的值传递为float
或vec2/3/4
,并在片段着色器中将其转换为int
,ivec2/3/4
。
另一个问题是,您将无法使用texelFetch
进行采样,该函数采用texel坐标而不是归一化纹理坐标。它仅返回单个像素的精确值。它不像普通的texture
函数那样支持过滤。
示例:
function main() {
const gl = document.querySelector('canvas').getContext('webgl2');
if (!gl) {
return alert("need webgl2");
}
const vs = `
#version 300 es
in vec4 position;
in ivec2 texelcoord;
out vec2 v_texcoord;
void main() {
v_texcoord = vec2(texelcoord);
gl_Position = position;
}
`;
const fs = `
#version 300 es
precision mediump float;
in vec2 v_texcoord;
out vec4 outColor;
uniform sampler2D tex;
void main() {
outColor = texelFetch(tex, ivec2(v_texcoord), 0);
}
`;
// compile shaders, link program, look up locations
const programInfo = twgl.createProgramInfo(gl, [vs, fs]);
// create buffers via gl.createBuffer, gl.bindBuffer, gl.bufferData)
const bufferInfo = twgl.createBufferInfoFromArrays(gl, {
position: {
numComponents: 2,
data: [
-.5, -.5,
.5, -.5,
0, .5,
],
},
texelcoord: {
numComponents: 2,
data: new Int32Array([
0, 0,
15, 0,
8, 15,
]),
}
});
// make a 16x16 texture
const ctx = document.createElement('canvas').getContext('2d');
ctx.canvas.width = 16;
ctx.canvas.height = 16;
for (let i = 23; i > 0; --i) {
ctx.fillStyle = `hsl(${i / 23 * 360 | 0}, 100%, ${i % 2 ? 25 : 75}%)`;
ctx.beginPath();
ctx.arc(8, 15, i, 0, Math.PI * 2, false);
ctx.fill();
}
const tex = twgl.createTexture(gl, { src: ctx.canvas });
gl.useProgram(programInfo.program);
twgl.setBuffersAndAttributes(gl, programInfo, bufferInfo);
// no need to set uniforms since they default to 0
// and only one texture which is already on texture unit 0
gl.drawArrays(gl.TRIANGLES, 0, 3);
}
main();
<canvas></canvas>
<script src="https://twgljs.org/dist/4.x/twgl-full.min.js"></script>
因此,对于您更新的问题,仍然不清楚您想做什么。为什么要将变量传递给片段着色器?您不能在片段着色器本身中做任何想要的数学运算吗?
示例:
uniform sampler2D tex;
out float result;
// some all the values in the texture
vec4 sum4 = vec4(0);
ivec2 texDim = textureSize(tex, 0);
for (int y = 0; y < texDim.y; ++y) {
for (int x = 0; x < texDim.x; ++x) {
sum4 += texelFetch(tex, ivec2(x, y), 0);
}
}
result = sum4.x + sum4.y + sum4.z + sum4.w;
Example2
uniform isampler2D indices;
uniform sampler2D data;
out float result;
// some only values in data pointed to by indices
vec4 sum4 = vec4(0);
ivec2 texDim = textureSize(indices, 0);
for (int y = 0; y < texDim.y; ++y) {
for (int x = 0; x < texDim.x; ++x) {
ivec2 index = texelFetch(indices, ivec2(x, y), 0).xy;
sum4 += texelFetch(tex, index, 0);
}
}
result = sum4.x + sum4.y + sum4.z + sum4.w;
请注意,我也不是GPGPU方面的专家,但是我有一种直觉,上面的代码并不是最快的方法,因为我相信并行化是基于输出发生的。上面的代码只有1个输出,因此没有并行化?更改很容易,因此它将块ID,图块ID,区域ID作为输入,并仅计算该区域的总和。然后,用每个块的总和写出较大的纹理,最后将块的总和求和。
此外,依赖和不均匀的纹理读取也是已知的性能问题。第一个示例按顺序读取纹理。那是缓存友好的。第二个示例以随机顺序(由索引指定)读取纹理,这对缓存不友好。