I am following the WebGL2Fundametals tutorial. Using some sample (cube) data I was able to successfully call gl.drawElements();
using index, normal and geometry data. This are my fragment and vertex shaders:
Fragment shader:
#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;
}
Vertex shader:
#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;
}
With the following call the cube is successfully rendered:
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this._indexBuffer);
GLHelper.setUniformValues(this._uniforms, uniformValues);
gl.drawElements(gl.TRIANGLES, 36, gl.UNSIGNED_SHORT, 0);
Here is the working cube data:
Vertices:
// 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,
Normals:
// 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,
Indices:
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
If I get it right the normals in this model should look something like the cube's normals in the very first animation in the "Introducing normals" part. Now I was trying to create and import a cube using Blender.
Unfortunately I wasn't able to recreate a cube with the same kind of normals. Blender generates only one (pink) normal per vertex and they also obviously have a totally different angle:
Some sources suggest that smoothing the faces would fix this issue. It did not for me. Here is the Wavefront file:
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
When I try to import and to render this data using gl.drawElements(gl.TRIANGLES, 72, gl.UNSIGNED_SHORT, 0);
I get the following output:
ERROR :GL_INVALID_OPERATION : glDrawElements: attempt to access out of range vertices in attribute 1
If I use above (.obj) vertex and normal data six time each I do not get any more errors but instead of a cube only a single triangle seems to be rendered.
I am pretty sure that attribute 1
is in vec3 a_normal;
in my vertex shader. So I am mainly confused about one thing here:
- How do I modify my code in order to interpret and render the data correctly?
EDIT:
After changing indices to be zero based I do get more than a single triangle, the object still looks wrong.
The result drawn with gl.LINES
:
wavefront
data correctly. I also subtracted 1 from every index in the file. This did change the rendered mesh, it is still not a cube though. Any help would be appreciated. $\endgroup$