WebGL纹理创建麻烦

时间:2014-03-26 15:57:55

标签: javascript google-chrome firefox opengl-es webgl

我正在编写光线追踪程序,主要思想是将三角形和.obj模型的索引保存为2个纹理:

a)顶点纹理 b)指数纹理

我这样做是为了在我的GLSL片段着色器中进行光线三角形交叉,浏览器告诉我我的纹理有这个问题:

a) Mozilla Firefox

错误#1(带顶点纹理):错误:WebGL:texImage2D:无效类型0x1406

错误#2(带索引纹理):错误:WebGL:texImage2D:无效类型0x1403

b) Google Chrome

两种纹理都出错: webgl invalid_enum teximage2d无效纹理类型

以下是代码:

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);

}

由于显而易见的原因,this.vertexArray和this.faceArray都有这些数据。

1 个答案:

答案 0 :(得分:5)

只有在检查并启用浮点纹理时才允许

gl.RGB / gl.FLOAT纹理。

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

gl.RGB / gl.UNSIGNED_SHORT纹理不存在。您可以尝试将无符号短值编码为R * 256 + G * 65536或沿着这些行进行编码。或者也可以在这里使用花车。

注意:像gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR)一样过滤浮动内容是一个单独的扩展程序OES_texture_float_linear,但在您的情况下,由于您只使用gl.NEAREST,因此您不需要检查该扩展名

至于将顶点数据放在纹理中,您可能会发现必须做一些工作来提取正确的值。

要索引纹理中的值,您需要计算将访问正确纹理元素的纹理坐标。要做到这一点,您需要从第一个纹素的中间访问到最后一个纹素的中间。

换句话说,如果我们有3个值(因此有3个像素),我们就会有类似的东西

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

如果我们只是使用index / numValues来计算纹理坐标,我们就得到

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

在texel之间是正确的,所以我们添加了halfTexel来获得这个

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

希望有道理。这是一个片段

&#13;
&#13;
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();
&#13;
body {
  margin: 0;
}
canvas {
  width: 100vw;
  height: 100vh;
  display: block;
}
&#13;
<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>
&#13;
&#13;
&#13;