framebuffer - 如何在帧缓冲区中为 3DTexture 的一层渲染单个像素?
问题描述
我有一个 4x4x4 3DTexture,我正在初始化并正确显示它以着色我的 4x4x4 顶点网格(请参阅带有一个白色像素 - 0,0,0 的附加红色网格)。
但是,当我在帧缓冲区中渲染 4 层(使用 gl.COLOR_ATTACHMENT0 --> gl.COLOR_ATTACHMENT3 一次全部四个层时,我的片段着色器成功渲染了层上的 16 个像素中的 4 个(变为绿色)。
当我只使用 gl.COLOR_ATTACHMENT0 做一层时,相同的 4 个像素显示为 1 层正确更改,而其他 3 层保持原始颜色不变。当我将 gl.viewport(0, 0, size, size) (在此示例中为 size = 4)更改为整个屏幕之类的其他内容,或者大小不超过 4 时,会写入不同的像素,但不会超过 4 . 我的目标是精确地单独指定每一层的所有 16 个像素。我现在使用颜色作为学习经验,但纹理实际上是用于物理模拟的每个顶点的位置和速度信息。我假设(错误的假设?)有 64 个点/顶点,我正在运行顶点着色器和片段着色器各 64 次,每次调用着色一个像素。
我已经从着色器中删除了除重要代码之外的所有代码。我没有改变javascript。我怀疑我的问题是初始化并错误地传递了顶点位置数组。
//Set x,y position coordinates to be used to extract data from one plane of our data cube
//remember, z we handle as a 1 layer of our cube which is composed of a stack of x-y planes.
const oneLayerVertices = new Float32Array(size * size * 2);
count = 0;
for (var j = 0; j < (size); j++) {
for (var i = 0; i < (size); i++) {
oneLayerVertices[count] = i;
count++;
oneLayerVertices[count] = j;
count++;
//oneLayerVertices[count] = 0;
//count++;
//oneLayerVertices[count] = 0;
//count++;
}
}
const bufferInfo = twgl.createBufferInfoFromArrays(gl, {
position: {
numComponents: 2,
data: oneLayerVertices,
},
});
然后我使用 bufferInfo 如下:
gl.useProgram(computeProgramInfo.program);
twgl.setBuffersAndAttributes(gl, computeProgramInfo, bufferInfo);
gl.viewport(0, 0, size, size); //remember size = 4
outFramebuffers.forEach((fb, ndx) => {
gl.bindFramebuffer(gl.FRAMEBUFFER, fb);
gl.drawBuffers([
gl.COLOR_ATTACHMENT0,
gl.COLOR_ATTACHMENT1,
gl.COLOR_ATTACHMENT2,
gl.COLOR_ATTACHMENT3
]);
const baseLayerTexCoord = (ndx * numLayersPerFramebuffer);
console.log("My baseLayerTexCoord is "+baseLayerTexCoord);
twgl.setUniforms(computeProgramInfo, {
baseLayerTexCoord,
u_kernel: [
0, 0, 0,
0, 0, 0,
0, 0, 0,
0, 0, 1,
0, 0, 0,
0, 0, 0,
0, 0, 0,
0, 0, 0,
0, 0, 0,
],
u_position: inPos,
u_velocity: inVel,
loopCounter: loopCounter,
numLayersPerFramebuffer: numLayersPerFramebuffer
});
gl.drawArrays(gl.POINTS, 0, (16));
});
顶点着色器:calc_vertex:
const compute_vs = `#version 300 es
precision highp float;
in vec4 position;
void main() {
gl_Position = position;
}
`;
片段着色器:calc_fragment:
const compute_fs = `#version 300 es
precision highp float;
out vec4 ourOutput[4];
void main() {
ourOutput[0] = vec4(0,1,0,1);
ourOutput[1] = vec4(0,1,0,1);
ourOutput[2] = vec4(0,1,0,1);
ourOutput[3] = vec4(0,1,0,1);
}
`;
解决方案
我不确定你想做什么以及你认为这些职位会做什么。
在 WebGL2 中有 2 个用于 GPU 模拟的选项
使用变换反馈。
在这种情况下,您传入属性并在缓冲区中生成数据。实际上,您拥有输入属性和输出属性,并且通常您只运行顶点着色器。换句话说,您的变量,即顶点着色器的输出,被写入缓冲区。因此,您至少有 2 组缓冲区,currentState 和 nextState,并且您的顶点着色器从 currentState 读取属性并将它们写入 nextState
这里有一个通过变换反馈写入缓冲区的示例,尽管该示例仅在开始时使用变换反馈来填充缓冲区一次。
使用附加到帧缓冲区的纹理
在这种情况下,类似地,您有 2 个纹理,currentState 和 nextState,您将 nextState 设置为您的渲染目标并从 currentState 读取以生成下一个状态。
困难在于您只能通过在顶点着色器中输出图元来渲染纹理。如果 currentState 和 nextState 是 2D 纹理,那就太简单了。只需从顶点着色器输出一个 -1.0 到 +1.0 的四边形,所有像素都
nextState
将被渲染到。如果您使用的是 3D 纹理,那么除了您一次只能渲染到 4 层(嗯,
gl.getParameter(gl.MAX_DRAW_BUFFERS)
)之外,其他的都是一样的。所以你必须做类似的事情for(let layer = 0; layer < numLayers; layer += 4) { // setup framebuffer to use these 4 layers gl.drawXXX(...) // draw to 4 layers) }
或更好
// at init time const fbs = []; for(let layer = 0; layer < numLayers; layer += 4) { fbs.push(createFramebufferForThese4Layers(layer); } // at draw time fbs.forEach((fb, ndx) => {; gl.bindFramebuffer(gl.FRAMEBUFFER, fb); gl.drawXXX(...) // draw to 4 layers) });
我猜多个绘制调用比一个绘制调用慢,因此另一种解决方案是将 2D 纹理视为 3D 数组并适当地计算纹理坐标。
我不知道哪个更好。如果您正在模拟粒子并且它们只需要查看它们自己的 currentState,那么变换反馈就更容易了。如果需要每个粒子能够查看其他粒子的状态,换句话说,您需要随机访问所有数据,那么您唯一的选择是将数据存储在纹理中。
至于职位,我不明白你的代码。位置定义了一个基元,要么POINTS
,,LINES
要么TRIANGLES
将整数 X,Y 值传递到我们的顶点着色器中如何帮助您定义POINTS
,LINES
或者TRIANGLES
?
看起来您正在尝试使用POINTS
在这种情况下您需要设置gl_PointSize
为要绘制的点的大小(1.0),并且您需要将这些位置转换为剪辑空间
gl_Position = vec4((position.xy + 0.5) / resolution, 0, 1);
resolution
纹理的大小在哪里。
但是这样做会很慢。最好只绘制一个全尺寸(-1 到 +1)的剪辑空间四边形。对于目标中的每个像素,都会调用片段着色器。gl_FragCoord.xy
将是当前正在渲染的像素的中心位置,因此左下角的第一个像素gl_FragCoord.xy
将是 (0.5, 0.5)。右侧的像素将是 (1.5, 0.5)。其右侧的像素将是 (2.5, 0.5)。您可以使用该值来计算如何访问currentState
. 假设 1x1 映射最简单的方法是
int n = numberOfLayerThatsAttachedToCOLOR_ATTACHMENT0;
vec4 currentStateValueForLayerN = texelFetch(
currentStateTexture, ivec3(gl_FragCoord.xy, n + 0), 0);
vec4 currentStateValueForLayerNPlus1 = texelFetch(
currentStateTexture, ivec3(gl_FragCoord.xy, n + 1), 0);
vec4 currentStateValueForLayerNPlus2 = texelFetch(
currentStateTexture, ivec3(gl_FragCoord.xy, n + 2), 0);
...
vec4 nextStateForLayerN = computeNextStateFromCurrentState(currentStateValueForLayerN);
vec4 nextStateForLayerNPlus1 = computeNextStateFromCurrentState(currentStateValueForLayerNPlus1);
vec4 nextStateForLayerNPlus2 = computeNextStateFromCurrentState(currentStateValueForLayerNPlus2);
...
outColor[0] = nextStateForLayerN;
outColor[1] = nextStateForLayerNPlus1;
outColor[2] = nextStateForLayerNPlus1;
...
我不知道你是否需要这个,但只是为了测试这里有一个简单的例子,它为 4x4x4 纹理的每个像素呈现不同的颜色,然后显示它们。
const pointVS = `
#version 300 es
uniform int size;
uniform highp sampler3D tex;
out vec4 v_color;
void main() {
int x = gl_VertexID % size;
int y = (gl_VertexID / size) % size;
int z = gl_VertexID / (size * size);
v_color = texelFetch(tex, ivec3(x, y, z), 0);
gl_PointSize = 8.0;
vec3 normPos = vec3(x, y, z) / float(size);
gl_Position = vec4(
mix(-0.9, 0.6, normPos.x) + mix(0.0, 0.3, normPos.y),
mix(-0.6, 0.9, normPos.z) + mix(0.0, -0.3, normPos.y),
0,
1);
}
`;
const pointFS = `
#version 300 es
precision highp float;
in vec4 v_color;
out vec4 outColor;
void main() {
outColor = v_color;
}
`;
const rtVS = `
#version 300 es
in vec4 position;
void main() {
gl_Position = position;
}
`;
const rtFS = `
#version 300 es
precision highp float;
uniform vec2 resolution;
out vec4 outColor[4];
void main() {
vec2 xy = gl_FragCoord.xy / resolution;
outColor[0] = vec4(1, 0, xy.x, 1);
outColor[1] = vec4(0.5, xy.yx, 1);
outColor[2] = vec4(xy, 0, 1);
outColor[3] = vec4(1, vec2(1) - xy, 1);
}
`;
function main() {
const gl = document.querySelector('canvas').getContext('webgl2');
if (!gl) {
return alert('need webgl2');
}
const pointProgramInfo = twgl.createProgramInfo(gl, [pointVS, pointFS]);
const rtProgramInfo = twgl.createProgramInfo(gl, [rtVS, rtFS]);
const size = 4;
const numPoints = size * size * size;
const tex = twgl.createTexture(gl, {
target: gl.TEXTURE_3D,
width: size,
height: size,
depth: size,
});
const clipspaceFullSizeQuadBufferInfo = twgl.createBufferInfoFromArrays(gl, {
position: {
data: [
-1, -1,
1, -1,
-1, 1,
-1, 1,
1, -1,
1, 1,
],
numComponents: 2,
},
});
const fb = gl.createFramebuffer();
gl.bindFramebuffer(gl.FRAMEBUFFER, fb);
for (let i = 0; i < 4; ++i) {
gl.framebufferTextureLayer(
gl.FRAMEBUFFER,
gl.COLOR_ATTACHMENT0 + i,
tex,
0, // mip level
i, // layer
);
}
gl.drawBuffers([
gl.COLOR_ATTACHMENT0,
gl.COLOR_ATTACHMENT1,
gl.COLOR_ATTACHMENT2,
gl.COLOR_ATTACHMENT3,
]);
gl.viewport(0, 0, size, size);
gl.useProgram(rtProgramInfo.program);
twgl.setBuffersAndAttributes(
gl,
rtProgramInfo,
clipspaceFullSizeQuadBufferInfo);
twgl.setUniforms(rtProgramInfo, {
resolution: [size, size],
});
twgl.drawBufferInfo(gl, clipspaceFullSizeQuadBufferInfo);
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
gl.drawBuffers([
gl.BACK,
]);
gl.useProgram(pointProgramInfo.program);
twgl.setUniforms(pointProgramInfo, {
tex,
size,
});
gl.drawArrays(gl.POINTS, 0, numPoints);
}
main();
<canvas></canvas>
<script src="https://twgljs.org/dist/4.x/twgl-full.min.js"></script>
推荐阅读
- python - Tensorflow Loss & Acc 在 CNN 模型中保持不变
- json - 在 Angular 中按 ID 从 JSON 文件中检索数据。TypeError: items.find 不是函数
- wpf - 如何在实时图表中禁用某些图例?
- c# - ASP .NET Core 3.1 IOC - 仅注入一些参数,传递其他参数
- javascript - 网站上的“电话:”正在失去通过的扩展名
- amazon-web-services - Lambda@Edge:无法为指定的 Lambda 函数启用复制
- flutter -
在颤动的堆栈跟踪输出中 - node.js - 基本 NodeJS/PostgreSQL 程序正在输出错误:绑定消息提供 1 个参数,但准备好的语句“”需要 0
- xml - 嵌套元素中的 XML 模式扩展
- domain-driven-design - 在 DDD Aggregate 中修改 Aggregate Root 的子实体的逻辑在哪里?