Three.js 中使用 FBO/RenderTarget 的 GLSL uv 查找和精度
GLSL uv lookup and precision with FBO / RenderTarget in Three.js
我的应用程序使用 Javascript + Three.js / WebGL + GLSL 编码。我有 200 条曲线,每条曲线由 85 个点组成。为了使曲线动画化,我添加了一个新点并删除了最后一个点。
所以我制作了一个 positions
着色器将新位置存储到纹理 (1) 和 lines
着色器将所有曲线的位置写入另一个纹理 (2)。
目标是将纹理用作数组:我知道一行的第一个和最后一个索引,所以我需要将这些索引转换为 uv
坐标。
我使用 FBOHelper 调试 FBO。
1) 此 1D 纹理包含每条曲线的新点(总共 200 个):positionTexture
2) 这些是 200 条曲线及其所有点,一个接一个:linesTexture
黑色部分是这里的BUG。那些纹素不应该是黑色的。
它是如何工作的:着色器在每一帧查找 positionTexture
中每一行的新点并相应地更新 linesTextures
,使用 for
循环,如下所示:
#define LINES_COUNT = 200
#define LINE_POINTS = 85 // with 100 it works!!!
// Then in main()
vec2 uv = gl_FragCoord.xy / resolution.xy;
for (float i = 0.0; i < LINES_COUNT; i += 1.0) {
float startIdx = i * LINE_POINTS; // line start index
float endIdx = beginIdx + LINE_POINTS - 1.0; // line end index
vec2 lastCell = getUVfromIndex(endIdx); // last uv coordinate reserved for current line
if (match(lastCell, uv)) {
pos = texture2D( positionTexture, vec2((i / LINES_COUNT) + minFloat, 0.0)).xyz;
} else if (index >= startIdx && index < endIdx) {
pos = texture2D( lineTexture, getNextUV(uv) ).xyz;
}
}
这行得通,但是当我有很多行(150+)时它有点错误:可能是精度问题。我不确定我编写的查找纹理的函数是否正确。我写了像 getNextUV(uv)
这样的函数来从下一个索引(转换为 uv 坐标)中获取值并复制到上一个。或 match(xy, uv)
了解当前片段是否是我想要的纹素。
虽然我可以简单地使用经典公式:
index = uv.y * width + uv.x
但它比这更复杂。例如 match()
:
// Wether a point XY is within a UV coordinate
float size = 132.0; // width and height of texture
float unit = 1.0 / size;
float minFloat = unit / size;
bool match(vec2 point, vec2 uv) {
vec2 p = point;
float x = floor(p.x / unit) * unit;
float y = floor(p.y / unit) * unit;
return x <= uv.x && x + unit > uv.x && y <= uv.y && y + unit > uv.y;
}
或getUVfromIndex()
:
vec2 getUVfromIndex(float index) {
float row = floor(index / size); // Example: 83.56 / 10 = 8
float col = index - (row * size); // Example: 83.56 - (8 * 10) = 3.56
col = col / size + minFloat; // u = 0.357
row = row / size + minFloat; // v = 0.81
return vec2(col, row);
}
有人可以通过从 index
值获取 uv
坐标来解释在纹理中查找值的最有效方法是什么吗?
纹理坐标来自像素的边缘而不是中心,因此计算 UV 坐标的公式需要为
u = (xPixelCoord + .5) / widthOfTextureInPixels;
v = (yPixelCoord + .5) / heightOfTextureInPixels;
所以我猜你希望 getUVfromIndex
成为
uniform vec2 sizeOfTexture; // allow texture to be any size
vec2 getUVfromIndex(float index) {
float widthOfTexture = sizeOfTexture.x;
float col = mod(index, widthOfTexture);
float row = floor(index / widthOfTexture);
return (vec2(col, row) + .5) / sizeOfTexture;
}
或者,based on some other experience with math issues in shaders您可能需要伪造索引
uniform vec2 sizeOfTexture; // allow texture to be any size
vec2 getUVfromIndex(float index) {
float fudgedIndex = index + 0.1;
float widthOfTexture = sizeOfTexture.x;
float col = mod(fudgedIndex, widthOfTexture);
float row = floor(fudgedIndex / widthOfTexture);
return (vec2(col, row) + .5) / sizeOfTexture;
}
如果您使用的是 WebGL2,则可以使用 texelFetch
,它采用整数像素坐标从纹理中获取值
我的应用程序使用 Javascript + Three.js / WebGL + GLSL 编码。我有 200 条曲线,每条曲线由 85 个点组成。为了使曲线动画化,我添加了一个新点并删除了最后一个点。
所以我制作了一个 positions
着色器将新位置存储到纹理 (1) 和 lines
着色器将所有曲线的位置写入另一个纹理 (2)。
目标是将纹理用作数组:我知道一行的第一个和最后一个索引,所以我需要将这些索引转换为 uv
坐标。
我使用 FBOHelper 调试 FBO。
1) 此 1D 纹理包含每条曲线的新点(总共 200 个):positionTexture
2) 这些是 200 条曲线及其所有点,一个接一个:linesTexture
黑色部分是这里的BUG。那些纹素不应该是黑色的。
它是如何工作的:着色器在每一帧查找 positionTexture
中每一行的新点并相应地更新 linesTextures
,使用 for
循环,如下所示:
#define LINES_COUNT = 200
#define LINE_POINTS = 85 // with 100 it works!!!
// Then in main()
vec2 uv = gl_FragCoord.xy / resolution.xy;
for (float i = 0.0; i < LINES_COUNT; i += 1.0) {
float startIdx = i * LINE_POINTS; // line start index
float endIdx = beginIdx + LINE_POINTS - 1.0; // line end index
vec2 lastCell = getUVfromIndex(endIdx); // last uv coordinate reserved for current line
if (match(lastCell, uv)) {
pos = texture2D( positionTexture, vec2((i / LINES_COUNT) + minFloat, 0.0)).xyz;
} else if (index >= startIdx && index < endIdx) {
pos = texture2D( lineTexture, getNextUV(uv) ).xyz;
}
}
这行得通,但是当我有很多行(150+)时它有点错误:可能是精度问题。我不确定我编写的查找纹理的函数是否正确。我写了像 getNextUV(uv)
这样的函数来从下一个索引(转换为 uv 坐标)中获取值并复制到上一个。或 match(xy, uv)
了解当前片段是否是我想要的纹素。
虽然我可以简单地使用经典公式:
index = uv.y * width + uv.x
但它比这更复杂。例如 match()
:
// Wether a point XY is within a UV coordinate
float size = 132.0; // width and height of texture
float unit = 1.0 / size;
float minFloat = unit / size;
bool match(vec2 point, vec2 uv) {
vec2 p = point;
float x = floor(p.x / unit) * unit;
float y = floor(p.y / unit) * unit;
return x <= uv.x && x + unit > uv.x && y <= uv.y && y + unit > uv.y;
}
或getUVfromIndex()
:
vec2 getUVfromIndex(float index) {
float row = floor(index / size); // Example: 83.56 / 10 = 8
float col = index - (row * size); // Example: 83.56 - (8 * 10) = 3.56
col = col / size + minFloat; // u = 0.357
row = row / size + minFloat; // v = 0.81
return vec2(col, row);
}
有人可以通过从 index
值获取 uv
坐标来解释在纹理中查找值的最有效方法是什么吗?
纹理坐标来自像素的边缘而不是中心,因此计算 UV 坐标的公式需要为
u = (xPixelCoord + .5) / widthOfTextureInPixels;
v = (yPixelCoord + .5) / heightOfTextureInPixels;
所以我猜你希望 getUVfromIndex
成为
uniform vec2 sizeOfTexture; // allow texture to be any size
vec2 getUVfromIndex(float index) {
float widthOfTexture = sizeOfTexture.x;
float col = mod(index, widthOfTexture);
float row = floor(index / widthOfTexture);
return (vec2(col, row) + .5) / sizeOfTexture;
}
或者,based on some other experience with math issues in shaders您可能需要伪造索引
uniform vec2 sizeOfTexture; // allow texture to be any size
vec2 getUVfromIndex(float index) {
float fudgedIndex = index + 0.1;
float widthOfTexture = sizeOfTexture.x;
float col = mod(fudgedIndex, widthOfTexture);
float row = floor(fudgedIndex / widthOfTexture);
return (vec2(col, row) + .5) / sizeOfTexture;
}
如果您使用的是 WebGL2,则可以使用 texelFetch
,它采用整数像素坐标从纹理中获取值