1

我编写了一个生成噪声的着色器,它使用 gl_TexCoord[0].xy(由 SFML 生成的东西)作为输入,并为每个像素输出一个浮点数。

我想要 CPU 上的数据,作为一个 32 位浮点数组。

这是我正在做的事情:

//using opengl 3.0 (I also tried 3.2)
ContextSettings settings(0, 0, 0, 3, 0);
sf::RenderWindow window(sf::VideoMode(windowsize.x, windowsize.y),
    "yada", 7, settings
);

在这里,我在创建纹理后覆盖纹理。我正在使用只有 opengl1.1 标头的 Windows,所以我必须手动设置 GL_R32F(很高兴由 dav1d 完成):

#ifndef GL_R32F 
#define GL_R32F 0x822E // found this in glad.h for opengl 3.0, easy guess is that this value doesn't change across opengl versions
#endif
auto handle = sprite_perlin.getTexture()->getNativeHandle(); 
glBindTexture(GL_TEXTURE_2D, handle);
auto size = sprite_perlin.getTexture()->getSize();
glTexImage2D(GL_TEXTURE_2D, 0, GL_R32F, size.x, size.y, 0, GL_RED, GL_FLOAT, NULL);
        }

在这里,我将纹理数据读入值:

float values[4096];
// whole_size is the size of the texture, here it is 60
if(whole_size*whole_size < 4096)
    glGetTexImage(GL_TEXTURE_2D, 0, GL_RED, GL_FLOAT, values);

Image brought_back;
brought_back.create(whole_size, whole_size, Color::Red);

float mi = 1e20, ma = -1e20;

for (int i = 0; i < whole_size; ++i) {
    for (int j = 0; j < whole_size; ++j) {
        brought_back.setPixel(i, j, { 0,Uint8(255.f*values[whole_size*i + j]),0 });
        ma = max(values[whole_size*i + j], ma);
        mi = min(values[whole_size*i + j], mi);
    }
}

mi 和 ma 均为 0.0f;

这是在带有着色器的精灵上显示时产生的噪声(具有一些带有浮点数的颜色函数)着色器按预期工作,我只想在 CPU 上检索数据作为浮点数。

噪声示例

这是着色器的简化版本:

uniform int texture_size;

float get_value_at(ivec2 pixel_coord) {
    //dummy value
    return 0.34f;
}

void main(){
    ivec2 pix_pos = ivec2(gl_TexCoord[0].xy*texture_size);

    float val = get_value_at(pix_pos);

    gl_FragColor = vec4(val,0,0,1);
}
4

1 回答 1

1

我怎样才能拉回浮动数据,[...]

片段着色器的输出被写入帧缓冲区。精度取决于帧缓冲区的格式。对于每个颜色通道,默认帧缓冲区的格式(可能)是 1 个字节。因此,从缓冲区读取颜色时使用哪种类型并不重要。将颜色写入缓冲区时会丢失精度。

如果您想以更高的精度存储颜色,那么您必须渲染到Framebuffer,它使用RenderbufferTexture,其格式可以以更高的精度存储值。
请参阅图像格式

如果您想渲染到帧缓冲区,那么我建议您分别阅读类似FramebuffersRender To Texture的教程Khronos wiki - Framebuffer Object Extension Examples

无论如何,我会提供一个例子。您必须设置一个提供内部格式GL_R32F(或GL_RGBA32F4 个颜色通道)的帧缓冲区。

使用内部格式设置渲染缓冲区GL_R32F并将其附加到帧缓冲区:

// render buffer with internal format `GL_RGBA32F`
GLuint rbo32F = 0;
glGenRenderbuffers(1, &rbo32F);
glBindRenderbuffer(GL_RENDERBUFFER, rbo32F);
glRenderbufferStorage(GL_RENDERBUFFER, GL_R32F, size.x, size.y);

// framebuffer with attached render buffer
GLuint fbo = 0;
glGenFramebuffers(1, &fbo);
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, rbo32F);
glBindFramebuffer(GL_FRAMEBUFFER, 0);

或者使用格式设置纹理GL_R32F并将其附加到帧缓冲区。如果您只是想创建一个纹理,用于进一步渲染,那么这就是您过去的方法。无需将图像上传到 CPU,因为图像已经渲染为纹理,可以进一步使用。

// texture with internal format `GL_R32F`
GLuint tex32F = 0;
glGenTextures(1, &tex32F);
glBindTexture(GL_TEXTURE_2D, tex32F);
glTexImage2D(GL_TEXTURE_2D, 0, GL_R32F, size.x, size.y, 0, GL_RED, GL_FLOAT, nullptr);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

// framebuffer with attached texture
GLuint fbo = 0;
glGenFramebuffers(1, &fbo);
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, tex32F, 0);
glBindFramebuffer(GL_FRAMEBUFFER, 0);

在这两种情况下,您都应该验证帧缓冲区的完整性glCheckFramebufferStatus

glBindFramebuffer(GL_FRAMEBUFFER, fbo);
int status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
if (status != GL_FRAMEBUFFER_COMPLETE) {
    // error handling
    // [...]
}
glBindFramebuffer(GL_FRAMEBUFFER, 0);

渲染前绑定帧缓冲区:

glBindFramebuffer(GL_FRAMEBUFFER, fbo);

绘制几何图形后,纹理可以通过glGetTexImage. 例如:

std::vector<GLfloat> buffer(size.x * size.y * 1); // widht * height * 1 color channel
glBindTexture(GL_TEXTURE_2D, tex32F);
glGetTexImage(GL_TEXTURE_2D, 0, GL_RED, GL_FLOAT, buffer.data());
于 2019-09-21T14:30:56.923 回答