我正在关注WebGL2Fundametals教程。使用一些样本(多维数据集)数据,我能够使用索引,法线和几何数据成功调用gl.drawElements();。这是我的片段着色器和顶点着色器:

片段着色器:

#version 300 es    
precision mediump float;

in vec3 v_normal;
in vec3 v_surfaceToLight;
in vec3 v_surfaceToView;

uniform vec4 u_color;
uniform float u_shininess;
uniform vec3 u_lightDirection;

uniform float u_innerLimit; // In dot space
uniform float u_outerLimit; // In dot space

out vec4 outColor;

void main() {
  vec3 normal = normalize(v_normal);

  vec3 surfaceToLightDirection = normalize(v_surfaceToLight);
  vec3 surfaceToViewDirection = normalize(v_surfaceToView);
  vec3 halfVector = normalize(surfaceToLightDirection + surfaceToViewDirection);

  float dotFromDirection = dot(surfaceToLightDirection, -u_lightDirection);
  float inLight = smoothstep(u_outerLimit, u_innerLimit, dotFromDirection);
  // Using normals for color calculation based on the incidence of light
  float light = inLight * dot(normal, surfaceToLightDirection);
  float specular = inLight * pow(dot(normal, halfVector), u_shininess);

  outColor = u_color;

  outColor.rgb *= light;

  // Add in the specular
  outColor.rgb += specular;
}


顶点着色器:

#version 300 es
in vec4 a_position;
in vec3 a_normal;

uniform mat4 u_world;
uniform mat4 u_worldViewProjection;
uniform mat4 u_worldInverseTranspose;

uniform vec3 u_lightWorldPosition;
uniform vec3 u_viewWorldPosition;

// varying to pass the normal to the fragment shader
out vec3 v_normal;

out vec3 v_surfaceToLight;
out vec3 v_surfaceToView;

void main() {

  gl_Position = u_worldViewProjection * a_position;

  // Orient the normals and pass to the fragment shader
  v_normal = mat3(u_worldInverseTranspose) * a_normal;

  vec3 surfaceWorldPosition = (u_world * a_position).xyz;
  v_surfaceToLight = u_lightWorldPosition - surfaceWorldPosition;
  v_surfaceToView = u_viewWorldPosition - surfaceWorldPosition;
}


通过以下调用成功渲染了多维数据集:

gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this._indexBuffer);
GLHelper.setUniformValues(this._uniforms, uniformValues);
gl.drawElements(gl.TRIANGLES, 36, gl.UNSIGNED_SHORT, 0);


这是工作多维数据集数据:

顶点:

 // Front face
-100, -100, 100,
 100, -100, 100,
 100,  100, 100,
-100,  100, 100,

 // Back face
-100, -100, -100,
-100,  100, -100,
 100,  100, -100,
 100, -100, -100,

 // Top face
-100,  100, -100,
-100,  100,  100,
 100,  100,  100,
 100,  100, -100,

 // Bottom face
-100, -100, -100,
 100, -100, -100,
 100, -100,  100,
-100, -100,  100,

 // Right face
 100, -100, -100,
 100,  100, -100,
 100,  100,  100,
 100, -100,  100,

 // Left face
-100, -100, -100,
-100, -100,  100,
-100,  100,  100,
-100,  100, -100,


正常值:

 // Front
 0.0,  0.0,  1.0,
 0.0,  0.0,  1.0,
 0.0,  0.0,  1.0,
 0.0,  0.0,  1.0,

 // Back face
 0.0,  0.0, -1.0,
 0.0,  0.0, -1.0,
 0.0,  0.0, -1.0,
 0.0,  0.0, -1.0,

 // Top face
 0.0,  1.0,  0.0,
 0.0,  1.0,  0.0,
 0.0,  1.0,  0.0,
 0.0,  1.0,  0.0,

 // Bottom face
 0.0, -1.0,  0.0,
 0.0, -1.0,  0.0,
 0.0, -1.0,  0.0,
 0.0, -1.0,  0.0,

 // Right face
 1.0,  0.0,  0.0,
 1.0,  0.0,  0.0,
 1.0,  0.0,  0.0,
 1.0,  0.0,  0.0,

 // Left face
-1.0,  0.0,  0.0,
-1.0,  0.0,  0.0,
-1.0,  0.0,  0.0,
-1.0,  0.0,  0.0,


指数:

 0,  1,  2,  0,  2,  3, // Front face
 4,  5,  6,  4,  6,  7, // Back face
 8,  9, 10,  8, 10, 11, // Top face
12, 13, 14, 12, 14, 15, // Bottom face
16, 17, 18, 16, 18, 19, // Right face
20, 21, 22, 20, 22, 23, // Left face


如果我理解正确,则此模型中的法线应该看起来像“简介法线”部分中第一个动画中的立方体法线。现在,我正在尝试使用Blender创建和导入多维数据集。不幸的是,我无法使用相同的法线来重新创建多维数据集。 Blender每个顶点仅生成一个(粉红色)法线,并且它们显然也具有完全不同的角度:



一些资料表明,平滑面部可以解决此问题。它不适合我。这是Wavefront文件:

o Cube
v   1.000000 -1.000000 -1.000000
v   1.000000 -1.000000  1.000000
v  -1.000000 -1.000000  1.000000
v  -1.000000 -1.000000 -1.000000
v   1.000000  1.000000 -0.999999
v   0.999999  1.000000  1.000001
v  -1.000000  1.000000  1.000000
v  -1.000000  1.000000 -1.000000
vn  0.0000   -1.0000    0.0000
vn  0.0000    1.0000    0.0000
vn  1.0000   -0.0000    0.0000
vn  0.0000   -0.0000    1.0000
vn -1.0000   -0.0000   -0.0000
vn  0.0000    0.0000   -1.0000
s off
f 1//1 3//1 4//1
f 8//2 6//2 5//2
f 5//3 2//3 1//3
f 6//4 3//4 2//4
f 3//5 8//5 4//5
f 1//6 8//6 5//6
f 1//1 2//1 3//1
f 8//2 7//2 6//2
f 5//3 6//3 2//3
f 6//4 7//4 3//4
f 3//5 7//5 8//5
f 1//6 4//6 8//6


当我尝试导入并使用gl.drawElements(gl.TRIANGLES, 72, gl.UNSIGNED_SHORT, 0);渲染此数据时,得到以下输出:

<错误:GL_INVALID_OPERATION:glDrawElements:尝试访问属性1中超出范围的顶点


如果我分别使用上述(.obj)顶点和普通数据六次我再也没有得到任何错误,但是似乎只渲染了一个三角形,而不是一个立方体。 。因此,我对这里的一件事主要感到困惑:


如何修改我的代码以正确解释和呈现数据?

编辑:

将索引更改为基于零的值后,我的确得到了多个三角形,该对象看起来还是错误的。



attribute 1绘制的结果:



评论

Blender问题属于Blender。如果您可以编辑问题的“如何使用Blender”部分,那么我们应该可以在编程部分为您提供帮助。

首先要检查的是,您显示的文件中的索引是从1开始的,而在GL中,索引应该从0开始。也就是说,您需要从文件中的所有索引中减去一个。

@DanHulme完成,我编辑了问题部分,寻求帮助来修改代码,以便正确解释波前数据。我还从文件中的每个索引中减去了1。这确实改变了渲染的网格,但是它仍然不是立方体。任何帮助将不胜感激。

#1 楼

问题在于波前文件和OpenGL如何与索引一起使用。

OpenGL

OpenGL允许您使用不同的缓冲区,从而使顶点具有多个属性。但是,对于一个面中的每个顶点,它只有一个索引。

让我们说我们有一个四边形。它由位置缓冲区和uv缓冲区组成。位置看起来像这样

1, 1, 0;  -1, 1, 0;  -1, -1, 0;  1, -1, 0;


uv看起来像这样

0, 0;     1, 0;      1, 1;       0, 1;


索引看起来像像这样

0, 1, 2, 0, 2, 3


OpenGL渲染时,它会查看数据位置的索引。如果我们拥有第一个面的第一个顶点,索引为0。则它选择索引为0的位置:(1, 1, 0)。然后,它也选择索引为零的uv:(0, 0)

为每个属性使用相同的索引意味着可以将所有数据顺序存储在一个长缓冲区中。如果OpenGL需要用于顶点的数据,则只需加载一个长条数据。对于缓存而言,这比必须转到内存的不同部分以仅查找一个顶点的所有信息更好。

OpenGL对所有顶点属性使用相同的索引。您不能告诉OpenGL在uv数为1的情况下处于位置3。

Wavefront

Wavefront的工作方式不同。进行索引时,它分别为每个属性建立索引。

这是一个面的声明

f 5//3 2//3 1//3


我们看到三个顶点,由一个空白。每个顶点都有两个数字。第一个数字是头寸的索引。第二个数字是法线的索引。它们都不同。

Wavefront不需要关心缓存。它只需要使用最少的内存来存储数据(如果我们将所有内容都加载到内存中。以纯文本格式保存当然不是很有效)。查看您的.obj文件时,我们可以看到我们有8个位置,但只有6个法线。我们总共可以删除6个浮点数(2个法线由3个浮点组成)。

Wavefront为每个顶点属性指定了不同的索引。对于顶点,位置可以是5,但法线是4。结论和结论Wavefront和OpenGL使用不同的数据存储方式。这意味着在解析波前文件时,需要将数据重新索引为OpenGL想要的。

发生的事情是OpenGL认为法线的索引实际上是位置的索引而且您的面孔数量是原来的两倍。

我要做的是将所有波前数据保存到临时列表中。您创建主顶点缓冲区。您遍历每个面孔的每个顶点。您查看该顶点的数据。然后查看是否已经在缓冲区中合并了该数据。如果是这样,则使用该数据的索引,否则将新数据添加到缓冲区中并添加该索引。