【发布时间】:2020-06-08 15:53:14
【问题描述】:
C#/OpenTK。
我一直无法找到一种在场景中组合不同几何类型的好方法。出于效率原因,我一直在尝试将相同的着色器和单个 VAO/VBO 用于混合原始类型(三角形、线条和可能的线条)。不确定这是否可能/可取。
我遇到的主要问题是三角形被实例化,并且它们在顶点着色器中的输入需要是顶点和法线。然而这些线没有被实例化,不需要光照,而是不同的颜色,所以它们需要输入到顶点着色器中需要顶点和颜色。所以在一种情况下我想要着色器中的法线,在另一种情况下我想要颜色。
一种解决方案是在着色器中设置此条件。我在顶点着色器的顶部有这个:
layout(location = 0) in vec3 aPos;
layout(location = 1) in vec3 aNormal;
layout(location = 2) in vec3 vertexColor;
如果我尝试将此设置为有条件的(即位置 2 = 正常或颜色取决于原始类型),我会收到语法错误。并不特别惊讶,因为这些行是main() 之外的定义。我认为那里不允许使用 If 语句。
另一种解决方案是单独的着色器,但我希望尽可能避免重复。
目前我已将多边形和线条分成两个单独的缓冲区,因为这似乎合乎逻辑。我可以使用相同的着色器渲染它们吗(不添加填充的虚拟值,这可以使数据结构一致但效率不高)?理想情况下,我有办法告诉它我在一种情况下传递法线,在另一种情况下传递顶点。
还有一些我不理解的行为。我认为,根据我能找到的关于该主题的所有内容,VertexAttribPointer 中的 index 参数必须对应于着色器中的 location。但这不是它的行为方式。我有顶点数据,我给出的索引为 2(在下面的代码中看到 InitVertexBuffers 的末尾),它对应于着色器中的颜色 - 但它的几何图形被正确渲染。
所以我的问题是:
1) 是否可以将着色器中的layout 定义设为有条件的?
2) 我应该将不同原始类型的数据保存在不同的缓冲区中吗?
3) VertexAttribPointer 的第一个参数 (location) 是否没有告诉 OpenGL 如何处理每组数据?如果不是,这是如何确定的?
private void InitVertexBuffers()
{
int floatSize = sizeof(float);
VBO1 = GL.GenBuffer();
VAO1 = GL.GenVertexArray();
// Bind Vertex Array Object...
GL.BindVertexArray(VAO1);
// Copy our vertices array in a buffer for OpenGL to use...
GL.BindBuffer(BufferTarget.ArrayBuffer, VBO1);
List<int> array_lengths = new List<int>() {
vertices.Length * floatSize };
// Calculate and store offset pointers...
List<int> array_offsets = new List<int>() {
0 };
// Set vertex attributes pointers...
GL.BufferData(BufferTarget.ArrayBuffer, vertices.Length * floatSize, (IntPtr)0, BufferUsageHint.StaticDraw);
GL.BufferSubData(BufferTarget.ArrayBuffer, (IntPtr)0, array_lengths[0], vertices);
// Sensor offset vertices...
GL.VertexAttribPointer(0, 3, VertexAttribPointerType.Float, false, 6 * floatSize, 0);
// Normals...
GL.VertexAttribPointer(1, 3, VertexAttribPointerType.Float, false, 6 * floatSize, 3 * floatSize);
GL.EnableVertexAttribArray(0);
GL.EnableVertexAttribArray(1);
// ---------------------------------------------------------------------------------------------------------------------------------------------------
VBO2 = GL.GenBuffer();
VAO2 = GL.GenVertexArray();
GL.BindVertexArray(VAO2);
GL.BindBuffer(BufferTarget.ArrayBuffer, VBO2);
array_lengths = new List<int>() {
line_vertices.Length * floatSize,
axis_widget_vertices.Length * floatSize,
};
array_offsets = new List<int>() {
0,
array_lengths[0] };
GL.BufferData(BufferTarget.ArrayBuffer, (line_vertices.Length + axis_widget_vertices.Length) * floatSize, (IntPtr)0, BufferUsageHint.StaticDraw);
GL.BufferSubData(BufferTarget.ArrayBuffer, (IntPtr)array_offsets[0], array_lengths[0], line_vertices);
GL.BufferSubData(BufferTarget.ArrayBuffer, (IntPtr)array_offsets[1], array_lengths[1], axis_widget_vertices);
// Individual lines...
GL.VertexAttribPointer(0, 3, VertexAttribPointerType.Float, false, 6 * floatSize, array_offsets[0]);
GL.VertexAttribPointer(1, 3, VertexAttribPointerType.Float, false, 6 * floatSize, array_offsets[0] + 3 * floatSize);
GL.VertexAttribPointer(2, 3, VertexAttribPointerType.Float, false, 6 * floatSize, array_offsets[1]);
GL.VertexAttribPointer(3, 3, VertexAttribPointerType.Float, false, 6 * floatSize, array_offsets[1] + 3 * floatSize);
for (int i = 0; i < 4; i++) // zero to three
{
GL.EnableVertexAttribArray(i);
}
GL.Enable(EnableCap.DepthTest); // enable z buffer
}
private void Draw()
{
GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);
GL.BindVertexArray(VAO1);
SetBool(Handle, "instanced", 1);
GL.DrawArraysInstanced(PrimitiveType.Triangles, 0, vertices.Length / 6, allNodes.Count);
SetBool(Handle, "instanced", 0);
GL.BindVertexArray(VAO2);
SetBool(Handle, "use_lighting", 0);
GL.DrawArrays(PrimitiveType.Lines, 0, line_vertices.Length / 6);
GL.DrawArrays(PrimitiveType.Lines, line_vertices.Length / 6, axis_widget_vertices.Length / 6);
SetBool(Handle, "use_lighting", 1);
glControl1.SwapBuffers();
}
string vertexShaderSource =
"# version 330 core\n" +
"uniform mat4 model;" +
"uniform mat4 view;" +
"uniform mat4 projection;" +
"uniform bool instanced;" +
$"uniform vec3 offsets[{ allNodes.Count }];" +
$"uniform vec3 colors[{ allNodes.Count }];" +
"layout(location = 0) in vec3 aPos;" +
"layout(location = 1) in vec3 aNormal;" +
"layout(location = 2) in vec3 vertexColor;" +
"out vec3 color_out;" +
"out vec3 FragPos;" +
"out vec3 Normal;" +
"void main()" +
"{" +
"vec3 offset = offsets[gl_InstanceID];" +
"gl_Position = projection * view * model * vec4(aPos + offset, 1.0);" +
"FragPos = vec3(model * vec4(aPos + offset, 1.0));" +
"Normal = vec3(model * vec4(aNormal, 1.0));" + // (normal has to be rotated the same as the model)
"if (instanced) {" +
"color_out = colors[gl_InstanceID];" +
"} else {" +
"color_out = vec3(1.0,1.0,1.0);" +
"}" +
"}";
【问题讨论】:
-
"如果可能,我希望避免重复。" 目前尚不清楚在这种情况下这意味着什么。如果一种渲染机制使用提供的颜色,而另一种使用固定的、预编译的,则这些是具有不同实现的不同着色器。对于法线来说更是如此,因为它们涉及光照,这意味着光照参数通常作为制服传递,以及进行光照计算所需的着色器代码。对于这些非常不同的渲染环境,究竟会有多少“重复”?
-
我主要指的是重复代码。那么单独的着色器将是要走的路吗?
-
再看一遍,我认为即使是这两种情况的着色器代码也不会有那么多重叠
标签: c# opengl glsl opentk vertex-shader