2
votes

I'm coding a ray tracing program and the main idea is save the triangles and the indices of .obj model into 2 textures:

a) Vertex texture b) Indices texture

Im doing this to do a ray-triangle intersection in my GLSL fragment shader, browsers are telling me that I've this problem with textures :

a) Mozilla Firefox:

Error #1 (with vertexes texture) : Error: WebGL: texImage2D: invalid type 0x1406.

Error #2 (with indices texture) : Error: WebGL: texImage2D: invalid type 0x1403.

b) Google Chrome :

Error with both textures : webgl invalid_enum teximage2d invalid texture type

Here is the code :

Obj.prototype.initTextures = function(){

this.vertexTexture = gl.createTexture();
gl.activeTexture(gl.TEXTURE0)
gl.bindTexture(gl.TEXTURE_2D, this.vertexTexture);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, this.vertexSize, 1, 0, gl.RGB, gl.FLOAT, new Float32Array(this.vertexArray));
gl.bindTexture(gl.TEXTURE_2D, null);

this.trianglesTexture = gl.createTexture();
gl.activeTexture(gl.TEXTURE1);
gl.bindTexture(gl.TEXTURE_2D, this.trianglesTexture);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, this.faceSize, 1, 0, gl.RGB,  gl.UNSIGNED_SHORT, new Uint16Array(this.faceArray));
gl.bindTexture(gl.TEXTURE_2D, null);

}

For obvious reasons this.vertexArray and this.faceArray have the data with them.

1

1 Answers

9
votes

gl.RGB / gl.FLOAT texture are only allowed if you check for and enable floating point textures.

var floatTextures = gl.getExtension('OES_texture_float');
if (!floatTextures) {
    alert('no floating point texture support');
    return;
}

gl.RGB / gl.UNSIGNED_SHORT textures do not exist period on WebGL. You can try encoding your unsigned short values as R * 256 + G * 65536 or something along those lines. Or just use floats here too.

Note: Filtering floats, as in gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR), is a separate extension OES_texture_float_linear but in your case since you're only using gl.NEAREST you don't need to check for that extension

As for putting vertex data in textures you might find you have to do some work to pull out the correct values.

To index a value in a texture you need to compute a texture coordinate that will access the correct texel. To do that you need to access from the middle of the first texel to the middle of the last texel.

In other words if we had 3 values (and therefore 3 texels) we'd have something like this

     ------3x1 ----- texels ----------
     [         ][         ][         ]
 0.0 |<----------------------------->| 1.0

If we just did index / numValues to compute a texture coordinate we'd get

     [         ][         ][         ]
     |          |          |
     0.0       0.333       0.666

Which is right between texels so we add a halfTexel to get this

     [         ][         ][         ]
          |          |          |
        0.167       0.5       0.833

Hope that made sense. Here's a snippet

var gl = document.querySelector("canvas").getContext("webgl");
var ext = gl.getExtension("OES_texture_float");
if (!ext) {
  alert("need OES_texture_float extension cause I'm lazy");
  //return;
}
if (gl.getParameter(gl.MAX_VERTEX_TEXTURE_IMAGE_UNITS) < 2) {
  alert("need to be able to access textures from vertex shaders");
  //return;
}

var m4 = twgl.m4;
var v3 = twgl.v3;
var program = twgl.createProgramFromScripts(gl, ["vshader", "fshader"]);

var positionIndexLoc = gl.getAttribLocation(program, "a_positionIndex");
var normalIndexLoc = gl.getAttribLocation(program, "a_normalIndex");

var colorLoc = gl.getUniformLocation(program, "u_color");
var mvpMatrixLoc = gl.getUniformLocation(program, "u_mvpMatrix");
var mvMatrixLoc = gl.getUniformLocation(program, "u_mvMatrix");
var lightDirLoc = gl.getUniformLocation(
  program, "u_lightDirection");
var u_positionsLoc = gl.getUniformLocation(
  program, "u_positions");
var u_normalsLoc = gl.getUniformLocation(
  program, "u_normals");
gl.uniform1i(u_positionsLoc, 0); // use texture unit 0 for positions
gl.uniform1i(u_normalsLoc, 1);   // use texture unit 1 for normals

// Cube data
var positions = [
  -1, -1, -1, // 0 lbb
  +1, -1, -1, // 1 rbb      2---3
  -1, +1, -1, // 2 ltb     /|  /|
  +1, +1, -1, // 3 rtb    6---7 |
  -1, -1, +1, // 4 lbf    | | | |
  +1, -1, +1, // 5 rbf    | 0-|-1
  -1, +1, +1, // 6 ltf    |/  |/
  +1, +1, +1, // 7 rtf    4---5
];
var positionIndices = [
  3, 7, 5, 3, 5, 1, // right
  6, 2, 0, 6, 0, 4, // left
  6, 7, 3, 6, 3, 2, // top
  0, 1, 5, 0, 5, 4, // bottom
  7, 6, 4, 7, 4, 5, // front
  2, 3, 1, 2, 1, 0, // back
];
var normals = [
  +1,  0,  0,
  -1,  0,  0,
  0, +1,  0,
  0, -1,  0,
  0,  0, +1,
  0,  0, -1,
]
  var normalIndices = [
  0, 0, 0, 0, 0, 0,  // right
  1, 1, 1, 1, 1, 1,  // left
  2, 2, 2, 2, 2, 2,  // top
  3, 3, 3, 3, 3, 3,  // bottom
  4, 4, 4, 4, 4, 4,  // front
  5, 5, 5, 5, 5, 5,  // back
];

function degToRad(deg) {
  return deg * Math.PI / 180;
}

function uploadIndices(loc, data, indices) {
  // scale indices into texture coordinates
  var scaledIndices = new Float32Array(indices.length);

  // to index the value in the texture we need to
  // compute a texture coordinate that will access
  // the correct texel. To do that we need access from
  // the middle of the first texel to the middle of the
  // last texel.
  //
  // In other words if we had 3 values (and therefore
  // 3 texels) we'd have something like this
  //
  //     ------3x1 ----- texels ----------
  //     [         ][         ][         ]
  // 0.0 |<----------------------------->| 1.0
  //
  // If we just did index / numValues we'd get
  //
  //     [         ][         ][         ]
  //     |          |          |
  //     0.0       0.333       0.666
  //
  // Which is right between texels so we add a
  // a halfTexel to get this
  //
  //     [         ][         ][         ]
  //          |          |          |
  //        0.167       0.5       0.833


  var size = data.length / 3;
  var texel = 1 / size;
  var halfTexel = texel / 2;
  for (var ii = 0; ii < indices.length; ++ii) {
    scaledIndices[ii] = indices[ii] / size + halfTexel;
  }
  var buffer = gl.createBuffer();
  gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
  gl.bufferData(gl.ARRAY_BUFFER,
                scaledIndices,
                gl.STATIC_DRAW);
  gl.enableVertexAttribArray(loc);
  gl.vertexAttribPointer(loc, 1, gl.FLOAT, false, 0, 0);
}

function uploadTexture(unit, data) {
  gl.activeTexture(gl.TEXTURE0 + unit);
  var tex = gl.createTexture();
  gl.bindTexture(gl.TEXTURE_2D, tex);
  gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB,
                data.length / 3, 1, 0,
                gl.RGB, gl.FLOAT, new Float32Array(data));
  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER,
                   gl.NEAREST);
  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER,
                   gl.NEAREST);
  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S,
                   gl.CLAMP_TO_EDGE);
  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T,
                   gl.CLAMP_TO_EDGE);
}

uploadIndices(positionIndexLoc, positions, positionIndices);
uploadIndices(normalIndexLoc, normals, normalIndices);
uploadTexture(0, positions);
uploadTexture(1, normals);

var xRot = 30;
var yRot = 20;
var zRot = 0;

var lightDir = v3.normalize([-0.2, -0.1, 0.5]);

function draw() {
  xRot += 0;
  yRot += 1;
  
  twgl.resizeCanvasToDisplaySize(gl.canvas);
  gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
  
  gl.enable(gl.DEPTH_TEST);
  gl.enable(gl.CULL_FACE);
  gl.clearColor(0, 0, 0, 1);
  gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);

  gl.useProgram(program);  

  var projection = m4.perspective(
    degToRad(45),
    gl.canvas.clientWidth / gl.canvas.clientHeight,
    0.1, 100.0);

  var world = m4.identity();
  world = m4.translate(world, [0.0, 0.0, -5.0]);

  world = m4.rotateX(world, degToRad(xRot));
  world = m4.rotateY(world, degToRad(yRot));
  world = m4.rotateZ(world, degToRad(zRot));

  var mvp = m4.multiply(projection, world);

  gl.uniformMatrix4fv(mvMatrixLoc, false, world);
  gl.uniformMatrix4fv(mvpMatrixLoc, false, mvp);

  gl.uniform4f(colorLoc, 0.5, 0.8, 1, 1);
  gl.uniform3fv(lightDirLoc, lightDir);

  gl.drawArrays(gl.TRIANGLES, 0, 36);
  requestAnimationFrame(draw);
}
draw();
body {
  margin: 0;
}
canvas {
  width: 100vw;
  height: 100vh;
  display: block;
}
<script src="//twgljs.org/dist/2.x/twgl-full.min.js"></script>
<script id="vshader" type="whatever">
attribute float a_positionIndex;
attribute float a_normalIndex;
attribute vec4 a_pos;

uniform sampler2D u_positions;
uniform sampler2D u_normals;
uniform mat4 u_mvpMatrix;
uniform mat4 u_mvMatrix;

varying vec3 v_normal;

void main() {
  vec3 position = texture2D(
      u_positions, vec2(a_positionIndex, 0.5)).rgb;
  vec3 normal = texture2D(
      u_normals, vec2(a_normalIndex, 0.5)).rgb;
  gl_Position = u_mvpMatrix * vec4(position, 1);
  v_normal = (u_mvMatrix * vec4(normal, 0)).xyz;
}
</script>
<script id="fshader" type="whatever">
precision mediump float;

uniform vec4 u_color;
uniform vec3 u_lightDirection;

varying vec3 v_normal;

void main() {
  float light = dot(
      normalize(v_normal), u_lightDirection) * 0.5 + 0.5;
  gl_FragColor = vec4(u_color.rgb * light, u_color.a);
}
</script>
<canvas></canvas>