在片段着色器问题中读取 image1d
Posted
技术标签:
【中文标题】在片段着色器问题中读取 image1d【英文标题】:Reading image1d in fragment shader problem 【发布时间】:2018-10-31 04:01:33 【问题描述】:我正在尝试从 sampler1D 的片段着色器中读取 voronoi 图的每个单元格的相应颜色,结果如下:
然后我创建一个观察和透视矩阵并使用键盘在 z 和 x 轴上移动相机,如果我在 x 轴上移动每个单元格仍然有其相应的颜色,但是当我在 z 轴上移动时似乎它丢失了我创建的颜色缓冲区的索引:
沿 z 轴移动之前的 Voronoi 图
沿z轴移动后的Voronoi图
为了检查我在使用相机在 z 轴上移动后是否丢失了片段着色器中的索引,我尝试使用每个点的索引(在客户端随机生成)作为每个单元格的颜色,如下所示(我生成 5 分):
带相机的 Voronoi 图,在使用索引作为颜色在 z 轴上移动之前
和
带有相机的 Voronoi 图,在使用索引作为颜色在 z 轴上移动之后
而且它不会丢失每个单元格的索引,但我希望能够从我创建的纹理中读取颜色。
这是我的客户端代码:
class VoronoiJFA
: public Core
public:
VoronoiJFA()
: Core(512, 512, "VoronoiJFA"), size_space(512)
virtual void Start() override
srand(time(nullptr));
shader_points = new Shader("draw_points.vert", "draw_points.frag");
shader_voronoi_jfa = new Shader("voronoi_jfa.vert", "voronoi_jfa.frag");
shader_display_voronoi = new Shader("display_voronoi.vert", "display_voronoi.frag");
size_points = 5;
size_triangles = 16000;
eye = vec3(0.0f, 0.0f, 3.0f);
target = vec3(0.0f, 0.0f, -1.0f);
up = vec3(0.0f, 1.0f, 0.0f);
vec4* p = new vec4[size_points];
vec4* c = new vec4[size_points];
// Generating random point for voronoi diagram
for (size_t i 0 ; i < size_points; i++)
float x (float(rand()) / RAND_MAX * 2.0f - 1.0f) ;
float y (float(rand()) / RAND_MAX * 2.0f - 1.0f) ;
//the "i" variable is used to store the index of each point to reference the color in the display_voronoi.frag shader
p[i] = vec4(x, y, 0.0f, i);
float r float(rand()) / RAND_MAX ;
float g float(rand()) / RAND_MAX ;
float b float(rand()) / RAND_MAX ;
c[i] = vec4(r, g, b, 1.0f);
// The space_texture is where i'm gonna paint voronoi using offscreen rendering for voronoi JFA
glCreateTextures(GL_TEXTURE_2D, 1, &space_texture);
glTextureStorage2D(space_texture, 1, GL_RGBA32F, size_space, size_space);
glBindImageTexture(0, space_texture, 0, GL_FALSE, 0, GL_READ_WRITE, GL_RGBA32F);
// Here i store the colors for each voronoi cell
glCreateTextures(GL_TEXTURE_1D, 1, &colors_texture);
glTextureStorage1D(colors_texture, 1, GL_RGBA32F, size_points);
glTextureSubImage1D(colors_texture, 0, 0, size_points, GL_RGBA, GL_FLOAT, c);
glBindTextureUnit(2, colors_texture);
// This is the fbo for the offscreen rendering to draw generate voronoi
glCreateFramebuffers(1, &framebuffer_object);
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer_object);
glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, space_texture, 0);
glDrawBuffer(GL_COLOR_ATTACHMENT0);
glCreateBuffers(1, &vertex_buffer_points);
glNamedBufferStorage(vertex_buffer_points, sizeof(vec4) * size_points, p, GL_DYNAMIC_STORAGE_BIT);
glCreateVertexArrays(1, &vertex_array_points);
glBindVertexArray(vertex_array_points);
glVertexArrayAttribFormat(vertex_array_points, 0, 4, GL_FLOAT, GL_FALSE, 0);
glEnableVertexArrayAttrib(vertex_array_points, 0);
glVertexArrayAttribBinding(vertex_array_points, 0, 0);
glVertexArrayVertexBuffer(vertex_array_points, 0, vertex_buffer_points, 0, sizeof(vec4));
Window::current->setColor(vec4(-1000000000.0f));
delete[] p;
delete[] c;
virtual void Update() override
static float time 0.0f ;
time += Time::deltaTime;
if (Input::getKeyDown(KeyCode::Escape))
Window::current->shouldClose(true);
// Camera controls
if (Input::getKey(KeyCode::A))
eye = eye - normalize(cross(target, up)) * Time::deltaTime * 1.0f;
if (Input::getKey(KeyCode::D))
eye = eye + normalize(cross(target, up)) * Time::deltaTime * 1.0f;
if (Input::getKey(KeyCode::S))
eye = eye - target * Time::deltaTime * 1.0f;
if (Input::getKey(KeyCode::W))
eye = eye + target * Time::deltaTime * 1.0f;
mat4 M;
mat4 V;
V = mat4::lookAt(eye, eye + target, up); // Virtual camera
mat4 P;
P = mat4::perspective(radians(45.0f), float(Window::current->getWidth()) / Window::current->getHeight(), 0.1f, 100.0f); // projection matrix
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer_object);
vec4 distance(-1000000000.0f);
glClearBufferfv(GL_COLOR, 0, &distance[0]);
glViewport(0, 0, size_space, size_space);
glMemoryBarrier(GL_FRAMEBUFFER_BARRIER_BIT | GL_TEXTURE_FETCH_BARRIER_BIT | GL_SHADER_IMAGE_ACCESS_BARRIER_BIT | GL_TEXTURE_UPDATE_BARRIER_BIT | GL_ATOMIC_COUNTER_BARRIER_BIT);
shader_points->use(); // Here i just paint the points generated previously in a texture 2D
glUniform1f(0, time);
vec2 mouse = (vec2(Input::mousePosition.x, Input::mousePosition.y) / vec2(Window::current->getWidth(), Window::current->getHeight())) * 2.0f - 1.0f;
glUniform2fv(1, 1, &mouse[0]);
glUniformMatrix4fv(2, 1, GL_FALSE, &M[0][0]);
glUniformMatrix4fv(3, 1, GL_FALSE, &V[0][0]);
glUniformMatrix4fv(4, 1, GL_FALSE, &P[0][0]);
glBindVertexArray(vertex_array_points);
glDrawArrays(GL_POINTS, 0, size_points);
glMemoryBarrier(GL_FRAMEBUFFER_BARRIER_BIT | GL_TEXTURE_FETCH_BARRIER_BIT | GL_SHADER_IMAGE_ACCESS_BARRIER_BIT | GL_TEXTURE_UPDATE_BARRIER_BIT | GL_ATOMIC_COUNTER_BARRIER_BIT);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glViewport(0, 0, size_space, size_space);
int step size_space / 2 ;
while(step >= 1)
shader_voronoi_jfa->use(); // Here i compute the voronoi diagram using JFA
glUniform1i(2, step);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
glMemoryBarrier(GL_FRAMEBUFFER_BARRIER_BIT | GL_TEXTURE_FETCH_BARRIER_BIT | GL_SHADER_IMAGE_ACCESS_BARRIER_BIT | GL_TEXTURE_UPDATE_BARRIER_BIT | GL_ATOMIC_COUNTER_BARRIER_BIT);
step /= 2;
step = size_space / 2 ;
while(step >= 1)
shader_voronoi_jfa->use(); // Again using JFA to eliminate "islands"
glUniform1i(2, step);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
glMemoryBarrier(GL_FRAMEBUFFER_BARRIER_BIT | GL_TEXTURE_FETCH_BARRIER_BIT | GL_SHADER_IMAGE_ACCESS_BARRIER_BIT | GL_TEXTURE_UPDATE_BARRIER_BIT | GL_ATOMIC_COUNTER_BARRIER_BIT);
step /= 2;
glViewport(0, 0, Window::current->getWidth(), Window::current->getHeight());
shader_display_voronoi->use(); // Display voronoi diagram
glUniform1i(0, size_space);
glUniform1i(1, size_points);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
virtual void End() override
delete shader_points;
delete shader_voronoi_jfa;
glDeleteBuffers(1, &vertex_buffer_points);
glDeleteVertexArrays(1, &vertex_array_points);
glDeleteTextures(1, &space_texture);
glDeleteTextures(1, &colors_texture);
glDeleteFramebuffers(1, &framebuffer_object);
private:
Shader* shader_points;
Shader* shader_voronoi_jfa;
Shader* shader_display_voronoi;
GLuint vertex_buffer_points;
GLuint vertex_array_points;
GLuint space_texture;
GLuint colors_texture;
GLuint framebuffer_object;
int size_space;
int size_points;
int size_triangles;
vec3 eye;
vec3 target;
vec3 up;
;
#if 1
CORE_MAIN(VoronoiJFA)
#endif
我的着色器是这些:
draw_points.vert
#version 450 core
layout(location = 0) in vec4 a_points;
layout(binding = 1, rgba32f) uniform image1D image_points;
layout(location = 0) uniform float u_time;
layout(location = 1) uniform vec2 u_mouse;
layout(location = 2) uniform mat4 M;
layout(location = 3) uniform mat4 V;
layout(location = 4) uniform mat4 P;
out vec4 color;
void main()
vec4 vertex = a_points;
gl_Position = P * V * M * vec4(vertex.xy, 0.0, 1.0);
color.x = vertex.a; // here i put the index of each point to reference the color
draw_points.frag
#version 450 core
in vec4 color;
out vec4 FragColor;
void main()
// Here i paint the points with its corresponding fragcoord and in the alpha i'm storing the index of each point
FragColor = vec4(gl_FragCoord.xy, 0.0, color.x);
display_voronoi.vert
#version 450 core
out vec2 uv;
void main()
vec2 v[4] = vec2[4]
(
vec2(-1.0, -1.0),
vec2( 1.0, -1.0),
vec2(-1.0, 1.0),
vec2( 1.0, 1.0)
);
vec4 p = vec4(v[gl_VertexID], 0.9998, 1.0);
gl_Position = p;
display_voronoi.frag
#version 450 core
layout(binding = 0, rgba32f) uniform image2D space; // here it's voronoi
layout(binding = 2) uniform sampler1D colors; // color buffer
layout(location = 0) uniform int u_spaceSize;
layout(location = 1) uniform int points_size;
out vec4 FragColor;
void main()
float index = imageLoad(space, ivec2(gl_FragCoord.xy)).a; // here i get the index of each cell
vec4 color = texelFetch(colors, int(index), 0); // using the index to get color
FragColor = color; //vec4(index / (points_size - 1)); The commented part is my "debugger" if i use the index divided by the number of points, it paints in gray scale
【问题讨论】:
只是一种预感 - 如果您将int(index)
更改为 int(index + 0.5)
会怎样?
【参考方案1】:
您的问题发生是因为您在屏幕空间上对颜色进行采样,使用片段坐标,无论您执行什么缩放都将保持不变(它们将始终是您的窗口坐标)。因此,当您执行缩放时,您不会得到实际的缩放,而只是减少这些片段坐标将采样的索引范围。您应该在第一阶段对颜色进行采样,并将它们存储在 g 缓冲区中,就像处理索引一样。
【讨论】:
但这并不能解释黑白 voronoi(最后两张图像),正如您所看到的,我缩小了并且我没有丢失索引。我用 5 种颜色生成 5 个点,因此每个点在其 alpha 通道(0、1、2、3 和 4)中都有一个索引,然后我在纹理 2D 中绘制这些点,但在绘制点的顶点着色器中,我首先通过索引在一个输出变量中,然后我将我的点乘以这样 gl_Position = PVM*vec4(point.xyz, 1.0) 所以基本上我在移动我的点,因此 voronoi 细胞移动 @JacobCerón 我认为您生成的调试图像的黑白图像是为了确保它们是正确的索引,据我从您的帖子中了解到。你甚至说它是这样工作的。 我已经解决了我的问题,问题是 NVIDIA 驱动程序。我在 Linux Ubuntu 16 中,我有来自 nvidia-324(专有,经过测试)的 NVIDIA 二进制驱动程序版本 384.130 和来自 xserver-xorg-video-nouveau(开源)的 X.Org X 服务器 -Nouveau 显示驱动程序,并且使用 nouveau 它可以正常工作,但是如果我更改为 nvidia 二进制驱动程序,一切都会变糟。所以我认为 Nvidia 二进制驱动程序在 opengl 上有一些错误,或者实现真的不同,这只是我的看法。你怎么看? 但是我仍然需要帮助,nvidia驱动有什么问题? @Nadir 查看第一张和第二张图片中的颜色,我猜这些图片看起来是一样的。以上是关于在片段着色器问题中读取 image1d的主要内容,如果未能解决你的问题,请参考以下文章
GLSL:无法从 FBO 读取纹理并使用片段着色器渲染到另一个 FBO
使用制服时 Xamarin OpenGL 片段着色器的奇怪行为