OpenGL – 一次性传递所有数据到着色器时遇到问题

huangapple go评论96阅读模式
英文:

OpenGL - trouble passing ALL data into shader at once

问题

我正在尝试使用OpenGL 3.3在四边形(由2个三角形组成)上显示纹理。

在一个四边形上绘制纹理效果很好;但是当我有一个纹理集(sprite atlas),并且使用两个四边形(对象)来显示纹理集的不同部分时,它们在绘制循环中会不断切换(一个消失然后再次出现,等等),并且位于各自的平移位置。

我绘制的方式不是标准的每个四边形(或对象)都使用DrawElements,而是将所有四边形、UV、平移等打包成一个大块(作为“in”变量)发送到着色器中:顶点着色器:

 #version 330 core

// 输入顶点数据,对于此着色器的所有执行都不同。
in vec3 vertexPosition_modelspace;
in vec3 vertexColor;
in vec2 vertexUV;
in vec3 translation;
in vec4 rotation;
in vec3 scale;
// 输出数据;将为每个片段插值。
out vec2 UV;
// 输出数据;将为每个片段插值。
out vec3 fragmentColor;
// 对于整个网格保持不变的值。
uniform mat4 MVP;

...

void main(){


    mat4 Model = mat4(1.0);

    mat4 t = translationMatrix(translation);
    mat4 s = scaleMatrix(scale);
    mat4 r = rotationMatrix(vec3(rotation), rotation[3]);

    Model *= t * r * s;


    gl_Position = MVP * Model * vec4 (vertexPosition_modelspace,1); //* MVP;

    // 每个顶点的颜色将被插值
    // 以产生每个片段的颜色
    fragmentColor = vertexColor;

    // 顶点的UV。没有特殊的空间。
    UV = vertexUV;

}

顶点着色器是否按照我认为的方式工作,即对于大块数据,它会将每个段作为单独的uniform进行绘制,因为它似乎不是这样?我的思路是否正确?

为了完整起见,这是我的片段着色器:

#version 330 core

// 从顶点着色器插值的值
in vec3 fragmentColor;

// 从顶点着色器插值的值
in vec2 UV;

// 输出数据
out vec4 color;

// 对于整个网格保持不变的值。
uniform sampler2D myTextureSampler;

void main()
{

    // 输出颜色=指定UV处纹理的颜色
    color = texture2D( myTextureSampler, UV ).rgba;

}

有人要求提供更多信息,所以我将说明如何将这些数据绑定到顶点着色器。以下代码只是我用于平移的代码。我还有更多用于颜色、旋转、缩放、UV等的代码:

gl.BindBuffer(gl.ARRAY_BUFFER, tvbo)
gl.BufferData(gl.ARRAY_BUFFER, len(data.Translations)*4, gl.Ptr(data.Translations), gl.DYNAMIC_DRAW)
tAttrib := uint32(gl.GetAttribLocation(program, gl.Str("translation\x00")))
gl.EnableVertexAttribArray(tAttrib)
gl.VertexAttribPointer(tAttrib, 3, gl.FLOAT, false, 0, nil)

...

gl.DrawElements(gl.TRIANGLES, int32(len(elements)), gl.UNSIGNED_INT, nil)
英文:

I'm trying to display textures on quads (2 triangles) using opengl 3.3

Drawing a texture on a quad works great; however when I have ONE textures (sprite atlas) but using 2 quads(objects) to display different parts of the atlas. When in draw loop, they end up switching back and fourth(one disappears than appears again, etc) at their individual translated locations.

The way I'm drawing this is not the standard DrawElements for each quad(or object) but I package all quads, uv, translations, etc send them up to the shader as one big chunk (as "in" variables): Vertex shader:

 #version 330 core

// Input vertex data, different for all executions of this shader.
in vec3 vertexPosition_modelspace;
in vec3 vertexColor;
in vec2 vertexUV;
in vec3 translation;
in vec4 rotation;
in vec3 scale;
// Output data ; will be interpolated for each fragment.
out vec2 UV;
// Output data ; will be interpolated for each fragment.
out vec3 fragmentColor;
// Values that stay constant for the whole mesh.
uniform mat4 MVP;

...

void main(){


    mat4 Model = mat4(1.0);

    mat4 t = translationMatrix(translation);
    mat4 s = scaleMatrix(scale);
    mat4 r = rotationMatrix(vec3(rotation), rotation[3]);

    Model *= t * r * s;


    gl_Position = MVP * Model * vec4 (vertexPosition_modelspace,1); //* MVP;

    // The color of each vertex will be interpolated
    // to produce the color of each fragment
    fragmentColor = vertexColor;

    // UV of the vertex. No special space for this one.
    UV = vertexUV;

}

Is the vertex shader working as I think it would with a large chunk of data - that it draws each segment passed up as uniform individually because it does not seem like it? Is my train of thought correct on this?

For completeness this is my fragment shader:

#version 330 core

// Interpolated values from the vertex shaders
in vec3 fragmentColor;

// Interpolated values from the vertex shaders
in vec2 UV;

// Ouput data
out vec4 color;

// Values that stay constant for the whole mesh.
uniform sampler2D myTextureSampler;

void main()
{

    // Output color = color of the texture at the specified UV
    color = texture2D( myTextureSampler, UV ).rgba;

}

A request for more information was made so I will put how i bind this data up to the vertex shader. The following code is just one I use for my translations. I have more for color, rotation, scale, uv, etc:

gl.BindBuffer(gl.ARRAY_BUFFER, tvbo)
gl.BufferData(gl.ARRAY_BUFFER, len(data.Translations)*4, gl.Ptr(data.Translations), gl.DYNAMIC_DRAW)
tAttrib := uint32(gl.GetAttribLocation(program, gl.Str("translation\x00")))
gl.EnableVertexAttribArray(tAttrib)
gl.VertexAttribPointer(tAttrib, 3, gl.FLOAT, false, 0, nil)

...

gl.DrawElements(gl.TRIANGLES, int32(len(elements)), gl.UNSIGNED_INT, nil)

答案1

得分: 0

你只有一个sampler2D,这意味着你只能使用一个纹理,无论你绑定了多少个纹理。如果你确实需要将数据作为单个块传递,那么你应该为每个纹理添加一个采样器。我不确定你有多少个对象/纹理,但是使用这种数据传递方式,你受到图形硬件纹理单元限制的限制。此外,你需要在数据中添加另一个值,告诉哪个图元使用哪个纹理单元,并在片段着色器中选择正确的纹理采样器。

你应该添加类似以下的内容:

// 顶点着色器
in int usedtexture;
out int txr;

void main()
{
txr = usedtexture;
}

// 片段着色器
uniform sampler2D myTextureSampler0;
uniform sampler2D myTextureSampler1;
uniform sampler2D myTextureSampler2;
uniform sampler2D myTextureSampler3;
in vec2 UV;
in int txr;
out vec4 color;
void main()
{
if (txr == 0)
color = texture2D(myTextureSampler0, UV).rgba;
else if (txr == 1)
color = texture2D(myTextureSampler1, UV).rgba;
else if (txr == 2)
color = texture2D(myTextureSampler2, UV).rgba;
else if (txr == 3)
color = texture2D(myTextureSampler3, UV).rgba;
else
color = vec4(0.0, 0.0, 0.0, 0.0);
}

这种传递方式存在以下问题:

  • 使用的纹理数量受到硬件纹理单元限制的限制。
  • 如果你的渲染需要额外的纹理,比如法线贴图/高光贴图/光照贴图,那么你需要为每种对象类型添加多个纹理,这样你的限制就会被2、3、4等整除。
  • 你需要在片段着色器中使用if/switch语句,这可能会显著降低性能。
  • 是的,你可以使用无分支的方式实现,但这样你需要一直访问所有纹理,增加了图形处理器的负荷,没有必要。

这种传递方式适用于以下情况:

  • 所有纹理都在单个图像中(如你提到的纹理图集)。
  • 这种方式可能更快,并且对于对象类型(或材质)数量较小但对象数量较大的场景是合理的。
英文:

You have just single sampler2D

  • which means you have just single texture at your disposal
  • regardless on how many of them you bind.
  • If you really need to pass the data as single block
  • then you should add sampler per each texture you got
  • not sure how many objects/textures you have
  • but you are limited by gfx hw limit on texture units with this way of data passing
  • also you need to add another value to your data telling which primitive use which texture unit
  • and inside fragment then select the right texture sampler ...

You should add stuff like this:

// vertex
in int usedtexture;
out int txr;

void main()
 {
 txr=usedtexture;
 }

// fragment
uniform sampler2D myTextureSampler0;
uniform sampler2D myTextureSampler1;
uniform sampler2D myTextureSampler2;
uniform sampler2D myTextureSampler3;
in vec2 UV;
in int txr;
out vec4 color;
void main
 {
      if (txr==0) color = texture2D( myTextureSampler0, UV ).rgba;
 else if (txr==1) color = texture2D( myTextureSampler1, UV ).rgba;
 else if (txr==2) color = texture2D( myTextureSampler2, UV ).rgba;
 else if (txr==3) color = texture2D( myTextureSampler3, UV ).rgba;
 else color=vec4(0.0,0.0,0.0,0.0);
 }

This way of passing is not good for these reasons:

  • number of used textures is limited to HW texture units limit
  • if your rendering would need additional textures like normal/shininess/light maps
  • then you need more then 1 texture per object type and your limit is suddenly divided by 2,3,4...
  • You need if/switch statements inside fragment which can slow things down considerably
  • Yes you can do it brunch less but then you would need to access all textures all the time increasing heat stress on gfx without reason...

This kind of passing is suitable for

  • all textures inside single image (as you mentioned texture atlas)
  • which can be faster this way and reasonable for scenes with small number of object types (or materials) but large object count...

答案2

得分: 0

由于我需要更多关于这个问题的输入,我在reddit上发布了这个页面,有人通过一条回复帮助了我!无论如何,这是reddit的链接:

https://www.reddit.com/r/opengl/comments/3gyvlt/opengl_passing_all_scene_data_into_shader_each/

在将所有顶点作为一个数据结构传递到顶点着色器后,看到两个单独的纹理/四边形的问题是因为我的元素索引有问题。我需要确定每组顶点的正确索引,以用于我的两个三角形(四边形)对象。只需像这样进行操作:

vertexInfo.Elements = append(vertexInfo.Elements, uint32(idx4), uint32(idx4+1), uint32(idx4+2), uint32(idx4), uint32(idx4+2), uint32(idx4+3))

英文:

Since I needed more input on this matter, I linked this page to reddit and someone was able to help me with one response! Anyways the reddit link is here:

https://www.reddit.com/r/opengl/comments/3gyvlt/opengl_passing_all_scene_data_into_shader_each/

The issue of seeing two individual textures/quads after passing all vertices as one data structure over to vertex shader was because my element indices were off. I needed to determine the correct index of each set of vertices for my 2 triangle(quad) objects. Simply had to do something like this:

		vertexInfo.Elements = append(vertexInfo.Elements, uint32(idx*4), uint32(idx*4+1), uint32(idx*4+2), uint32(idx*4), uint32(idx*4+2), uint32(idx*4+3))

huangapple
  • 本文由 发表于 2015年8月14日 08:34:57
  • 转载请务必保留本文链接:https://go.coder-hub.com/32000604.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定