【发布时间】:2015-03-09 04:29:08
【问题描述】:
我是 opengl 的新手,我遇到了这种非常意外的行为。
我正在实现一个基本的场景图并尝试添加重用着色器和网格数据的能力。
我有一个 GeometryData 对象,其中包含对顶点数组对象、顶点缓冲区和索引缓冲区的引用。它有一个使用 glDrawElements 绘制模型的渲染方法。
GeometryData 具有对 Shader 对象的引用,该对象具有对程序着色器句柄的引用。
问题是,如果我渲染具有相同着色器实例的连续对象,则只渲染第一个,而其他的由于某种原因不渲染,没有错误。如果我在连续的对象绘制之间交替着色器分配,它们都会正常绘制。
我的GeometryData的Render方法如下:
public override void Render()
{
GL.BindVertexArray(vertexArray);
if (UseShader != null)
UseShader.Use();
var err = GL.GetError();
GL.DrawElements(PrimitiveType.Triangles, numIndices, DrawElementsType.UnsignedInt, IntPtr.Zero);
err = GL.GetError();
GL.BindVertexArray(0);
}
UseShader.Use() 方法是这样的
public void Use()
{
int current = GL.GetInteger(GetPName.CurrentProgram);
if (current != ProgramHandle)
GL.UseProgram(ProgramHandle);
// Use a uniform block for the project and modelview matrices
var index = GL.GetUniformBlockIndex(ProgramHandle, "Matrices");
GL.UniformBlockBinding(ProgramHandle, index, World.BINDING_POINT);
// assume interleaved vertex data
GL.VertexAttribPointer(positionLoc, 3, VertexAttribPointerType.Float, false, 2 * Vector3.SizeInBytes, Vector3.SizeInBytes);
GL.EnableVertexAttribArray(positionLoc);
GL.VertexAttribPointer(colorLoc, 3, VertexAttribPointerType.Float, false, 2 * Vector3.SizeInBytes, 0);
GL.EnableVertexAttribArray(colorLoc);
}
这里有一些截图。这些盒子是使用这个循环创建的:
for (int i = -3; i <= 3; i+=3)
{
Transform child = new Transform();
child.Translation = new Vector3(i, 0, 0);
// this causes the second red box to not render
Geometry geom = new Geometry(i > 0 ? coloredShader : redShader, box);
// this causes all boxes to render normally
// Geometry geom = new Geometry(i == 0 ? coloredShader : redShader, box);
child.addChild(geom);
root.addChild(child);
}
具有交替着色器(红色、彩色、红色)的图片
使用相同着色器(红色、红色、彩色)的两个连续对象的图片。第二个红色对象未绘制。我对场景所做的唯一更改是着色器分配。
更新
经过进一步检查和调试,问题的根源似乎是统一块。我正在使用统一块将投影和模型视图矩阵发送到我的着色器。我怀疑第二次调用 glBufferSubData 来更新模型视图矩阵并没有更新制服,或者着色器在没有先切换到另一个着色器的情况下没有从制服中读取该值。
我尝试将模型视图矩阵从统一块中取出并分别发送到每个着色器。这使得所有对象都正确渲染,但我仍然想为此使用统一块以避免冗余上传。
【问题讨论】:
-
你为什么要为
null测试UseProgram?您是否曾经在没有着色器的情况下进行渲染? -
不,我不会在没有着色器的情况下进行渲染。如果我愿意,这应该覆盖由父节点设置的着色器。但我改变了实现,所以不再需要了。